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

Commit 0b85784

Browse files
committed
Fix #8
1 parent 996aa1c commit 0b85784

File tree

8 files changed

+132
-15
lines changed

8 files changed

+132
-15
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ JDK 8 data types.
2121
<tag>HEAD</tag>
2222
</scm>
2323
<properties>
24-
<version.jackson>2.6.0-rc1</version.jackson>
24+
<version.jackson>2.6.0-rc2-SNAPSHOT</version.jackson>
2525
<!-- explicitly target JDK 8 -->
2626
<javac.src.version>1.8</javac.src.version>
2727
<javac.target.version>1.8</javac.target.version>

release-notes/VERSION

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Project: jackson-datatype-jdk8
44
=== Releases ===
55
------------------------------------------------------------------------
66

7+
2.6.0 (not yet released)
8+
9+
#8: JDK8 module should respect JsonInclude.Include.NON_ABSENT
10+
(reported by Emerson F)
11+
712
2.5.3 (24-Apr-2015)
813
2.5.2 (29-Mar-2015)
914
2.5.1 (06-Feb-2015)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ public class Jdk8Module extends Module
99
public void setupModule(SetupContext context) {
1010
context.addSerializers(new Jdk8Serializers());
1111
context.addDeserializers(new Jdk8Deserializers());
12+
// And to fully support Optionals, need to modify type info:
13+
context.addTypeModifier(new Jdk8TypeModifier());
1214
}
1315

1416
@Override
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.fasterxml.jackson.datatype.jdk8;
2+
3+
import java.lang.reflect.Type;
4+
import java.util.*;
5+
6+
import com.fasterxml.jackson.databind.JavaType;
7+
import com.fasterxml.jackson.databind.type.TypeBindings;
8+
import com.fasterxml.jackson.databind.type.TypeFactory;
9+
import com.fasterxml.jackson.databind.type.TypeModifier;
10+
11+
/**
12+
* We need to ensure `Optional` is a `ReferenceType`
13+
*/
14+
public class Jdk8TypeModifier extends TypeModifier
15+
{
16+
/**
17+
* Set of single-parameter-type types that we can handle using "standard" handling,
18+
* no additional tricks needed.
19+
*/
20+
private final static Class<?>[] OPTIONAL_TYPES = new Class<?>[] {
21+
OptionalInt.class, OptionalLong.class, OptionalDouble.class
22+
};
23+
24+
private final static Class<?>[] OPTIONAL_TYPE_PARAMS = new Class<?>[] {
25+
Integer.TYPE, Long.TYPE, Double.TYPE
26+
};
27+
28+
@Override
29+
public JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, TypeFactory typeFactory)
30+
{
31+
final Class<?> raw = type.getRawClass();
32+
33+
for (int i = 0, len = OPTIONAL_TYPES.length; i < len; ++i) {
34+
if (raw == OPTIONAL_TYPES[i]) {
35+
return typeFactory.constructReferenceType(raw,
36+
typeFactory.constructType(OPTIONAL_TYPE_PARAMS[i]));
37+
}
38+
}
39+
if (Optional.class.isAssignableFrom(raw)) {
40+
JavaType[] types = typeFactory.findTypeParameters(type, Optional.class);
41+
JavaType t = (types == null || types.length == 0) ? null : types[0];
42+
if (t == null) {
43+
t = TypeFactory.unknownType();
44+
}
45+
return typeFactory.constructReferenceType(raw, t);
46+
}
47+
return type;
48+
}
49+
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public class OptionalDoubleSerializer extends StdSerializer<OptionalDouble>
1616
public OptionalDoubleSerializer() {
1717
super(OptionalDouble.class);
1818
}
19+
20+
@Override
21+
public boolean isEmpty(SerializerProvider provider, OptionalDouble value) {
22+
return (value == null) || !value.isPresent();
23+
}
1924

2025
@Override
2126
public void serialize(OptionalDouble value, JsonGenerator jgen, SerializerProvider provider)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.fasterxml.jackson.datatype.jdk8;
22

33
import java.io.IOException;
4+
import java.util.OptionalDouble;
45
import java.util.OptionalInt;
56

67
import com.fasterxml.jackson.core.JsonGenerator;
@@ -22,7 +23,7 @@ public OptionalIntSerializer() {
2223
public boolean isEmpty(SerializerProvider provider, OptionalInt value) {
2324
return (value == null) || !value.isPresent();
2425
}
25-
26+
2627
@Override
2728
public void serialize(OptionalInt value, JsonGenerator jgen, SerializerProvider provider)
2829
throws IOException

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public boolean isEmpty(SerializerProvider provider, OptionalLong value) {
2525

2626
@Override
2727
public void serialize(OptionalLong value, JsonGenerator jgen, SerializerProvider provider)
28-
throws IOException
28+
throws IOException
2929
{
3030
if (value.isPresent()) {
3131
jgen.writeNumber(value.getAsLong());

src/test/java/com/fasterxml/jackson/datatype/jdk8/TestOptionalSerializer.java

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import java.util.*;
55

66
import com.fasterxml.jackson.annotation.JsonCreator;
7+
import com.fasterxml.jackson.annotation.JsonInclude;
78
import com.fasterxml.jackson.annotation.JsonProperty;
9+
810
import com.fasterxml.jackson.core.type.TypeReference;
911
import com.fasterxml.jackson.databind.ObjectMapper;
1012

@@ -43,8 +45,22 @@ public int hashCode() {
4345

4446
static class OptionalStringBean {
4547
public Optional<String> value;
48+
49+
public OptionalStringBean() { }
50+
OptionalStringBean(String str) {
51+
value = Optional.ofNullable(str);
52+
}
4653
}
4754

55+
static class OptionalLongBean {
56+
public OptionalLong value;
57+
58+
public OptionalLongBean() { value = OptionalLong.empty(); }
59+
OptionalLongBean(long v) {
60+
value = OptionalLong.of(v);
61+
}
62+
}
63+
4864
// [issue#4]
4965
static class Issue4Entity {
5066
private final Optional<String> data;
@@ -70,13 +86,13 @@ public boolean equals(Object o) {
7086
}
7187
}
7288

73-
private ObjectMapper mapper;
89+
private ObjectMapper MAPPER;
7490

7591
@Override
7692
protected void setUp() throws Exception
7793
{
7894
super.setUp();
79-
mapper = mapperWithModule();
95+
MAPPER = mapperWithModule();
8096
}
8197

8298
/*
@@ -97,32 +113,32 @@ public void testStringPresent() throws Exception
97113

98114
public void testIntAbsent() throws Exception
99115
{
100-
assertFalse(mapper.readValue(mapper.writeValueAsBytes(OptionalInt.empty()), OptionalInt.class).isPresent());
116+
assertFalse(MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalInt.empty()), OptionalInt.class).isPresent());
101117
}
102118

103119
public void testIntPresent() throws Exception
104120
{
105-
assertEquals(5, mapper.readValue(mapper.writeValueAsBytes(OptionalInt.of(5)), OptionalInt.class).getAsInt());
121+
assertEquals(5, MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalInt.of(5)), OptionalInt.class).getAsInt());
106122
}
107123

108124
public void testLongAbsent() throws Exception
109125
{
110-
assertFalse(mapper.readValue(mapper.writeValueAsBytes(OptionalLong.empty()), OptionalLong.class).isPresent());
126+
assertFalse(MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalLong.empty()), OptionalLong.class).isPresent());
111127
}
112128

113129
public void testLongPresent() throws Exception
114130
{
115-
assertEquals(Long.MAX_VALUE, mapper.readValue(mapper.writeValueAsBytes(OptionalLong.of(Long.MAX_VALUE)), OptionalLong.class).getAsLong());
131+
assertEquals(Long.MAX_VALUE, MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalLong.of(Long.MAX_VALUE)), OptionalLong.class).getAsLong());
116132
}
117133

118134
public void testDoubleAbsent() throws Exception
119135
{
120-
assertFalse(mapper.readValue(mapper.writeValueAsBytes(OptionalInt.empty()), OptionalInt.class).isPresent());
136+
assertFalse(MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalInt.empty()), OptionalInt.class).isPresent());
121137
}
122138

123139
public void testDoublePresent() throws Exception
124140
{
125-
assertEquals(Double.MIN_VALUE, mapper.readValue(mapper.writeValueAsBytes(OptionalDouble.of(Double.MIN_VALUE)), OptionalDouble.class).getAsDouble());
141+
assertEquals(Double.MIN_VALUE, MAPPER.readValue(MAPPER.writeValueAsBytes(OptionalDouble.of(Double.MIN_VALUE)), OptionalDouble.class).getAsDouble());
126142
}
127143

128144
public void testBeanAbsent() throws Exception
@@ -140,9 +156,9 @@ public void testBeanPresent() throws Exception
140156
public void testBeanWithCreator() throws Exception
141157
{
142158
final Issue4Entity emptyEntity = new Issue4Entity(Optional.empty());
143-
final String json = mapper.writeValueAsString(emptyEntity);
159+
final String json = MAPPER.writeValueAsString(emptyEntity);
144160

145-
final Issue4Entity deserialisedEntity = mapper.readValue(json, Issue4Entity.class);
161+
final Issue4Entity deserialisedEntity = MAPPER.readValue(json, Issue4Entity.class);
146162
if (!deserialisedEntity.equals(emptyEntity)) {
147163
throw new IOException("Entities not equal");
148164
}
@@ -151,11 +167,50 @@ public void testBeanWithCreator() throws Exception
151167
// [issue#4]
152168
public void testOptionalStringInBean() throws Exception
153169
{
154-
OptionalStringBean bean = mapper.readValue("{\"value\":\"xyz\"}", OptionalStringBean.class);
170+
OptionalStringBean bean = MAPPER.readValue("{\"value\":\"xyz\"}", OptionalStringBean.class);
155171
assertNotNull(bean.value);
156172
assertEquals("xyz", bean.value.get());
157173
}
158174

175+
// To support [issue#8]
176+
public void testExcludeIfOptionalAbsent() throws Exception
177+
{
178+
ObjectMapper mapper = mapperWithModule()
179+
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
180+
assertEquals(aposToQuotes("{'value':'foo'}"),
181+
mapper.writeValueAsString(new OptionalStringBean("foo")));
182+
// absent is not strictly null so
183+
assertEquals(aposToQuotes("{'value':null}"),
184+
mapper.writeValueAsString(new OptionalStringBean(null)));
185+
186+
// however:
187+
mapper = mapperWithModule()
188+
.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);
189+
assertEquals(aposToQuotes("{'value':'foo'}"),
190+
mapper.writeValueAsString(new OptionalStringBean("foo")));
191+
assertEquals(aposToQuotes("{}"),
192+
mapper.writeValueAsString(new OptionalStringBean(null)));
193+
}
194+
195+
public void testExcludeIfOptionalLongAbsent() throws Exception
196+
{
197+
ObjectMapper mapper = mapperWithModule()
198+
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
199+
assertEquals(aposToQuotes("{'value':123}"),
200+
mapper.writeValueAsString(new OptionalLongBean(123L)));
201+
// absent is not strictly null so
202+
assertEquals(aposToQuotes("{'value':null}"),
203+
mapper.writeValueAsString(new OptionalLongBean()));
204+
205+
// however:
206+
mapper = mapperWithModule()
207+
.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);
208+
assertEquals(aposToQuotes("{'value':456}"),
209+
mapper.writeValueAsString(new OptionalLongBean(456L)));
210+
assertEquals(aposToQuotes("{}"),
211+
mapper.writeValueAsString(new OptionalLongBean()));
212+
}
213+
159214
/*
160215
/**********************************************************
161216
/* Helper methods
@@ -164,6 +219,6 @@ public void testOptionalStringInBean() throws Exception
164219

165220
private <T> Optional<T> roundtrip(Optional<T> obj, TypeReference<Optional<T>> type) throws IOException
166221
{
167-
return mapper.readValue(mapper.writeValueAsBytes(obj), type);
222+
return MAPPER.readValue(MAPPER.writeValueAsBytes(obj), type);
168223
}
169224
}

0 commit comments

Comments
 (0)