Skip to content

Commit 119ddc9

Browse files
committed
Fix #731
1 parent fa1c2ff commit 119ddc9

File tree

8 files changed

+109
-14
lines changed

8 files changed

+109
-14
lines changed

release-notes/CREDITS

+6-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,12 @@ Dylan Scott (dylanscott@github)
221221
issue)
222222
(2.5.2)
223223

224+
Dmitry Spikhalskiy (Spikhalskiy@github)
225+
* Reported #731, suggested the way to fix it: XmlAdapter result marshaling error in
226+
case of ValueType=Object
227+
(2.5.3)
228+
224229
John Meyer (jpmeyer@github)
225-
* Reported, contributed fix for #745: EnumDeserializer.deserializerForCreator fails
230+
* Reported, contributed fix for #745: EnumDeserializer.deserializerForCreator() fails
226231
when used to deserialize a Map key
227232
(2.5.3)

release-notes/VERSION

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

77
2.5.3 (not yet released)
88

9+
#731: XmlAdapter result marshaling error in case of ValueType=Object
10+
(reported, debugged by Dmitry S)
911
#742: Allow deserialization of `null` Object Id (missing already allowed)
1012
#744: Custom deserializer with parent object update failing
1113
(reported by migel@github)

src/main/java/com/fasterxml/jackson/databind/JavaType.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,17 @@ public boolean isConcrete() {
290290
@Override
291291
public boolean isMapLikeType() { return false; }
292292

293+
/**
294+
* Convenience method, short-hand for
295+
*<code>
296+
* getRawClass() == Object.class
297+
*</code>
298+
* and used to figure if we basically have "untyped" type object.
299+
*
300+
* @since 2.5
301+
*/
302+
public final boolean isJavaLangObject() { return _class == Object.class; }
303+
293304
/**
294305
* Accessor for checking whether handlers for dealing with values of
295306
* this type should use static typing (as opposed to dynamic typing).
@@ -299,7 +310,7 @@ public boolean isConcrete() {
299310
* @since 2.2
300311
*/
301312
public final boolean useStaticType() { return _asStatic; }
302-
313+
303314
/*
304315
/**********************************************************
305316
/* Public API, type parameter access; pass-through

src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ public JsonSerializer<Object> createSerializer(SerializerProvider prov,
159159
// [#359]: explicitly check (again) for @JsonSerializer...
160160
ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo());
161161
}
162-
if (ser == null) {
162+
// [databind#731]: Should skip if nominally java.lang.Object
163+
if (ser == null && !delegateType.isJavaLangObject()) {
163164
ser = _createSerializer2(prov, delegateType, beanDesc, true);
164165
}
165166
return new StdDelegatingSerializer(conv, delegateType, ser);

src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,9 @@ protected JsonSerializer<Object> findConvertingSerializer(SerializerProvider pro
366366
if (convDef != null) {
367367
Converter<Object,Object> conv = provider.converterInstance(prop.getMember(), convDef);
368368
JavaType delegateType = conv.getOutputType(provider.getTypeFactory());
369-
JsonSerializer<?> ser = provider.findValueSerializer(delegateType, prop);
369+
// [databind#731]: Should skip if nominally java.lang.Object
370+
JsonSerializer<?> ser = delegateType.isJavaLangObject() ? null
371+
: provider.findValueSerializer(delegateType, prop);
370372
return new StdDelegatingSerializer(conv, delegateType, ser);
371373
}
372374
}

src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java

+47-7
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,20 @@ public JsonSerializer<?> createContextual(SerializerProvider provider, BeanPrope
115115
if (delegateType == null) {
116116
delegateType = _converter.getOutputType(provider.getTypeFactory());
117117
}
118-
delSer = provider.findValueSerializer(delegateType);
118+
/* 02-Apr-2015, tatu: For "dynamic case", where type is only specified as
119+
* java.lang.Object (or missing generic), [databind#731]
120+
*/
121+
if (!delegateType.isJavaLangObject()) {
122+
delSer = provider.findValueSerializer(delegateType);
123+
}
119124
}
120125
if (delSer instanceof ContextualSerializer) {
121126
delSer = provider.handleSecondaryContextualization(delSer, property);
122127
}
123-
return (delSer == _delegateSerializer) ? this
124-
: withDelegate(_converter, delegateType, delSer);
128+
if (delSer == _delegateSerializer && delegateType == _delegateType) {
129+
return this;
130+
}
131+
return withDelegate(_converter, delegateType, delSer);
125132
}
126133

127134
/*
@@ -154,7 +161,12 @@ public void serialize(Object value, JsonGenerator gen, SerializerProvider provid
154161
provider.defaultSerializeNull(gen);
155162
return;
156163
}
157-
_delegateSerializer.serialize(delegateValue, gen, provider);
164+
// 02-Apr-2015, tatu: As per [databind#731] may need to do dynamic lookup
165+
JsonSerializer<Object> ser = _delegateSerializer;
166+
if (ser == null) {
167+
ser = _findSerializer(delegateValue, provider);
168+
}
169+
ser.serialize(delegateValue, gen, provider);
158170
}
159171

160172
@Override
@@ -165,21 +177,31 @@ public void serializeWithType(Object value, JsonGenerator gen, SerializerProvide
165177
* let's give it a chance?
166178
*/
167179
Object delegateValue = convertValue(value);
168-
_delegateSerializer.serializeWithType(delegateValue, gen, provider, typeSer);
180+
JsonSerializer<Object> ser = _delegateSerializer;
181+
if (ser == null) {
182+
ser = _findSerializer(value, provider);
183+
}
184+
ser.serializeWithType(delegateValue, gen, provider, typeSer);
169185
}
170186

171187
@Override
172-
@Deprecated // since 1.5
188+
@Deprecated // since 2.5
173189
public boolean isEmpty(Object value)
174190
{
175191
Object delegateValue = convertValue(value);
192+
if (_delegateSerializer == null) { // best we can do for now, too costly to look up
193+
return (value == null);
194+
}
176195
return _delegateSerializer.isEmpty(delegateValue);
177196
}
178197

179198
@Override
180199
public boolean isEmpty(SerializerProvider prov, Object value)
181200
{
182201
Object delegateValue = convertValue(value);
202+
if (_delegateSerializer == null) { // best we can do for now, too costly to look up
203+
return (value == null);
204+
}
183205
return _delegateSerializer.isEmpty(prov, delegateValue);
184206
}
185207

@@ -216,7 +238,10 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t
216238
/* 03-Sep-2012, tatu: Not sure if this can be made to really work
217239
* properly... but for now, try this:
218240
*/
219-
_delegateSerializer.acceptJsonFormatVisitor(visitor, typeHint);
241+
// 02-Apr-2015, tatu: For dynamic case, very little we can do
242+
if (_delegateSerializer != null) {
243+
_delegateSerializer.acceptJsonFormatVisitor(visitor, typeHint);
244+
}
220245
}
221246

222247
/*
@@ -239,4 +264,19 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t
239264
protected Object convertValue(Object value) {
240265
return _converter.convert(value);
241266
}
267+
268+
/**
269+
* Helper method used for locating serializer to use in dynamic use case, where
270+
* actual type value gets converted to is not specified beyond basic
271+
* {@link java.lang.Object}, and where serializer needs to be located dynamically
272+
* based on actual value type.
273+
*
274+
* @since 2.6
275+
*/
276+
protected JsonSerializer<Object> _findSerializer(Object value, SerializerProvider serializers)
277+
throws JsonMappingException
278+
{
279+
// NOTE: will NOT call contextualization
280+
return serializers.findValueSerializer(value.getClass());
281+
}
242282
}

src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,6 @@ protected JsonSerializer<?> findConvertingContentSerializer(SerializerProvider p
250250
* when applying contextual content converter; this is not ideal way,
251251
* but should work for most cases.
252252
*/
253-
254253
final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
255254
if (intr != null && prop != null) {
256255
AnnotatedMember m = prop.getMember();
@@ -259,7 +258,8 @@ protected JsonSerializer<?> findConvertingContentSerializer(SerializerProvider p
259258
if (convDef != null) {
260259
Converter<Object,Object> conv = provider.converterInstance(prop.getMember(), convDef);
261260
JavaType delegateType = conv.getOutputType(provider.getTypeFactory());
262-
if (existingSerializer == null) {
261+
// [databind#731]: Should skip if nominally java.lang.Object
262+
if (existingSerializer == null && !delegateType.hasRawClass(Object.class)) {
263263
existingSerializer = provider.findValueSerializer(delegateType);
264264
}
265265
return new StdDelegatingSerializer(conv, delegateType, existingSerializer);

src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingSerializer.java

+35-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,33 @@ public void serialize(Target a, JsonGenerator jsonGenerator, SerializerProvider
122122
jsonGenerator.writeString("Target");
123123
}
124124
}
125-
125+
126+
// [Issue#731]
127+
public static class DummyBean {
128+
public final int a, b;
129+
public DummyBean(int v1, int v2) {
130+
a = v1 * 2;
131+
b = v2 * 2;
132+
}
133+
}
134+
135+
@JsonSerialize(converter = UntypedConvertingBeanConverter.class)
136+
static class ConvertingBeanWithUntypedConverter {
137+
public int x, y;
138+
public ConvertingBeanWithUntypedConverter(int v1, int v2) {
139+
x = v1;
140+
y = v2;
141+
}
142+
}
143+
144+
static class UntypedConvertingBeanConverter extends StdConverter<ConvertingBeanWithUntypedConverter, Object>
145+
{
146+
@Override
147+
public Object convert(ConvertingBeanWithUntypedConverter cb) {
148+
return new DummyBean(cb.x, cb.y);
149+
}
150+
}
151+
126152
/*
127153
/**********************************************************
128154
/* Test methods
@@ -168,4 +194,12 @@ public void testIssue359() throws Exception {
168194
String json = objectWriter().writeValueAsString(new Bean359());
169195
assertEquals("{\"stuff\":[\"Target\"]}", json);
170196
}
197+
198+
// [databind#731]: Problems converting from java.lang.Object ("unknown")
199+
public void testIssue731() throws Exception
200+
{
201+
String json = objectWriter().writeValueAsString(new ConvertingBeanWithUntypedConverter(1, 2));
202+
// must be {"a":2,"b":4}
203+
assertEquals("{\"a\":2,\"b\":4}", json);
204+
}
171205
}

0 commit comments

Comments
 (0)