另一种选择是使用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时)并且您将不是构造函数而是属性它不会按预期工作 - 注入的值字段将始终覆盖 json 中的值,无论您是否输入. 这是因为jackson在调用构造函数后注入了这些属性(即使属性是)-字段在构造函数中设置为正确的值,但随后被覆盖(检查:https ://github.com/FasterXML/jackson-databind/问题/2678和https://github.com/rzwitserloot/lombok/issues/1528#issuecomment-607725333@JacksonInject
useInput = OptBoolean.TRUE
@JacksonInject
final
有关更多信息),不幸的是,这个测试通过了:
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)
传递的 using中使用属性
mapper.reader().forType(SomeType.class).withAttributes(singletonMap("dbConnection", dbConnection)).readValue(jsonString);
像那样:
@Override
public String getNullValue(DeserializationContext ctxt) {
return ((DbConnection)ctxt.getAttribute("dbConnection")).getDefaultValue(...);
}
希望这对有类似问题的人有所帮助。
PS我正在使用jackson v. 2.9.6