Skip to content
This repository was archived by the owner on Nov 7, 2019. It is now read-only.

Commit a4a0fb4

Browse files
author
Henning Schmiedehausen
committed
Add configureAbsentsAsNulls config setting
This aligns this feature with the Guava module. However, the default is 'false' as compared to the Guava 'true' for backwards compatibility.
1 parent 21d3908 commit a4a0fb4

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.fasterxml.jackson.datatype.jdk8;
2+
3+
import com.fasterxml.jackson.databind.BeanDescription;
4+
import com.fasterxml.jackson.databind.SerializationConfig;
5+
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
6+
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
7+
8+
import java.util.List;
9+
import java.util.Optional;
10+
11+
/**
12+
* {@link BeanSerializerModifier} needed to sneak in handler to exclude "absent"
13+
* optional values iff handling of "absent as nulls" is enabled.
14+
*/
15+
public class Jdk8BeanSerializerModifier extends BeanSerializerModifier
16+
{
17+
@Override
18+
public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
19+
BeanDescription beanDesc,
20+
List<BeanPropertyWriter> beanProperties)
21+
{
22+
for (int i = 0; i < beanProperties.size(); ++i) {
23+
final BeanPropertyWriter writer = beanProperties.get(i);
24+
if (Optional.class.isAssignableFrom(writer.getPropertyType())) {
25+
beanProperties.set(i, new Jdk8OptionalBeanPropertyWriter(writer));
26+
}
27+
}
28+
return beanProperties;
29+
}
30+
}

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,57 @@
55

66
public class Jdk8Module extends Module
77
{
8+
/**
9+
* Configuration setting that determines whether `Optional.empty()` is
10+
* considered "same as null" for serialization purposes; that is, to be
11+
* filtered same as nulls are.
12+
* If enabled, absent values are treated like nulls; if disabled, they are not.
13+
* In either case, absent values are always considered "empty".
14+
*<p>
15+
* Default value is `false` for backwards compatibility (2.5 and prior
16+
* only had this behavior).
17+
*<p>
18+
* Note that this setting MUST be changed BEFORE registering the module:
19+
* changes after registration will have no effect.
20+
*/
21+
protected boolean _cfgHandleAbsentAsNull = false;
22+
823
@Override
924
public void setupModule(SetupContext context) {
1025
context.addSerializers(new Jdk8Serializers());
1126
context.addDeserializers(new Jdk8Deserializers());
1227
// And to fully support Optionals, need to modify type info:
1328
context.addTypeModifier(new Jdk8TypeModifier());
29+
30+
// Allow enabling "treat Optional.empty() like Java nulls"
31+
if (_cfgHandleAbsentAsNull) {
32+
context.addBeanSerializerModifier(new Jdk8BeanSerializerModifier());
33+
}
1434
}
1535

1636
@Override
1737
public Version version() {
1838
return PackageVersion.VERSION;
1939
}
2040

41+
/**
42+
* Configuration method that may be used to change configuration setting
43+
* {@link #_cfgHandleAbsentAsNull}: enabling means that `Optional.empty()` values
44+
* are handled like Java nulls (wrt filtering on serialization); disabling that
45+
* they are only treated as "empty" values, but not like native Java nulls.
46+
* Recommended setting for this value is `false`. For compatibility with older versions
47+
* of other "optional" values (like Guava optionals), it can be set to 'true'. The
48+
* default is `false` for backwards compatibility.
49+
*
50+
* @return This module instance, useful for chaining calls
51+
*
52+
* @since 2.6
53+
*/
54+
public Jdk8Module configureAbsentsAsNulls(boolean state) {
55+
_cfgHandleAbsentAsNull = state;
56+
return this;
57+
}
58+
2159
@Override
2260
public int hashCode() {
2361
return getClass().hashCode();
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.fasterxml.jackson.datatype.jdk8;
2+
3+
import com.fasterxml.jackson.core.JsonGenerator;
4+
import com.fasterxml.jackson.databind.SerializerProvider;
5+
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
6+
7+
import java.util.Optional;
8+
9+
public class Jdk8OptionalBeanPropertyWriter extends BeanPropertyWriter {
10+
11+
protected Jdk8OptionalBeanPropertyWriter(BeanPropertyWriter base) {
12+
super(base);
13+
}
14+
15+
@Override
16+
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov) throws Exception
17+
{
18+
if (_nullSerializer == null) {
19+
Object value = get(bean);
20+
if (value == null || Optional.empty().equals(value)) {
21+
return;
22+
}
23+
}
24+
super.serializeAsField(bean, jgen, prov);
25+
}
26+
27+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.fasterxml.jackson.datatype.jdk8;
2+
3+
import java.util.Optional;
4+
5+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
6+
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
7+
import com.fasterxml.jackson.annotation.JsonInclude;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
10+
public class TestConfigureAbsentsAsNulls extends ModuleTestBase
11+
{
12+
@JsonAutoDetect(fieldVisibility=Visibility.ANY)
13+
public static final class OptionalData {
14+
public Optional<String> myString = Optional.empty();
15+
}
16+
17+
/*
18+
/**********************************************************************
19+
/* Test methods
20+
/**********************************************************************
21+
*/
22+
23+
public void testConfigAbsentsAsNullsTrue() throws Exception {
24+
ObjectMapper mapper = new ObjectMapper();
25+
mapper.registerModule(new Jdk8Module().configureAbsentsAsNulls(true));
26+
27+
OptionalData data = new OptionalData();
28+
String value = mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(data);
29+
assertEquals("{}", value);
30+
}
31+
32+
public void testConfigAbsentsAsNullsFalse() throws Exception {
33+
ObjectMapper mapper = new ObjectMapper();
34+
mapper.registerModule(new Jdk8Module().configureAbsentsAsNulls(false));
35+
36+
OptionalData data = new OptionalData();
37+
String value = mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(data);
38+
assertEquals("{\"myString\":null}", value);
39+
}
40+
41+
public void testConfigNonAbsentAbsentsAsNullsTrue() throws Exception {
42+
ObjectMapper mapper = new ObjectMapper();
43+
mapper.registerModule(new Jdk8Module().configureAbsentsAsNulls(true));
44+
45+
OptionalData data = new OptionalData();
46+
String value = mapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT).writeValueAsString(data);
47+
assertEquals("{}", value);
48+
}
49+
50+
public void testConfigNonAbsentAbsentsAsNullsFalse() throws Exception {
51+
ObjectMapper mapper = new ObjectMapper();
52+
mapper.registerModule(new Jdk8Module().configureAbsentsAsNulls(false));
53+
54+
OptionalData data = new OptionalData();
55+
String value = mapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT).writeValueAsString(data);
56+
assertEquals("{}", value);
57+
}
58+
}

0 commit comments

Comments
 (0)