另一种选择是使用InjectableValues 和@JacksonInject。如果您需要使用不总是相同的值,而是从数据库或其他地方获取特定情况下的值,这将非常有用。下面是一个使用JacksonInject的例子:
protected static class Some {
private final String field1;
private final String field2;
public Some(@JsonProperty("field1") final String field1,
@JsonProperty("field2") @JacksonInject(value = "defaultValueForField2",
useInput = OptBoolean.TRUE) final String field2) {
this.field1 = requireNonNull(field1);
this.field2 = requireNonNull(field2);
}
public String getField1() {
return field1;
}
public String getField2() {
return field2;
}
}
@Test
public void testReadValueInjectables() throws JsonParseException, JsonMappingException, IOException {
final ObjectMapper mapper = new ObjectMapper();
final InjectableValues injectableValues =
new InjectableValues.Std().addValue("defaultValueForField2", "somedefaultValue");
mapper.setInjectableValues(injectableValues);
final Some actualValueMissing = mapper.readValue("{\"field1\": \"field1value\"}", Some.class);
assertEquals(actualValueMissing.getField1(), "field1value");
assertEquals(actualValueMissing.getField2(), "somedefaultValue");
final Some actualValuePresent =
mapper.readValue("{\"field1\": \"field1value\", \"field2\": \"field2value\"}", Some.class);
assertEquals(actualValuePresent.getField1(), "field1value");
assertEquals(actualValuePresent.getField2(), "field2value");
}
请记住,如果您使用构造函数来创建实体(这通常发生在您在 lombok 中使用 @Value 或 @AllArgsConstructor 时)并且您将 @JacksonInject 不是构造函数而是属性它将无法按预期工作 - 注入字段的值将始终覆盖 json 中的值,无论您是否将 useInput = OptBoolean.TRUE 放入 @JacksonInject。这是因为杰克逊在调用构造函数后注入了这些属性(即使属性是final) - 字段在构造函数中设置为正确的值,但随后被覆盖(检查:https://github.com/FasterXML/jackson-databind/issues/2678 和https://github.com/rzwitserloot/lombok/issues/1528#issuecomment-607725333 了解更多信息) ,不幸的是,这个测试通过了:
protected static class Some {
private final String field1;
@JacksonInject(value = "defaultValueForField2", useInput = OptBoolean.TRUE)
private final String field2;
public Some(@JsonProperty("field1") final String field1,
@JsonProperty("field2") @JacksonInject(value = "defaultValueForField2",
useInput = OptBoolean.TRUE) final String field2) {
this.field1 = requireNonNull(field1);
this.field2 = requireNonNull(field2);
}
public String getField1() {
return field1;
}
public String getField2() {
return field2;
}
}
@Test
public void testReadValueInjectablesIncorrectBehavior() throws JsonParseException, JsonMappingException, IOException {
final ObjectMapper mapper = new ObjectMapper();
final InjectableValues injectableValues =
new InjectableValues.Std().addValue("defaultValueForField2", "somedefaultValue");
mapper.setInjectableValues(injectableValues);
final Some actualValueMissing = mapper.readValue("{\"field1\": \"field1value\"}", Some.class);
assertEquals(actualValueMissing.getField1(), "field1value");
assertEquals(actualValueMissing.getField2(), "somedefaultValue");
final Some actualValuePresent =
mapper.readValue("{\"field1\": \"field1value\", \"field2\": \"field2value\"}", Some.class);
assertEquals(actualValuePresent.getField1(), "field1value");
// unfortunately "field2value" is overrided because of putting "@JacksonInject" to the field
assertEquals(actualValuePresent.getField2(), "somedefaultValue");
}
另一种方法是使用JsonDeserializer,例如:
public class DefaultValueDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException {
return jsonParser.getText();
}
@Override
public String getNullValue(DeserializationContext ctxt) {
return "some random value that can be different each time: " + UUID.randomUUID().toString();
}
}
然后像这样注释一个字段:
public class Content {
@JsonDeserialize(using = DefaultValueDeserializer.class)
private String someField;
...
}
请记住,您可以使用 getNullValue(DeserializationContext ctxt) 传递的属性
mapper.reader().forType(SomeType.class).withAttributes(singletonMap("dbConnection", dbConnection)).readValue(jsonString);
这样:
@Override
public String getNullValue(DeserializationContext ctxt) {
return ((DbConnection)ctxt.getAttribute("dbConnection")).getDefaultValue(...);
}
希望这对有类似问题的人有所帮助。
附:我正在使用杰克逊 v. 2.9.6