5

There is a feature of Jackson ObjectMapper which allows empty values to be removed:

class Car { Optional<String> ownerName; String manufacturer; public Optional<String> getOwnerName() { return ownerName; } public String getManufacturer() { return manufacturer; } } Car batMobile = new Car(); batMobile.owner = Optional.of("Batman"); batMobile.manufacturer = null; Car stolenCar = new Car(); stolenCar.owner = Optional.empty(); stolenCar.manufacturer = "Tesla"; ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(Include.NON_ABSENT); mapper.writeValueAsString(batMobile); /* { "ownerName": "Batman" } */ mapper.writeValueAsString(stolenCar); /* { "manufacturer": "Tesla" } */ 

What I want instead is for Optional.empty values to be removed, but null values to remain:

mapper.writeValueAsString(batMobile); /* { "ownerName": "Batman", "manufacturer": null } */ mapper.writeValueAsString(stolenCar); /* { "manufacturer": "Tesla" } */ 

And I want this to apply globally without needing any annotations on the DTOs.

1 Answer 1

1
+100

In case you use java.util.Optional be sure you have registered com.fasterxml.jackson.datatype.jdk8.Jdk8Module module which provides extra serialisers and deserialisers.

In your case the simplest way would be to use annotation on property levels:

class Car { @JsonInclude(JsonInclude.Include.NON_EMPTY) Optional<String> ownerName; @JsonInclude String manufacturer; //getters, setters } 

Default value is Include.ALWAYS which should be used in your case.

If you want to do that globally you need to implement custom filter and check all cases manually. Simple example:

class CustomFilter { @Override public boolean equals(Object obj) { // return false to keep value in response // return true to filter out value from response if (obj == null) { return false; } if (obj instanceof Optional) { Optional opt = (Optional) obj; return !opt.isPresent(); } //other cases if needed return false; } } 

You can register it as below:

ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.registerModule(new Jdk8Module()); mapper.setDefaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.CUSTOM, JsonInclude.Include.CUSTOM, CustomFilter.class, CustomFilter.class)); 

In case, this is not what you wanted you need to set globally most appropriate value and for given properties set another value. If you can not modify DTO classes, you can always use MixIn feature.

Sign up to request clarification or add additional context in comments.

3 Comments

indeed but seems a requirement : And I want this to apply globally without needing any annotations on the DTOs.
@CodeScale, It is not an easy task to implement custom global filter. Take a look on above one to check if it will work for you.
Be aware that some things you can set-up also using application properties. See baeldung.com/… or some reference here docs.spring.io/spring-boot/docs/current/reference/htmlsingle/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.