Java 使用Jackson映射时将默认值设置为空字段

Java 使用Jackson映射时将默认值设置为空字段,java,json,jackson,Java,Json,Jackson,我试图用Jackson将一些JSON对象映射到Java对象。JSON对象中的一些字段是必需的(我可以用@NotNull标记),而有些字段是可选的 在与Jackson进行映射之后,JSON对象中未设置的所有字段在Java中都将具有空值。是否有类似于@NotNull的注释,可以告诉Jackson为Java类成员设置默认值,以防该值为null 编辑: 为了让问题更清楚,这里有一些代码示例 Java对象: class JavaObject { @NotNull public String

我试图用Jackson将一些JSON对象映射到Java对象。JSON对象中的一些字段是必需的(我可以用
@NotNull
标记),而有些字段是可选的

在与Jackson进行映射之后,JSON对象中未设置的所有字段在Java中都将具有空值。是否有类似于
@NotNull
的注释,可以告诉Jackson为Java类成员设置默认值,以防该值为null

编辑: 为了让问题更清楚,这里有一些代码示例

Java对象:

class JavaObject {
    @NotNull
    public String notNullMember;

    @DefaultValue("Value")
    public String optionalMember;
}
JSON对象可以是:

{
    "notNullMember" : "notNull"
}
或:


@DefaultValue
注释只是为了显示我的要求。这不是一个真正的注释。如果JSON对象与第一个示例类似,我希望
optionalMember
的值为
“value”
,而不是
null
。有这样的注释吗?

看起来解决方案是在默认构造函数中设置属性的值。因此,在本例中,java类是:

class JavaObject {

    public JavaObject() {

        optionalMember = "Value";
    }

    @NotNull
    public String notNullMember;

    public String optionalMember;
}
与Jackson映射后,如果JSON中缺少
optionalMember
,则Java类中的值为
“value”


但是,我仍然有兴趣知道是否有一个带有注释而没有默认构造函数的解决方案。

没有注释来设置默认值。
只能在java类级别设置默认值:

public class JavaObject 
{
    public String notNullMember;

    public String optionalMember = "Value";
}

使成员私有并添加setter/getter对。 在setter中,如果为null,则改为设置默认值。 此外,我还展示了这个片段,其中getter在内部值为null时也返回默认值

class JavaObject {
    private static final String DEFAULT="Default Value";

    public JavaObject() {
    }

    @NotNull
    private String notNullMember;
    public void setNotNullMember(String value){
            if (value==null) { notNullMember=DEFAULT; return; }
            notNullMember=value;
            return;
    }

    public String getNotNullMember(){
            if (notNullMember==null) { return DEFAULT;}
            return notNullMember;
    }

    public String optionalMember;
}

当显式设置了
某个值:null
时,只有一个建议的解决方案保留了
默认值(POJO可读性在那里丢失,而且很笨拙)

下面是如何保持
默认值
,而从不将其设置为
null

@JsonProperty("some-value")
public String someValue = "default-value";

@JsonSetter("some-value")
public void setSomeValue(String s) {
    if (s != null) { 
        someValue = s; 
    }
}

我有一个类似的问题,但在我的例子中,默认值在数据库中。以下是解决方案:

 @Configuration
 public class AppConfiguration {
 @Autowired
 private AppConfigDao appConfigDao;

 @Bean
 public Jackson2ObjectMapperBuilder builder() {
   Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
       .deserializerByType(SomeDto.class, 
 new SomeDtoJsonDeserializer(appConfigDao.findDefaultValue()));
   return builder;
 }

然后在
SomeDtoJsonDeserializer
中,使用
ObjectMapper
对json进行反序列化,如果字段/对象为空,则设置默认值。

您可以创建自己的JsonDeserializer,并使用
@JsonDeserialize(as=DefaultZero.class)

例如:要将BigDecimal配置为默认为零:

public static class DefaultZero extends JsonDeserializer<BigDecimal> {
    private final JsonDeserializer<BigDecimal> delegate;

    public DefaultZero(JsonDeserializer<BigDecimal> delegate) {
        this.delegate = delegate;
    }

    @Override
    public BigDecimal deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        return jsonParser.getDecimalValue();
    }

    @Override
    public BigDecimal getNullValue(DeserializationContext ctxt) throws JsonMappingException {
        return BigDecimal.ZERO;
    }
}

已经有很多好的建议,但这里还有一个。您可以使用@JsonDeserialize执行Jackson将调用后反序列化的任意“消毒剂”:

@JsonDeserialize(converter=Message1._Sanitizer.class)  
public class Message1 extends MessageBase
{
    public String string1 = "";
    public int integer1;

    public static class _Sanitizer extends StdConverter<Message1,Message1> {
        @Override
        public Message1 convert(Message1 message) {
            if (message.string1 == null) message.string1 = "";
            return message;
        }
    }
}
@JsonDeserialize(converter=Message1.\u Sanitizer.class)
公共类Message1扩展了MessageBase
{
公共字符串string1=“”;
公共整数1;
公共静态类\u Sanitizer扩展了StdConverter{
@凌驾
公共消息1转换(消息1消息){
如果(message.string1==null)message.string1=“”;
返回消息;
}
}
}
另一个选项是使用和。如果您需要使用不总是相同的值,而是一个从DB或其他地方获取的特定情况下的值,那么它非常有用。下面是使用
JacksonInject
的示例:

受保护的静态类{
私有最终字符串字段1;
私有最终字符串字段2;
public Some(@JsonProperty(“field1”)最终字符串field1,
@JsonProperty(“field2”)@JacksonInject(value=“defaultValueForField2”,
useInput=OptBoolean.TRUE)最终字符串字段2){
this.field1=requirennull(field1);
this.field2=requirennull(field2);
}
公共字符串getField1(){
返回字段1;
}
公共字符串getField2(){
返回字段2;
}
}
@试验
public void testReadValueInjectables()抛出JsonParseException、JsonMappingException、IOException{
最终ObjectMapper映射器=新ObjectMapper();
最终可注射值可注射值=
新的InjectableValues.Std().addValue(“defaultValueForField2”,“somedefaultValue”);
映射器.setInjectableValues(injectableValues);
final Some actualValueMissing=mapper.readValue(“{\”field1\“:\”field1value\“}”,Some.class);
assertEquals(actualValueMissing.getField1(),“field1value”);
assertEquals(actualValueMissing.getField2(),“somedefaultValue”);
最后给出了一些实际的价值=
mapper.readValue(“{\'field1\':\'field1value\',\'field2\':\'field2value\'”),Some.class);
assertEquals(actualValuePresent.getField1(),“field1value”);
assertEquals(actualValuePresent.getField2(),“field2value”);
}
请记住,如果使用构造函数创建实体(通常在使用或中时发生),并且将
@JacksonInject
放在属性上而不是构造函数上,则该属性将不会按预期工作-注入字段的值将始终覆盖json中的值,无论您是否将
useInput=OptBoolean.TRUE
放入
@JacksonInject
中。这是因为jackson在调用构造函数后注入了这些属性(即使该属性是
final
)-字段在构造函数中设置为正确的值,但随后被覆盖(检查:有关更多信息,此测试不幸通过:

受保护的静态类{
私有最终字符串字段1;
@JacksonInject(value=“defaultValueForField2”,useInput=OptBoolean.TRUE)
私有最终字符串字段2;
public Some(@JsonProperty(“field1”)最终字符串field1,
@JsonProperty(“field2”)@JacksonInject(value=“defaultValueForField2”,
useInput=OptBoolean.TRUE)最终字符串字段2){
this.field1=requirennull(field1);
this.field2=requirennull(field2);
}
公共字符串getField1(){
返回字段1;
}
公共字符串getField2(){
返回字段2;
}
}
@试验
公共无效测试READV
class Sth {

   @JsonDeserialize(as = DefaultZero.class)
   BigDecimal property;
 }
@JsonDeserialize(converter=Message1._Sanitizer.class)  
public class Message1 extends MessageBase
{
    public String string1 = "";
    public int integer1;

    public static class _Sanitizer extends StdConverter<Message1,Message1> {
        @Override
        public Message1 convert(Message1 message) {
            if (message.string1 == null) message.string1 = "";
            return message;
        }
    }
}