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

12
我有一个Java类,其中包含一个Optional字段。 我正在使用Jackson 2.8.3(从Spring web 4.3.3中调用)将该类序列化为JSON。
我希望在Optional为空时使序列化器跳过该字段,并且在存在时序列化包含的字符串。以下是我想要的具有两个对象列表的示例结果:
[
    {
        "id": 1,
        "foo": "bar"
    },
    {
        "id": 2,
    }
]

这里,id为2的对象的foo选项为空。

然而,我收到的是:

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

即使我在类中注释“bar”字段,结果也是这样。
@JsonInclude(JsonInclude.Include.NON_ABSENT)
public Optional<String> getFoo() { ...

有没有办法使用Jackson注释或自定义序列化程序来实现像第一个列表那样的结果?

1
顺便提一下,在一般情况下,将Optional用作bean的字段是一个不好的想法。 - biziclop
为什么被认为是有问题的? - André Risnes
它不是为那种类型的使用设计的。这是一个非常微妙的语义差异,但在字段的情况下,null 被认为是另一个值。而如果您正在设计库的 API 中的方法,则可能希望强调它可能不返回值,因此使用 Optional。更多信息请参见此处 - biziclop
3个回答

28

不需要编写自定义序列化器。使用@JsonInclude(JsonInclude.Include.NON_ABSENT)为您的类进行注释。

您还需要:

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

谢谢,包括 com.fasterxml.jackson.datatype:jackson-datatype-jdk8 的依赖解决了问题。 - André Risnes
1
@JsonInclude(JsonInclude.Include.NON_ABSENT)就足够了。 - GKVM
2
为了排除Optional.empty()字段,正如原始问题所问,必须添加依赖项并设置NON_ABSENT。仅使用NON_NULL是不够的。 - Robert

2

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

但是如果您仍然希望从JSON中删除空值,请使用以下代码:

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

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);

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

1
com.fasterxml.jackson.datatype:jackson-datatype-jdk8是更好的选择。使用这种自定义解决方案,您需要注释每个字段。此外,除非您编写自定义反序列化程序并使用@JsonDeserialize注释每个字段,否则反序列化将无法正常工作。 - Bartosz Bilicki
@BartoszBilicki 当然,我同意你的观点。两种方法都可以,但是你的建议更好。 - Mechkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接