Jackson optional field empty() vs null

Viewed 1997

I'm trying to use Optional in a POJO to indicate when a json field can be null, absent, or present. My issue is that I cannot figure out how to configure jackson to not treat Optional.empty() and null as the same. For empty(), I want the field to be ignored. None of the JsonInclude values seem to do this. Jdk8Module#configureAbsentsAsNulls() looked promising, but it doesn't change the results of my tests. Is there a way to do this?

tl;dr null values should be serialized, Optional.empty() should not be serialized.

Here are some tests exhibiting the behavior I'm trying to achieve.

class POJO {
    public Optional<String> content;
}

private ObjectMapper getMapper() {
    return new ObjectMapper().registerModule(new Jdk8Module());
}

@org.junit.Test
public void testAbsent() throws JsonProcessingException {
    ObjectMapper mapper = getMapper();

    POJO pojo = new POJO();
    pojo.content = Optional.empty();

    String result = mapper.writeValueAsString(pojo);

    assertEquals("{}", result);
}

@org.junit.Test
public void testNull() throws JsonProcessingException {
    ObjectMapper mapper = getMapper();

    POJO pojo = new POJO();
    pojo.content = null;

    String result = mapper.writeValueAsString(pojo);

    assertEquals("{\"content\":null}", result);
}

@org.junit.Test
public void testPresent() throws JsonProcessingException {
    ObjectMapper mapper = getMapper();

    POJO pojo = new POJO();
    pojo.content = Optional.of("Hello");

    String result = mapper.writeValueAsString(pojo);

    assertEquals("{\"content\":\"Hello\"}", result);
}
1 Answers

It is possible to implement using JsonInclude.Include.CUSTOM rule and custom value filter:

class OptionalFilter {

    @Override
    public boolean equals(Object obj) {
        return obj instanceof Optional && ((Optional<?>) obj).isEmpty();
    }
}

class POJO {
    @JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = OptionalFilter.class)
    public Optional<String> content;
}

with setting configureAbsentsAsNulls to true:

new Jdk8Module().configureAbsentsAsNulls(true)
Related