Recently I updated my project from Spring Boot 2.1.0 to Spring Boot 2.5.6. Since then I see differences in JSON serialization.
To distinguish between cases (a) any value, (b) explicit null, and (c) no value I use java.util.Optional
and Jackson's @JsonInclude(NON_NULL)
annotation. Furthermore I use Spring Data JPA's projection pattern to define the JSON format as follows:
public interface MyProjection {
@JsonInclude(Include.NON_NULL)
Optional<String> getMyAttribute();
}
This worked perfect with Spring Boot 2.1.0.
Field value | Rendered JSON |
---|---|
Optional.of("something") |
{ "myAttribute": "something" } |
Optional.empty() |
{ "myAttribute": null } |
null |
{} |
As of now (after my update to Spring Boot 2.5.6) null
is rendered like Optional.empty()
:
Field value | Rendered JSON |
---|---|
Optional.of("something") |
{ "myAttribute": "something" } |
Optional.empty() |
{ "myAttribute": null } |
null |
{ "myAttribute": null } |
My first assumption was that Jackson's NON_NULL
no longer works correctly with Optional
. But that was not the case. As @Pedro said, it works correctly. After some investigation I found out that Spring's ProjectionFactory
seems to handle null
values different. It converts null
to Optional.empty()
, while it didn't do that formerly.
I'd like the old behavior back. Does anyone know how to prevent Spring from converting nulls to empty Optionals and instead keep it nulls? Is there a new default configuration? I didn't find any.
CodePudding user response:
After a few time of searching, i found the interested test case.
Here is the excerpt of test case with optional in jackson projects:
public void testConfigAbsentsAsNullsTrue() throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Jdk8Module().configureAbsentsAsNulls(true));
OptionalData data = new OptionalData();
String value = mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(data);
assertEquals("{}", value);
}
public void testConfigAbsentsAsNullsFalse() throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Jdk8Module().configureAbsentsAsNulls(false));
OptionalData data = new OptionalData();
String value = mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(data);
assertEquals("{\"myString\":null}", value);
}
class OptionalData {
public Optional<String> myString = Optional.empty();
}
Unit test Optional Link:
And there is a note configureAbsentsAsNulls. https://github.com/FasterXML/jackson-datatype-jdk8/blob/master/src/main/java/com/fasterxml/jackson/datatype/jdk8/Jdk8Module.java
For compatibility with older versions
* of other "optional" values (like Guava optionals), it can be set to 'true'. The
* default is `false` for backwards compatibility.
public Jdk8Module configureAbsentsAsNulls(boolean state) {
_cfgHandleAbsentAsNull = state;
return this;
}
So you just need to set configureAbsentsAsNulls(true) to roll back your previous state.
CodePudding user response:
I have used the @JsonSerialize(include = Inclusion.NON_NULL)
to solve the problem and it worked.
CodePudding user response:
There is a three-way to ignore the null field at 1). Field level 2). Class level 3). Globally
@JsonInclude(Include.NON_NULL) private String userName;
@JsonInclude(Include.NON_NULL)
public class Book implements Comparable<Book> {
private String title;
private String author;
private int price;
public Book(String title, String author, int price) {
this.title = title;
this.author = author;
this.price = price;
}
}
// let's create a book with author as null
Book book = new Book(null, null, 42);
ObjectMapper mapper = new ObjectMapper();
// configure ObjectMapper to exclude null fields whiel serializing
mapper.setSerializationInclusion(Include.NON_NULL);
String json =mapper.writeValueAsString(cleanCode);
System.out.println(json);