Java Optional with Jackson

This post will explain how to serialize/deserialize the java optional type field using Jackson. So, let’s first look at the problem, and then we will move on to its solution.

We have created a Person model class with firstName as an Optional type field, and then we will serialize and deserialize it.

import java.util.Optional;

public class Person {

	private Optional<String> firstName;
	private String lastName;
	private int age;

	// getters and setters
	public Optional<String> getFirstName() {
		return firstName;
	}

	public void setFirstName(Optional<String> firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}

Now, we will try to serialize and deserialize the Person object.

Serialization

import java.util.Optional;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Codekru {

	public static void main(String[] args) throws JsonProcessingException {
		Person person = new Person();
		person.setFirstName(Optional.of("Codekru"));
		person.setLastName("Programming");
		person.setAge(1);
		ObjectMapper mapper = new ObjectMapper();
		System.out.println(mapper.writeValueAsString(person)); // serializing object
	}

}

On Java 8, you will be getting the below JSON as an output –

{"firstName":{"present":true},"lastName":"Programming","age":1}

while on Java 15, we will get

{"firstName":{"empty":false,"present":true},"lastName":"Programming","age":1}

Well, in both scenarios, we are not getting what we wanted. firstName value wasn’t populated in either of the java versions. Let’s look at the deserialization, too, and then we will move on to the solution.

Deserialization

Here we will be getting some exceptions 😥

public class Codekru {

	public static void main(String[] args) throws IOException {

		String personJson = "{\"firstName\": \"Codekru\",\"lastName\":\"Programming\",\"age\":22}";

		ObjectMapper mapper = new ObjectMapper();
		Person person = mapper.readValue(personJson, Person.class); // deserializing object
		System.out.println(person.getFirstName());
	}

}

On Java 8, we will be getting the MismatchedInputException: Cannot construct instance of java.util.Optional

Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.util.Optional` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('Codekru')

While on Java 15, we will get the InvalidDefinitionException: Cannot construct instance of java.util.Optional

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.util.Optional` (no Creators, like default constructor, exist): no String-argument constructor/factory method to deserialize from String value ('Codekru')
 at [Source: (String)"{"firstName":"Codekru","lastName":"Programming","age":1}"; line: 1, column: 14] (through reference chain: Test.Person["firstName"])
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)

Solution

To overcome the issues stated above, we need to follow two steps.

  • We Need to add the Jackson-datatype-jdk8 jar to your project. Maven dependency of Jackson-datatype-jdk8 can be added by adding the below lines in pom.xml
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
    <version>2.11.2</version>
</dependency>

Note: If you have used spring-boot-starter-parent as a parent in the maven pom.xml file, then this jar will be already present.

  • The second step is to register the Jdk8 module in the object mapper object.
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Jdk8Module());

After these changes, let’s try the serialization and deserialization again.

Serialization

    public static void main(String[] args) throws JsonProcessingException {
        Person person = new Person();
        person.setFirstName(Optional.of("Codekru"));
        person.setLastName("Programming");
        person.setAge(1);
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Jdk8Module());
        System.out.println(mapper.writeValueAsString(person));// serializaing object
    }
{"firstName":"Codekru","lastName":"Programming","age":1}

Finally, we got our desired result and now let’s try to do the same with the deserialization of the object.

Deserialization

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;

public class Codekru {

	public static void main(String[] args) throws JsonMappingException, JsonProcessingException {

		String personJson = "{\"firstName\":\"Codekru\",\"lastName\":\"Programming\",\"age\":1}";

		ObjectMapper mapper = new ObjectMapper();
		mapper.registerModule(new Jdk8Module());
		Person person = mapper.readValue(personJson, Person.class); // deserializing object
		System.out.println("firstName = " + person.getFirstName());
	}

}

Output –

firstName = Optional[Codekru]

Well, it worked fine too.

You might be thinking that the code is printing Optional[Codekru] instead of Codekru alone. This is because the Optional is calling its toString() function and thus printing it in this way. If you want to go more deeply into seeing the internal implementation of its toString() method, we have written it below.

Internal Implementation of toString() function for Optional

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

Here you can see how Optional is printing its value in the toString() function.

Thank you for reading this article. If you have any doubts or concerns, please feel free to write us in the comments or mail us at [email protected]

Jackson Annotations –

Liked the article? Share this on

Leave a Comment

Your email address will not be published.