“This is the 18th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

Author: Tangyuan

Personal blog: Javalover.cc

preface

Optional is a feature added to Java8. It is designed to solve the problem of null Pointers in Java.

However, when manipulating Optional properties in Jackson, there are some problems. For example, the serialized data does not meet the expectations.

Here are the problems encountered and how to solve them.

directory

  1. Problem with serializing Optional types
  2. Cause analysis,
  3. The solution

The body of the

1. Serialize Optional type problems

Other types of properties have little problem with serialization, depending on the value of the object.

But Optional is special. It prints present:true when it’s serialized;

Now let’s look at an example;

This is the User object where the nickname is of type Optional:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    public String username;

    public Optional<String> nickname;

}
Copy the code

The serialization code looks like this:

User user = new User("jalon", Optional.of("xiaowang"));
ObjectMapper objectMapper = new ObjectMapper();
String str = objectMapper.writeValueAsString(user);
System.out.println(str);
Copy the code

The expected result is something like the following:

{"username":"jalon"."nickname":"xiaowang"}
Copy the code

But the actual output looks like this:

2. Cause analysis

Serialization outputs {“present”:true} because of Jackson’s default serialization behavior;

Jackson’s default serialization serializes all public get methods, fetching all accessible properties from the object and populating them into the result.

By default, the Optional object has only one public get method, isPresent(), which returns either true (when Optional is not null) or false (when Optional is null);

The partial content of the Optional class looks like this:

public final class Optional<T> {
  
    private final T value;

    public T get(a) {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    public boolean isPresent(a) {
        returnvalue ! =null;
    }


    @Override
    public String toString(a) {
        returnvalue ! =null
            ? String.format("Optional[%s]", value)
            : "Optional.empty"; }}Copy the code

As you can see, although there is a value property, it cannot be read directly by Jackson because it is of private type.

So by default Jackson only reads isPresent(), which is true;

3. Solutions

Fortunately, Jackson already has an official maven dependency that is designed to address various issues caused by the new Java8 data types;

Add the following dependencies:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
    <version>2.12.5</version>
</dependency>
Copy the code

Then in the ObjectMapper object configuration jdk8 modules: ObjectMapper. RegisterModule (new Jdk8Module ());

User user = new User("jalon", Optional.of("xiaowang"));
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
String str = objectMapper.writeValueAsString(user);
System.out.println(str);
Copy the code

The final output is as expected, as follows:

conclusion

When Jackson manipulates an Optional attribute, the output does not meet the expectations due to Jackson’s default behavior.

The solution is to load the jackson-datatype- JDk8 dependency and register the Java8 module Jdk8Module globally;