-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Open
Labels
to-evaluateIssue that has been received but not yet evaluatedIssue that has been received but not yet evaluated
Description
Search before asking
- I searched in the issues and found nothing similar.
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
Metadata
Metadata
Assignees
Labels
to-evaluateIssue that has been received but not yet evaluatedIssue that has been received but not yet evaluated