Skip to content

Commit ad152e1

Browse files
committed
Merge branch 'master' of github.com:FasterXML/jackson-databind
2 parents 4e935cb + 295d9e4 commit ad152e1

File tree

8 files changed

+166
-33
lines changed

8 files changed

+166
-33
lines changed

release-notes/VERSION

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ Version: 2.5.0 (xx-xxx-2014)
44
#521: Keep bundle annotations, prevent problems with recursive annotation
55
types
66
(reported by tea-dragon@github)
7+
#539: Problem with post-procesing of "empty bean" serializer; was not calling
8+
'BeanSerializerModifier.modifySerializer()` for empty beans
9+
(reported by Fabien R, fabienrenaud@github)
710
#543: Problem resolving self-referential recursive types
811
(reported by ahgittin@github)
912

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,12 +1073,23 @@ public ObjectMapper enableDefaultTyping(DefaultTyping dti) {
10731073
* Method for enabling automatic inclusion of type information, needed
10741074
* for proper deserialization of polymorphic types (unless types
10751075
* have been annotated with {@link com.fasterxml.jackson.annotation.JsonTypeInfo}).
1076+
*<P>
1077+
* NOTE: use of {@link JsonTypeInfo.As#EXTERNAL_PROPERTY} is <b>NOT SUPPORTED</b>;
1078+
* and attempts of do so will throw an {@link IllegalArgumentException} to make
1079+
* this limitation explicit.
10761080
*
10771081
* @param applicability Defines kinds of types for which additional type information
10781082
* is added; see {@link DefaultTyping} for more information.
10791083
*/
10801084
public ObjectMapper enableDefaultTyping(DefaultTyping applicability, JsonTypeInfo.As includeAs)
10811085
{
1086+
/* 18-Sep-2014, tatu: Let's add explicit check to ensure no one tries to
1087+
* use "As.EXTERNAL_PROPERTY", since that will not work.
1088+
*/
1089+
if (includeAs == JsonTypeInfo.As.EXTERNAL_PROPERTY) {
1090+
throw new IllegalArgumentException("Can not use includeAs of "+includeAs);
1091+
}
1092+
10821093
TypeResolverBuilder<?> typer = new DefaultTypeResolverBuilder(applicability);
10831094
// we'll always use full class name, when using defaulting
10841095
typer = typer.init(JsonTypeInfo.Id.CLASS, null);

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

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,17 @@ public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> unknownType) {
745745
return _unknownTypeSerializer;
746746
}
747747

748+
/**
749+
* Helper method called to see if given serializer is considered to be
750+
* something returned by {@link #getUnknownTypeSerializer}, that is, something
751+
* for which no regular serializer was found or constructed.
752+
*
753+
* @since 2.5
754+
*/
755+
public boolean isUnknownTypeSerializer(JsonSerializer<?> ser) {
756+
return (ser == _unknownTypeSerializer) || (ser == null);
757+
}
758+
748759
/*
749760
/**********************************************************
750761
/* Methods for creating instances based on annotations
@@ -987,19 +998,26 @@ protected void _reportIncompatibleRootType(Object value, JavaType rootType)
987998
* @return Serializer if one can be found, null if not.
988999
*/
9891000
protected JsonSerializer<Object> _findExplicitUntypedSerializer(Class<?> runtimeType)
990-
throws JsonMappingException
1001+
throws JsonMappingException
9911002
{
9921003
// Fast lookup from local lookup thingy works?
9931004
JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(runtimeType);
994-
if (ser != null) {
995-
return ser;
996-
}
997-
// If not, maybe shared map already has it?
998-
ser = _serializerCache.untypedValueSerializer(runtimeType);
999-
if (ser != null) {
1000-
return ser;
1005+
if (ser == null) {
1006+
// If not, maybe shared map already has it?
1007+
ser = _serializerCache.untypedValueSerializer(runtimeType);
1008+
if (ser == null) {
1009+
ser = _createAndCacheUntypedSerializer(runtimeType);
1010+
/* 18-Sep-2014, tatu: This is unfortunate patch over related change
1011+
* that pushes creation of "unknown type" serializer deeper down
1012+
* in BeanSerializerFactory; as a result, we need to "undo" creation
1013+
* here.
1014+
*/
1015+
if (isUnknownTypeSerializer(ser)) {
1016+
return null;
1017+
}
1018+
}
10011019
}
1002-
return _createAndCacheUntypedSerializer(runtimeType);
1020+
return ser;
10031021
}
10041022

10051023
/*

src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,11 @@ private void _explode(Collection<PropertyName> newNames,
857857
for (Linked<?> node = accessors; node != null; node = node.next) {
858858
PropertyName name = node.name;
859859
if (!node.isNameExplicit || name == null) { // no explicit name -- problem!
860+
// [Issue#541] ... but only as long as it's visible
861+
if (!node.isVisible) {
862+
continue;
863+
}
864+
860865
throw new IllegalStateException("Conflicting/ambiguous property name definitions (implicit name '"
861866
+_name+"'): found multiple explicit names: "
862867
+newNames+", but also implicit accessor: "+node);

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,12 @@ protected JsonSerializer<?> _createSerializer2(SerializerProvider prov,
223223
// Finally: maybe we can still deal with it as an implementation of some basic JDK interface?
224224
if (ser == null) {
225225
ser = findSerializerByAddonType(config, type, beanDesc, staticTyping);
226+
// 18-Sep-2014, tatu: Actually, as per [jackson-databind#539], need to get
227+
// 'unknown' serializer assigned earlier, here, so that it gets properly
228+
// post-processed
229+
if (ser == null) {
230+
ser = prov.getUnknownTypeSerializer(beanDesc.getBeanClass());
231+
}
226232
}
227233
}
228234
}
@@ -403,14 +409,10 @@ protected JsonSerializer<Object> constructBeanSerializer(SerializerProvider prov
403409

404410
JsonSerializer<Object> ser = (JsonSerializer<Object>) builder.build();
405411

406-
/* However, after all modifications: no properties, no serializer
407-
* (note; as per [JACKSON-670], check was moved later on from an earlier location)
408-
*/
409412
if (ser == null) {
410-
/* 27-Nov-2009, tatu: Except that as per [JACKSON-201], we are
411-
* ok with that as long as it has a recognized class annotation
412-
* (which may come from a mix-in too)
413-
*/
413+
// If we get this far, there were no properties found, so no regular BeanSerializer
414+
// would be constructed. But, couple of exceptions.
415+
// First: if there are known annotations, just create 'empty bean' serializer
414416
if (beanDesc.hasKnownClassAnnotations()) {
415417
return builder.createDummy();
416418
}

src/test/java/com/fasterxml/jackson/databind/introspect/TestPropertyConflicts.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.fasterxml.jackson.databind.introspect;
22

3+
import com.fasterxml.jackson.annotation.*;
4+
35
import com.fasterxml.jackson.core.JsonProcessingException;
6+
47
import com.fasterxml.jackson.databind.*;
58

69
/**
@@ -62,6 +65,21 @@ public void _stuff(String value) {
6265
}
6366
}
6467

68+
// For [Issue#541]
69+
static class Bean541 {
70+
protected String str;
71+
72+
@JsonCreator
73+
public Bean541(@JsonProperty("str") String str) {
74+
this.str = str;
75+
}
76+
77+
@JsonProperty("s")
78+
public String getStr() {
79+
return str;
80+
}
81+
}
82+
6583
/*
6684
/**********************************************************
6785
/* Test methods
@@ -110,4 +128,23 @@ public void testInferredNameConflictsWithSetters() throws Exception
110128
Infernal inf = mapper.readValue(aposToQuotes("{'stuff':'Bob'}"), Infernal.class);
111129
assertNotNull(inf);
112130
}
131+
132+
public void testIssue541() throws Exception {
133+
final ObjectMapper mapper = new ObjectMapper();
134+
mapper.disable(
135+
MapperFeature.AUTO_DETECT_CREATORS,
136+
MapperFeature.AUTO_DETECT_FIELDS,
137+
MapperFeature.AUTO_DETECT_GETTERS,
138+
MapperFeature.AUTO_DETECT_IS_GETTERS,
139+
MapperFeature.AUTO_DETECT_SETTERS,
140+
MapperFeature.USE_GETTERS_AS_SETTERS
141+
);
142+
Bean541 data = mapper.readValue("{\"str\":\"the string\"}", Bean541.class);
143+
if (data == null) {
144+
throw new IllegalStateException("data is null");
145+
}
146+
if (!"the string".equals(data.getStr())) {
147+
throw new IllegalStateException("bad value for data.str");
148+
}
149+
}
113150
}

src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ enum Choice { YES, NO; }
3232
* Another enum type, but this time forcing sub-classing
3333
*/
3434
enum ComplexChoice {
35-
MAYBE(true), PROBABLY_NOT(false);
35+
MAYBE(true), PROBABLY_NOT(false);
3636

37-
private boolean state;
37+
private boolean state;
3838

39-
private ComplexChoice(boolean b) { state = b; }
39+
private ComplexChoice(boolean b) { state = b; }
4040

4141
@Override
42-
public String toString() { return String.valueOf(state); }
42+
public String toString() { return String.valueOf(state); }
4343
}
4444

4545
// [JACKSON-311]
@@ -113,6 +113,24 @@ public void testBeanAsObject() throws Exception
113113
assertEquals("abc", ((StringBean) result[0]).name);
114114
}
115115

116+
// with 2.5, another test to check that "as-property" is valid option
117+
public void testBeanAsObjectUsingAsProperty() throws Exception
118+
{
119+
ObjectMapper m = new ObjectMapper();
120+
m.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL,
121+
".hype");
122+
// note: need to wrap, to get declared as Object
123+
String json = m.writeValueAsString(new StringBean("abc"));
124+
125+
System.err.println("JSON == "+json);
126+
127+
// Ok: serialization seems to work as expected. Now deserialize:
128+
Object result = m.readValue(json, Object.class);
129+
assertNotNull(result);
130+
assertEquals(StringBean.class, result.getClass());
131+
assertEquals("abc", ((StringBean) result).name);
132+
}
133+
116134
/**
117135
* Unit test that verifies that an abstract bean is stored with type information
118136
* if default type information is enabled for non-concrete types.
@@ -331,6 +349,18 @@ public void testFeature432() throws Exception
331349
String json = mapper.writeValueAsString(new BeanHolder(new StringBean("punny")));
332350
assertEquals("{\"bean\":{\"*CLASS*\":\"com.fasterxml.jackson.databind.jsontype.TestDefaultForObject$StringBean\",\"name\":\"punny\"}}", json);
333351
}
352+
353+
public void testNoGoWithExternalProperty() throws Exception
354+
{
355+
ObjectMapper mapper = new ObjectMapper();
356+
try {
357+
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT,
358+
JsonTypeInfo.As.EXTERNAL_PROPERTY);
359+
fail("Should not have passed");
360+
} catch (IllegalArgumentException e) {
361+
verifyException(e, "Can not use includeAs of EXTERNAL_PROPERTY");
362+
}
363+
}
334364

335365
/*
336366
/**********************************************************

src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import com.fasterxml.jackson.annotation.*;
77
import com.fasterxml.jackson.core.*;
8-
98
import com.fasterxml.jackson.databind.*;
109
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
1110
import com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder;
@@ -26,17 +25,11 @@
2625
@SuppressWarnings("serial")
2726
public class TestBeanSerializer extends BaseMapTest
2827
{
29-
/*
30-
/********************************************************
31-
/* Helper types
32-
/********************************************************
33-
*/
34-
35-
static class ModuleImpl extends SimpleModule
28+
static class SerializerModifierModule extends SimpleModule
3629
{
3730
protected BeanSerializerModifier modifier;
3831

39-
public ModuleImpl(BeanSerializerModifier modifier)
32+
public SerializerModifierModule(BeanSerializerModifier modifier)
4033
{
4134
super("test", Version.unknownVersion());
4235
this.modifier = modifier;
@@ -178,6 +171,24 @@ public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
178171
}
179172
}
180173

174+
// [Issue#539]: use post-modifier
175+
static class EmptyBeanModifier539 extends BeanSerializerModifier
176+
{
177+
@Override
178+
public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
179+
BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties)
180+
{
181+
System.err.println("DEBUG: changeProperties!");
182+
return beanProperties;
183+
}
184+
185+
@Override
186+
public JsonSerializer<?> modifySerializer(SerializationConfig config,
187+
BeanDescription beanDesc, JsonSerializer<?> serializer) {
188+
System.err.println("DEBUG: modifySer!");
189+
return new BogusBeanSerializer(42);
190+
}
191+
}
181192
// [Issue#120], arrays, collections, maps
182193

183194
static class ArraySerializerModifier extends BeanSerializerModifier {
@@ -251,30 +262,30 @@ enum EnumABC { A, B, C };
251262
public void testPropertyRemoval() throws Exception
252263
{
253264
ObjectMapper mapper = new ObjectMapper();
254-
mapper.registerModule(new ModuleImpl(new RemovingModifier("a")));
265+
mapper.registerModule(new SerializerModifierModule(new RemovingModifier("a")));
255266
Bean bean = new Bean();
256267
assertEquals("{\"b\":\"b\"}", mapper.writeValueAsString(bean));
257268
}
258269

259270
public void testPropertyReorder() throws Exception
260271
{
261272
ObjectMapper mapper = new ObjectMapper();
262-
mapper.registerModule(new ModuleImpl(new ReorderingModifier()));
273+
mapper.registerModule(new SerializerModifierModule(new ReorderingModifier()));
263274
Bean bean = new Bean();
264275
assertEquals("{\"a\":\"a\",\"b\":\"b\"}", mapper.writeValueAsString(bean));
265276
}
266277

267278
public void testBuilderReplacement() throws Exception
268279
{
269280
ObjectMapper mapper = new ObjectMapper();
270-
mapper.registerModule(new ModuleImpl(new BuilderModifier(new BogusBeanSerializer(17))));
281+
mapper.registerModule(new SerializerModifierModule(new BuilderModifier(new BogusBeanSerializer(17))));
271282
Bean bean = new Bean();
272283
assertEquals("17", mapper.writeValueAsString(bean));
273284
}
274285
public void testSerializerReplacement() throws Exception
275286
{
276287
ObjectMapper mapper = new ObjectMapper();
277-
mapper.registerModule(new ModuleImpl(new ReplacingModifier(new BogusBeanSerializer(123))));
288+
mapper.registerModule(new SerializerModifierModule(new ReplacingModifier(new BogusBeanSerializer(123))));
278289
Bean bean = new Bean();
279290
assertEquals("123", mapper.writeValueAsString(bean));
280291
}
@@ -295,6 +306,22 @@ public void setupModule(SetupContext context)
295306
assertEquals("{\"bogus\":\"foo\"}", json);
296307
}
297308

309+
// [Issue#539]
310+
public void testEmptyBean539() throws Exception
311+
{
312+
ObjectMapper mapper = new ObjectMapper();
313+
mapper.registerModule(new SimpleModule("test", Version.unknownVersion()) {
314+
@Override
315+
public void setupModule(SetupContext context)
316+
{
317+
super.setupModule(context);
318+
context.addBeanSerializerModifier(new EmptyBeanModifier539());
319+
}
320+
});
321+
String json = mapper.writeValueAsString(new EmptyBean());
322+
assertEquals("42", json);
323+
}
324+
298325
// [Issue#121]
299326

300327
public void testModifyArraySerializer() throws Exception

0 commit comments

Comments
 (0)