Skip to content

Commit f6b98df

Browse files
committed
Fix #251
1 parent 9a9f0eb commit f6b98df

File tree

5 files changed

+104
-8
lines changed

5 files changed

+104
-8
lines changed

datatypes/src/main/java/com/fasterxml/jackson/datatype/jdk8/Jdk8Deserializers.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,29 @@ public class Jdk8Deserializers
1313
{
1414
private static final long serialVersionUID = 1L;
1515

16+
/**
17+
* @since 2.14
18+
*/
19+
protected final boolean _cfgReadAbsentAsNull;
20+
21+
public Jdk8Deserializers() {
22+
// for backwards compatibility
23+
this(Jdk8Module.DEFAULT_READ_ABSENT_AS_NULL);
24+
}
25+
26+
public Jdk8Deserializers(boolean cfgReadAbsentAsNull) {
27+
_cfgReadAbsentAsNull = cfgReadAbsentAsNull;
28+
}
29+
1630
@Override // since 2.7
1731
public JsonDeserializer<?> findReferenceDeserializer(ReferenceType refType,
1832
DeserializationConfig config, BeanDescription beanDesc,
1933
TypeDeserializer contentTypeDeserializer, JsonDeserializer<?> contentDeserializer)
2034
{
2135
if (refType.hasRawClass(Optional.class)) {
22-
return new OptionalDeserializer(refType, null, contentTypeDeserializer,contentDeserializer);
36+
return new OptionalDeserializer(refType, null,
37+
contentTypeDeserializer,contentDeserializer,
38+
_cfgReadAbsentAsNull);
2339
}
2440
// 21-Oct-2015, tatu: Should probably consider possibility of custom deserializer being
2541
// added to property; if so, `contentDeserializer` would not be null.

datatypes/src/main/java/com/fasterxml/jackson/datatype/jdk8/Jdk8Module.java

+32-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
public class Jdk8Module extends Module
77
{
8+
public final static boolean DEFAULT_READ_ABSENT_AS_NULL = false;
9+
810
/**
911
* Configuration setting that determines whether `Optional.empty()` is
1012
* considered "same as null" for serialization purposes; that is, to be
@@ -22,17 +24,25 @@ public class Jdk8Module extends Module
2224
* criteria for filtering out absent optionals; this setting is mostly useful for
2325
* legacy use cases that predate version 2.6.
2426
*/
25-
protected boolean _cfgHandleAbsentAsNull = false;
27+
protected boolean _cfgWriteAbsentAsNull = false;
28+
29+
/**
30+
* See {@link #configureReadAbsentAsNull} for details of this configuration
31+
* setting.
32+
*
33+
* @since 2.14
34+
*/
35+
protected boolean _cfgReadAbsentAsNull = DEFAULT_READ_ABSENT_AS_NULL;
2636

2737
@Override
2838
public void setupModule(SetupContext context) {
2939
context.addSerializers(new Jdk8Serializers());
30-
context.addDeserializers(new Jdk8Deserializers());
40+
context.addDeserializers(new Jdk8Deserializers(_cfgReadAbsentAsNull));
3141
// And to fully support Optionals, need to modify type info:
3242
context.addTypeModifier(new Jdk8TypeModifier());
3343

3444
// Allow enabling "treat Optional.empty() like Java nulls"
35-
if (_cfgHandleAbsentAsNull) {
45+
if (_cfgWriteAbsentAsNull) {
3646
context.addBeanSerializerModifier(new Jdk8BeanSerializerModifier());
3747
}
3848
}
@@ -64,7 +74,25 @@ public Version version() {
6474
*/
6575
@Deprecated
6676
public Jdk8Module configureAbsentsAsNulls(boolean state) {
67-
_cfgHandleAbsentAsNull = state;
77+
_cfgWriteAbsentAsNull = state;
78+
return this;
79+
}
80+
81+
/**
82+
* Method for configuring handling of "absent" {@link java.util.Optional}
83+
* values; absent meaning case where no value is found to pass via
84+
* Creator method (constructor, factory method).
85+
* If enabled (set to {@code true}) it will be deserialized as
86+
* {@code null}; if disabled it will be read as "empty" {@code Optional}
87+
* (same as if encountering actual JSON {@code null} value).
88+
*<p>
89+
* Default is {@code false} for backwards compatibility (retains behavior
90+
* pre-2.14); for Jackson 3.0 default will likely be changed.
91+
*
92+
* @since 2.14
93+
*/
94+
public Jdk8Module configureReadAbsentAsNull(boolean state) {
95+
_cfgReadAbsentAsNull = state;
6896
return this;
6997
}
7098

datatypes/src/main/java/com/fasterxml/jackson/datatype/jdk8/OptionalDeserializer.java

+33-2
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,35 @@ final class OptionalDeserializer
1212
{
1313
private static final long serialVersionUID = 1L;
1414

15+
protected final boolean _cfgReadAbsentAsNull;
16+
1517
/*
1618
/**********************************************************
1719
/* Life-cycle
1820
/**********************************************************
1921
*/
2022

23+
/**
24+
* @since 2.14
25+
*/
26+
public OptionalDeserializer(JavaType fullType, ValueInstantiator inst,
27+
TypeDeserializer typeDeser, JsonDeserializer<?> deser,
28+
boolean cfgReadAbsentAsNull)
29+
{
30+
super(fullType, inst, typeDeser, deser);
31+
_cfgReadAbsentAsNull = cfgReadAbsentAsNull;
32+
}
33+
2134
/**
2235
* @since 2.9
36+
* @deprecated Since 2.14
2337
*/
38+
@Deprecated // @since 2.14
2439
public OptionalDeserializer(JavaType fullType, ValueInstantiator inst,
2540
TypeDeserializer typeDeser, JsonDeserializer<?> deser)
2641
{
27-
super(fullType, inst, typeDeser, deser);
42+
this(fullType, inst, typeDeser, deser,
43+
Jdk8Module.DEFAULT_READ_ABSENT_AS_NULL);
2844
}
2945

3046
/*
@@ -36,7 +52,7 @@ public OptionalDeserializer(JavaType fullType, ValueInstantiator inst,
3652
@Override
3753
public OptionalDeserializer withResolved(TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser) {
3854
return new OptionalDeserializer(_fullType, _valueInstantiator,
39-
typeDeser, valueDeser);
55+
typeDeser, valueDeser, _cfgReadAbsentAsNull);
4056
}
4157

4258
@Override
@@ -53,6 +69,21 @@ public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingExcep
5369
return getNullValue(ctxt);
5470
}
5571

72+
/**
73+
* As of Jackson 2.14 we will either return either same as
74+
* {@link #getNullValue} or {@code null}: see
75+
* {@like Jdk8Module#configureReadAbsentLikeNull(boolean)} for
76+
* details.
77+
*/
78+
@Override // @since 2.14
79+
public Object getAbsentValue(DeserializationContext ctxt) throws JsonMappingException {
80+
// Note: actual `null` vs "null value" (which is coerced as "empty")
81+
if (_cfgReadAbsentAsNull) {
82+
return null;
83+
}
84+
return getNullValue(ctxt);
85+
}
86+
5687
@Override
5788
public Optional<?> referenceValue(Object contents) {
5889
return Optional.ofNullable(contents);

datatypes/src/test/java/com/fasterxml/jackson/datatype/jdk8/CreatorTest.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.fasterxml.jackson.annotation.JsonCreator;
66
import com.fasterxml.jackson.annotation.JsonProperty;
77
import com.fasterxml.jackson.databind.ObjectMapper;
8+
import com.fasterxml.jackson.databind.json.JsonMapper;
89

910
public class CreatorTest extends ModuleTestBase
1011
{
@@ -34,7 +35,7 @@ public CreatorWithOptionalStrings(@JsonProperty("a") Optional<String> a,
3435
* Test to ensure that creator parameters use defaulting
3536
* (introduced in Jackson 2.6)
3637
*/
37-
public void testCreatorWithOptional() throws Exception
38+
public void testCreatorWithOptionalDefault() throws Exception
3839
{
3940
CreatorWithOptionalStrings bean = MAPPER.readValue(
4041
a2q("{'a':'foo'}"), CreatorWithOptionalStrings.class);
@@ -45,4 +46,22 @@ public void testCreatorWithOptional() throws Exception
4546
assertFalse(bean.b.isPresent());
4647
assertEquals("foo", bean.a.get());
4748
}
49+
50+
public void testCreatorWithOptionalAbsentAsNull() throws Exception
51+
{
52+
Jdk8Module module = new Jdk8Module()
53+
.configureReadAbsentAsNull(true);
54+
final ObjectMapper mapper = JsonMapper.builder()
55+
.addModule(module)
56+
.build();
57+
CreatorWithOptionalStrings bean = mapper.readValue(
58+
a2q("{'a':'foo'}"), CreatorWithOptionalStrings.class);
59+
assertNotNull(bean);
60+
assertNotNull(bean.a);
61+
assertTrue(bean.a.isPresent());
62+
assertEquals("foo", bean.a.get());
63+
64+
// This is the config change
65+
assertNull(bean.b);
66+
}
4867
}

release-notes/VERSION-2.x

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Modules:
1919
(contributed by (Maciej D)
2020
#242: Fix InstantSerializer ignoring the JsonFormat shape
2121
(contributed by KaseiFR@github)
22+
#251: Allow `Optional` deserialization for "absent" value as Java `null`
23+
(like other Reference types), not "empty"
2224

2325
2.13.4 (03-Sep-2022)
2426
2.13.3 (14-May-2022)

0 commit comments

Comments
 (0)