jackson-databind-nullable icon indicating copy to clipboard operation
jackson-databind-nullable copied to clipboard

Confusion with setSerializationInclusion(Include.NON_NULL)

Open midovlvn opened this issue 2 years ago • 1 comments

I'm confused with setup in "Usage section":


ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.registerModule(new JsonNullableModule());

Please take a look on the following snippet ( val=null is passed in JSON and in Map):

record A (JsonNullable<BigDecimal> val) {}
ObjectMapper objectMapper = new ObjectMapper().registerModule(new JsonNullableModule());

A a1 = objectMapper.readValue("{ \"val\" : null }", A.class);
System.out.println(a1);

HashMap hashMap = new HashMap();
hashMap.put("val", null);
A a2 = objectMapper.convertValue(hashMap, A.class);
System.out.println(a2);

Output is consistent

A[val=JsonNullable[null]] A[val=JsonNullable[null]]

But with setup

ObjectMapper objectMapper = new ObjectMapper().registerModule(new JsonNullableModule())
        .setSerializationInclusion(JsonInclude.Include.NON_NULL);

Output is not consistent

A[val=JsonNullable[null]] A[val=JsonNullable.undefined]

Well, time for questions:

  1. I do not understand why you propose in "Usage" section setup .setSerializationInclusion(JsonInclude.Include.NON_NULL). It looks useless.
  2. I do not understand how serialization setup may affect deserialization (objectMapper.convertValue is deserialization)

midovlvn avatar Sep 21 '23 23:09 midovlvn

serializationInclusion impacts the conversion from a Java object to a String. It does not impact the conversion from a String into a Java object.

mapper.readValue(someString, A.class) will always produce the same value, regardless of the serializationInclusion property. mapper.writeValueAsString(someDto) may serialize to a different String based on the value of serializationInclusion. mapper.convertValue(someDto, A.class)isn't exactly the same, but is similar to calling mapper.readValue(mapper.writeValueAsString(someDto), A.class) and as such it may also be impacted by the different configuration options of serializationInclusion (check the javadoc on convertValue for details on how it works).

If you use JsonInclude.Include.ALWAYS, then new A(null) will be converted to a String with the value of {"val": null}. When you then attempt to convert this String back into A, it will be converted into new A(JsonNullable.of(null). This is typically undesirable. If you use JsonInclude.Include.NON_NULL (or NON_ABSENT or NON_EMPTY) then new A(null) will be converted to a String with the value of {}. When you attempt to convert this String back into A it will be converted into new A(null). This is the typical desirable behavior.

(I say String above, but I believe the same applies if you're using a byte array, InputStream, etc. String is just the simplest for discussion here.)


This module relies on the below concepts. By using JsonInclude.Include.ALWAYS for serialization, these concepts don't work well on the deserialization side.

  • A JSON object without a property is equivalent to JsonNullable.undefined().
  • A JSON object with a property set to null is equivalent to JsonNullable.of(null).
  • A JSON object with a property set to any value is equivalent to JsonNullable.of(someValue).

nrayburn-tech avatar Sep 25 '25 18:09 nrayburn-tech