Search before asking
Describe the bug
Optional handling differs between Jackson 2.x and 3.0 despite the information that Jdk8 module was pulled in to core.
Version Information
3.0
Reproduction
I have a record class with an Optional field. When I use JsonMapper from Jackson 3.0, I'm unable to configure it in a way that will inject Optional.empty if either value is null or absent in the JSON.
Works in Jackson 2.x:
public class TestOptional
{
public static void main(String[] args) throws JsonProcessingException {
JsonMapper.Builder builder = JsonMapper.builder();
builder.addModule(new Jdk8Module());
builder.defaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.ALWAYS));
Person person = new Person("Alice", Optional.empty());
ObjectMapper mapper = builder.build();
System.out.println(mapper.writeValueAsString(person));
System.out.println(mapper.readValue(mapper.writeValueAsString(person), Person.class));
System.out.println(mapper.readValue("{\"name\": \"Alice\"}}", Person.class));
System.out.println(mapper.readValue("{\"name\": \"Alice\", \"nickname\": null}}", Person.class));
}
record Person(String name, Optional<String> nickname)
{
public Person {
requireNonNull(name, "name is null");
requireNonNull(nickname, "nickname is null");
}
}
}
Doesn't work in Jackson 3:
public class TestOptional
{
public static void main(String[] args)
{
JsonMapper.Builder builder = JsonMapper.builder();
builder.changeDefaultPropertyInclusion(_ -> JsonInclude.Value.construct(JsonInclude.Include.NON_ABSENT, JsonInclude.Include.ALWAYS));
Person person = new Person("Alice", Optional.empty());
ObjectMapper mapper = builder.build();
System.out.println(mapper.writeValueAsString(person));
System.out.println(mapper.readValue(mapper.writeValueAsString(person), Person.class));
System.out.println(mapper.readValue("{\"name\": \"Alice\"}}", Person.class));
System.out.println(mapper.readValue("{\"name\": \"Alice\", \"nickname\": null}}", Person.class));
}
record Person(String name, Optional<String> nickname)
{
public Person {
requireNonNull(name, "name is null");
requireNonNull(nickname, "nickname is null");
}
}
}
Result:
{"name":"Alice"}
Exception in thread "main" tools.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `io.airlift.json.TestOptional$Person`, problem: nickname is null
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); byte offset: #UNKNOWN]
at tools.jackson.databind.exc.ValueInstantiationException.from(ValueInstantiationException.java:44)
at tools.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:2076)
at tools.jackson.databind.deser.std.StdValueInstantiator.wrapAsDatabindException(StdValueInstantiator.java:581)
at tools.jackson.databind.deser.std.StdValueInstantiator.rewrapCtorProblem(StdValueInstantiator.java:602)
at tools.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:289)
at tools.jackson.databind.deser.ValueInstantiator.createFromObjectWith(ValueInstantiator.java:270)
at tools.jackson.databind.deser.bean.PropertyBasedCreator.build(PropertyBasedCreator.java:252)
at tools.jackson.databind.deser.bean.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:697)
at tools.jackson.databind.deser.bean.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1417)
at tools.jackson.databind.deser.bean.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:480)
at tools.jackson.databind.deser.bean.BeanDeserializer.deserialize(BeanDeserializer.java:200)
at tools.jackson.databind.deser.DeserializationContextExt.readRootValue(DeserializationContextExt.java:265)
at tools.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2610)
at tools.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1522)
at io.airlift.json.TestOptional.main(TestOptional.java:22)
Caused by: java.lang.NullPointerException: nickname is null
at java.base/java.util.Objects.requireNonNull(Objects.java:246)
at io.airlift.json.TestOptional$Person.<init>(TestOptional.java:31)
at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:735)
at tools.jackson.databind.introspect.AnnotatedConstructor.call(AnnotatedConstructor.java:113)
at tools.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:287)
... 10 more
Process finished with exit code 1
Expected behavior
Optionals should work :)
Additional context
No response
Search before asking
Describe the bug
Optional handling differs between Jackson 2.x and 3.0 despite the information that Jdk8 module was pulled in to core.
Version Information
3.0
Reproduction
I have a record class with an Optional field. When I use JsonMapper from Jackson 3.0, I'm unable to configure it in a way that will inject Optional.empty if either value is null or absent in the JSON.
Works in Jackson 2.x:
Doesn't work in Jackson 3:
Result:
Expected behavior
Optionals should work :)
Additional context
No response