Skip to content
This repository was archived by the owner on Jan 20, 2025. It is now read-only.

Commit eb6b98a

Browse files
committed
Fixed #64
1 parent 8ca5023 commit eb6b98a

File tree

7 files changed

+166
-78
lines changed

7 files changed

+166
-78
lines changed

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Guava (http://code.google.com/p/guava-libraries/) types (currently mostly just c
2929
</contributors>
3030

3131
<properties>
32-
<version.jackson>2.6.0-rc2</version.jackson>
32+
<version.jackson>2.6.0-rc3</version.jackson>
3333

3434
<version.guava>15.0</version.guava>
3535
<version.guava.osgi>${version.guava}.0</version.guava.osgi>

release-notes/CREDITS

+10-7
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ Tatu Saloranta ([email protected]): author
77
Steven Schlansker ([email protected]): co-author
88

99
Stephan Schroevers: (Stephan202@github)
10-
11-
* Contributed #56: Add support `HashCode`
12-
(2.5.0)
13-
* Contributed #65: Add deserialization support for SortedMultiset and ImmutableSortedMultiset
14-
(2.5.3)
10+
* Contributed #56: Add support `HashCode`
11+
(2.5.0)
12+
* Contributed #65: Add deserialization support for SortedMultiset and ImmutableSortedMultiset
13+
(2.5.3)
1514

1615
Michael Hixson:
16+
* Contributed fix for #67: Support deserializing ImmutableSetMultimaps
17+
(2.6.0)
18+
19+
Alexey Kobyakov (akobiakov@github)
20+
* Reported #64: `@JsonUnwrapped` annotation is ignored when a field is an Optional
21+
(2.6.0)
1722

18-
* Contributed fix for #67: Support deserializing ImmutableSetMultimaps
19-
(2.6.0)

release-notes/VERSION

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Project: jackson-datatype-guava
66

77
2.6.0 (not yet released)
88

9+
#64: `@JsonUnwrapped` annotation is ignored when a field is an Optional
10+
(reported by Alexey K, akobiakov@github)
911
#66: Add `GuavaModule.configureAbsentsAsNulls(boolean)` to change whether
1012
`Optional.absent()` is to be handled same as Java null during serialization
1113
(default: true) or not.

src/main/java/com/fasterxml/jackson/datatype/guava/GuavaModule.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*<p>
1111
* Current configurability includes:
1212
*<ul>
13-
* <li><code>configureAbsentsAsNulls</code> (default: <code>true</code>):
13+
* <li><code>configureAbsentsAsNulls</code> (default: <code>false</code>):
1414
* Determines whether inclusion strategy of <code>NON_NULL</code> should additionally consider
1515
* <code>Optional.absent()</code> values (as POJO properties) to be excluded; if true, they will
1616
* be excluded, if false, they will be included.

src/main/java/com/fasterxml/jackson/datatype/guava/ser/GuavaOptionalSerializer.java

+141-54
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
88
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
99
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
10+
import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
1011
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
1112
import com.fasterxml.jackson.databind.type.TypeFactory;
13+
import com.fasterxml.jackson.databind.util.NameTransformer;
1214
import com.google.common.base.Optional;
1315

1416
@SuppressWarnings("serial")
@@ -17,43 +19,61 @@ public final class GuavaOptionalSerializer
1719
implements ContextualSerializer
1820
{
1921
/**
20-
* Declared type for the property being serialized with
21-
* this serializer instance.
22+
* Declared type parameter for Optional.
2223
*/
23-
protected final JavaType _optionalType;
24+
protected final JavaType _referredType;
2425

26+
protected final BeanProperty _property;
27+
2528
protected final JsonSerializer<Object> _valueSerializer;
2629

27-
public GuavaOptionalSerializer(JavaType type) {
28-
this(type, null);
30+
/**
31+
* To support unwrapped values of dynamic types, will need this:
32+
*/
33+
protected final NameTransformer _unwrapper;
34+
35+
/**
36+
* If element type can not be statically determined, mapping from
37+
* runtime type to serializer is handled using this object
38+
*
39+
* @since 2.6
40+
*/
41+
protected transient PropertySerializerMap _dynamicSerializers;
42+
43+
/*
44+
/**********************************************************
45+
/* Life-cycle
46+
/**********************************************************
47+
*/
48+
49+
public GuavaOptionalSerializer(JavaType optionalType) {
50+
super(optionalType);
51+
_referredType = _valueType(optionalType);
52+
_property = null;
53+
_valueSerializer = null;
54+
_unwrapper = null;
55+
_dynamicSerializers = PropertySerializerMap.emptyForProperties();
2956
}
3057

3158
@SuppressWarnings("unchecked")
32-
protected GuavaOptionalSerializer(JavaType type, JsonSerializer<?> valueSer)
59+
protected GuavaOptionalSerializer(GuavaOptionalSerializer base,
60+
BeanProperty property, JsonSerializer<?> valueSer, NameTransformer unwrapper)
3361
{
34-
super(type);
35-
_optionalType = type;
62+
super(base);
63+
_referredType = base._referredType;
64+
_dynamicSerializers = base._dynamicSerializers;
65+
_property = property;
3666
_valueSerializer = (JsonSerializer<Object>) valueSer;
67+
_unwrapper = unwrapper;
3768
}
3869

39-
protected GuavaOptionalSerializer withResolved(BeanProperty property,
40-
JsonSerializer<?> ser)
70+
protected GuavaOptionalSerializer withResolved(BeanProperty prop,
71+
JsonSerializer<?> ser, NameTransformer unwrapper)
4172
{
42-
if (_valueSerializer == ser) {
73+
if ((_property == prop) && (_valueSerializer == ser) && (_unwrapper == unwrapper)) {
4374
return this;
4475
}
45-
return new GuavaOptionalSerializer(_optionalType, ser);
46-
}
47-
48-
@Override
49-
@Deprecated
50-
public boolean isEmpty(Optional<?> value) {
51-
return isEmpty(null, value);
52-
}
53-
54-
@Override
55-
public boolean isEmpty(SerializerProvider prov, Optional<?> value) {
56-
return (value == null) || !value.isPresent();
76+
return new GuavaOptionalSerializer(this, prop, ser, unwrapper);
5777
}
5878

5979
@Override
@@ -63,78 +83,145 @@ public JsonSerializer<?> createContextual(SerializerProvider provider,
6383
JsonSerializer<?> ser = _valueSerializer;
6484
if (ser == null) {
6585
// we'll have type parameter available due to GuavaTypeModifier making sure it is, so:
66-
JavaType valueType = _valueType();
67-
boolean realType = !valueType.hasRawClass(Object.class);
86+
boolean realType = !_referredType.hasRawClass(Object.class);
6887
/* Can only assign serializer statically if the declared type is final,
6988
* or if we are to use static typing (and type is not "untyped")
7089
*/
7190
if (realType &&
7291
(provider.isEnabled(MapperFeature.USE_STATIC_TYPING)
73-
|| valueType.isFinal())) {
92+
|| _referredType.isFinal())) {
7493
return withResolved(property,
75-
provider.findPrimaryPropertySerializer(valueType, property));
94+
provider.findPrimaryPropertySerializer(_referredType, property),
95+
_unwrapper);
7696
}
7797
} else {
7898
// not sure if/when this should occur but proper way to deal would be:
7999
return withResolved(property,
80-
provider.handlePrimaryContextualization(ser, property));
100+
provider.handlePrimaryContextualization(ser, property),
101+
_unwrapper);
81102
}
82103
return this;
83104
}
84105

85106
@Override
86-
public void serialize(Optional<?> value, JsonGenerator jgen, SerializerProvider provider)
107+
public JsonSerializer<Optional<?>> unwrappingSerializer(NameTransformer transformer) {
108+
JsonSerializer<Object> ser = _valueSerializer;
109+
if (ser != null) {
110+
ser = ser.unwrappingSerializer(transformer);
111+
}
112+
NameTransformer unwrapper = (_unwrapper == null) ? transformer
113+
: NameTransformer.chainedTransformer(transformer, _unwrapper);
114+
return withResolved(_property, ser, unwrapper);
115+
}
116+
117+
/*
118+
/**********************************************************
119+
/* API overrides
120+
/**********************************************************
121+
*/
122+
123+
@Override
124+
@Deprecated
125+
public boolean isEmpty(Optional<?> value) {
126+
return isEmpty(null, value);
127+
}
128+
129+
@Override
130+
public boolean isEmpty(SerializerProvider prov, Optional<?> value) {
131+
return (value == null) || !value.isPresent();
132+
}
133+
134+
public boolean isUnwrappingSerializer() {
135+
return (_unwrapper != null);
136+
}
137+
138+
/*
139+
/**********************************************************
140+
/* Serialization methods
141+
/**********************************************************
142+
*/
143+
144+
@Override
145+
public void serialize(Optional<?> opt, JsonGenerator gen, SerializerProvider provider)
87146
throws IOException
88147
{
89-
if (value.isPresent()) {
90-
if (_valueSerializer != null) {
91-
_valueSerializer.serialize(value.get(), jgen, provider);
92-
} else {
93-
provider.defaultSerializeValue(value.get(), jgen);
148+
if (opt.isPresent()) {
149+
Object value = opt.get();
150+
JsonSerializer<Object> ser = _valueSerializer;
151+
if (ser == null) {
152+
ser = _findSerializer(provider, value.getClass());
94153
}
154+
ser.serialize(value, gen, provider);
95155
} else {
96-
provider.defaultSerializeNull(jgen);
156+
provider.defaultSerializeNull(gen);
97157
}
98158
}
99159

100160
@Override
101-
public void serializeWithType(Optional<?> value,
102-
JsonGenerator jgen, SerializerProvider provider,
161+
public void serializeWithType(Optional<?> opt,
162+
JsonGenerator gen, SerializerProvider provider,
103163
TypeSerializer typeSer) throws IOException
104164
{
105-
if (value.isPresent()) {
165+
if (opt.isPresent()) {
166+
Object value = opt.get();
106167
JsonSerializer<Object> ser = _valueSerializer;
107168
if (ser == null) {
108-
// note: could improve by retaining property... needed?
109-
ser = provider.findValueSerializer(_valueType(), null);
169+
ser = _findSerializer(provider, value.getClass());
110170
}
111-
ser.serializeWithType(value.get(), jgen, provider, typeSer);
171+
ser.serializeWithType(value, gen, provider, typeSer);
112172
} else {
113-
provider.defaultSerializeNull(jgen);
173+
provider.defaultSerializeNull(gen);
114174
}
115175
}
116176

177+
/*
178+
/**********************************************************
179+
/* Introspection support
180+
/**********************************************************
181+
*/
182+
117183
@Override
118184
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException
119185
{
120-
JavaType valueType = _valueType();
121-
if (valueType != null) {
122-
JsonSerializer<?> ser = _valueSerializer;
123-
if (ser == null) {
124-
ser = visitor.getProvider().findValueSerializer(valueType, null);
125-
}
126-
ser.acceptJsonFormatVisitor(visitor, valueType);
127-
} else {
128-
super.acceptJsonFormatVisitor(visitor, typeHint);
186+
JsonSerializer<?> ser = _valueSerializer;
187+
if (ser == null) {
188+
ser = _findSerializer(visitor.getProvider(), _referredType.getRawClass());
129189
}
190+
ser.acceptJsonFormatVisitor(visitor, _referredType);
130191
}
131192

132-
protected JavaType _valueType() {
133-
JavaType valueType = _optionalType.containedType(0);
193+
/*
194+
/**********************************************************
195+
/* Misc other
196+
/**********************************************************
197+
*/
198+
199+
protected static JavaType _valueType(JavaType optionalType) {
200+
JavaType valueType = optionalType.containedType(0);
134201
if (valueType == null) {
135202
valueType = TypeFactory.unknownType();
136203
}
137204
return valueType;
138205
}
206+
207+
/**
208+
* Helper method that encapsulates logic of retrieving and caching required
209+
* serializer.
210+
*/
211+
protected final JsonSerializer<Object> _findSerializer(SerializerProvider provider, Class<?> type)
212+
throws JsonMappingException
213+
{
214+
PropertySerializerMap.SerializerAndMapResult result = _dynamicSerializers
215+
.findAndAddPrimarySerializer(type, provider, _property);
216+
if (_dynamicSerializers != result.map) {
217+
_dynamicSerializers = result.map;
218+
}
219+
JsonSerializer<Object> ser = result.serializer;
220+
// 26-Jun-2015, tatu: Sub-optimal if we do not cache unwrapped instance; but on plus side
221+
// construction is a cheap operation, so won't add huge overhead
222+
if (_unwrapper != null) {
223+
ser = ser.unwrappingSerializer(_unwrapper);
224+
}
225+
return ser;
226+
}
139227
}
140-

src/test/java/com/fasterxml/jackson/datatype/guava/TestOptional.java renamed to src/test/java/com/fasterxml/jackson/datatype/guava/OptionalBasicTest.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import com.fasterxml.jackson.databind.ObjectMapper;
1010
import com.google.common.base.Optional;
1111

12-
public class TestOptional extends ModuleTestBase
12+
public class OptionalBasicTest extends ModuleTestBase
1313
{
1414
private final ObjectMapper MAPPER = mapperWithModule();
1515

@@ -76,39 +76,39 @@ public void testDeserComplexObject() throws Exception {
7676
assertTrue(data.get().myString.isPresent());
7777
assertEquals("simpleString", data.get().myString.get());
7878
}
79-
79+
8080
public void testDeserGeneric() throws Exception {
8181
TypeReference<Optional<OptionalGenericData<String>>> type = new TypeReference<Optional<OptionalGenericData<String>>>() {};
8282
Optional<OptionalGenericData<String>> data = MAPPER.readValue("{\"myData\":\"simpleString\"}", type);
8383
assertTrue(data.isPresent());
8484
assertTrue(data.get().myData.isPresent());
8585
assertEquals("simpleString", data.get().myData.get());
8686
}
87-
87+
8888
public void testSerAbsent() throws Exception {
8989
String value = MAPPER.writeValueAsString(Optional.absent());
9090
assertEquals("null", value);
9191
}
92-
92+
9393
public void testSerSimpleString() throws Exception {
9494
String value = MAPPER.writeValueAsString(Optional.of("simpleString"));
9595
assertEquals("\"simpleString\"", value);
9696
}
97-
97+
9898
public void testSerInsideObject() throws Exception {
9999
OptionalData data = new OptionalData();
100100
data.myString = Optional.of("simpleString");
101101
String value = MAPPER.writeValueAsString(data);
102102
assertEquals("{\"myString\":\"simpleString\"}", value);
103103
}
104-
104+
105105
public void testSerComplexObject() throws Exception {
106106
OptionalData data = new OptionalData();
107107
data.myString = Optional.of("simpleString");
108108
String value = MAPPER.writeValueAsString(Optional.of(data));
109109
assertEquals("{\"myString\":\"simpleString\"}", value);
110110
}
111-
111+
112112
public void testSerGeneric() throws Exception {
113113
OptionalGenericData<String> data = new OptionalGenericData<String>();
114114
data.myData = Optional.of("simpleString");
@@ -198,7 +198,7 @@ public void testObjectId() throws Exception
198198
final Unit input = new Unit();
199199
input.link(input);
200200
String json = MAPPER.writeValueAsString(input);
201-
Unit result = MAPPER.readValue(json, Unit.class);
201+
Unit result = MAPPER.readValue(json, Unit.class);
202202
assertNotNull(result);
203203
assertNotNull(result.baseUnit);
204204
assertTrue(result.baseUnit.isPresent());

0 commit comments

Comments
 (0)