Jackson 序列化期间如何跳过 Optional.empty 字段?

Posted

技术标签:

【中文标题】Jackson 序列化期间如何跳过 Optional.empty 字段?【英文标题】:How to skip Optional.empty fields during Jackson serialization? 【发布时间】:2017-03-17 10:15:23 【问题描述】:

我有一个带有 Optional 字段的 Java 类。我正在使用 Jackson 2.8.3(从 Spring web 4.3.3 调用)将类序列化为 JSON。

如果 Optional 为空,我希望让序列化程序跳过该字段,并序列化包含的字符串(如果存在)。我正在寻找包含两个对象的列表的结果示例:

[
    
        "id": 1,
        "foo": "bar"
    ,
    
        "id": 2,
    
]

对于 id 为 2 的对象,这里的 foo Optional 为空。

相反,我得到的是:

[
    
        "id": 1,
        "foo": 
            "present": true
        
    ,
    
        "id": 2,
        "foo": 
            "present": false
        
    
]

即使我在类中注释“栏”字段,这也是结果

@JsonInclude(JsonInclude.Include.NON_ABSENT)
public Optional<String> getFoo()  ...

有没有什么方法可以使用 Jackson 注释或自定义序列化程序来实现第一个列表之类的结果?

【问题讨论】:

顺便说一句,使用Optional 作为 bean 的字段通常是一个 Bad Idea™。 为什么这被认为是有问题的? 它不是为这种用途而设计的。这是一个非常微妙的语义差异,但在字段的情况下,null 被认为只是另一个值。如果您在库的 API 中设计方法,您可能需要强调它可能不会返回值,因此使用Optional。查看更多here 【参考方案1】:

根据您的需要使用JsonSerializer

类似这样的东西(半伪):

public class MySer extends JsonSerializer<Opional<?>> 

        @Override
        public void serialize(Optional<?> optString, JsonGenerator generator, SerializerProvider provider)
                                          throws IOException, JsonProcessingException 
        //Check Optional here...        
        generator.writeString(/* DO SOMETHING HERE WHATEVER */);
    

//然后在你的模型中:

public class ClassWhatever            
    @JsonSerialize(using = MySer .class)
    public Optional<String> getFoo()  ...
 

为避免使用 @JsonSerialize 注释每个字段,您可以使用

将自定义序列化程序注册到对象映射器
  ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null));
testModule.addSerializer(new MyCustomSerializer()); // assuming serializer declares correct class to bind to
mapper.registerModule(testModule);

此外,给定的解决方案仅适用于序列化。除非您编写自己的反序列化器,否则反序列化将失败。然后您需要使用@JsonDeserialize 注释每个字段或注册您的自定义反序列化器。

【讨论】:

com.fasterxml.jackson.datatype:jackson-datatype-jdk8 是更好的选择。使用这样的自定义解决方案,您需要注释每个字段。此外,除非您编写自定义反序列化程序并使用 @JsonDeserialize 注释每个字段,否则反序列化将不起作用 @BartoszBilicki 当然,同意你的意见。两者都可以,但您的建议更好。【参考方案2】:

无需编写自定义序列化程序。使用 @JsonInclude(JsonInclude.Include.NON_ABSENT) 注释您的课程。

您还需要:

包括 com.fasterxml.jackson.datatype:jackson-datatype-jdk8 作为您的依赖项 并向您的对象映射器注册相应的模块:objectMapper.registerModule(new Jdk8Module());

【讨论】:

谢谢,包括对 com.fasterxml.jackson.datatype:jackson-datatype-jdk8 的依赖。 @JsonInclude(JsonInclude.Include.NON_ABSENT) 就足够了。 要排除 Optional.empty() 字段,正如 OP 最初要求的那样,必须添加依赖项并必须设置 NON_ABSENT。 NON_NULL 是不够的。【参考方案3】:

您可以使用objectMapper.registerModule(new Jdk8Module());,但它会使用空值进行序列化。

但您仍然想从 JSON 中删除空值,请使用以下代码:

objectMapper.registerModule(new Jdk8Module().configureAbsentsAsNulls(true));
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

【讨论】:

以上是关于Jackson 序列化期间如何跳过 Optional.empty 字段?的主要内容,如果未能解决你的问题,请参考以下文章

在使用 Jackson 反序列化期间选择性地忽略 JSON 属性

在 Java 中的 Jackson JSON 反序列化期间忽略丢失的属性

使用 com.fasterxml.jackson.databind.ObjectMapper 在序列化/反序列化 JSON 内容期间从异常日志中删除敏感数据

选择性地@JsonIgnore Immutables 访问器方法,仅在使用 Jackson 进行序列化或反序列化期间

Symfony在序列化期间跳过空值

如果值为空,如何告诉杰克逊在序列化期间忽略一个字段?