Skip to content

Commit 914f643

Browse files
committed
Fix #756
1 parent 6f60dd9 commit 914f643

File tree

6 files changed

+264
-13
lines changed

6 files changed

+264
-13
lines changed

Diff for: release-notes/VERSION

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Project: jackson-databind
2222
#737: Add support for writing raw values in TokenBuffer
2323
(suggested by Guillaume S, gsmet@github)
2424
#743: Add `RawValue` helper type, for piping raw values through `TokenBuffer`
25+
#756: Disabling SerializationFeature.FAIL_ON_EMPTY_BEANS does not affect `canSerialize()`
26+
(reported by nickwongdev@github)
2527
#762: Add `ObjectWriter.withoutRootName()`, `ObjectReader.withoutRootName()`
2628
#765: `SimpleType.withStaticTyping()` impl incorrect
2729
- Remove old cglib compatibility tests; cause problems in Eclipse

Diff for: src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java

+23-2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ public abstract class SerializerProvider
4848
new FailingSerializer("Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)");
4949

5050
/**
51+
* Placeholder serializer used when <code>java.lang.Object</code> typed property
52+
* is marked to be serialized.
53+
*<br />
54+
* NOTE: starting with 2.6, this instance is NOT used for any other types, and
55+
* separate instances are constructed for "empty" Beans.
56+
*<p>
5157
* NOTE: changed to <code>protected</code> for 2.3; no need to be publicly available.
5258
*/
5359
protected final static JsonSerializer<Object> DEFAULT_UNKNOWN_SERIALIZER = new UnknownSerializer();
@@ -816,7 +822,12 @@ public JsonSerializer<Object> findNullValueSerializer(BeanProperty property)
816822
* @param unknownType Type for which no serializer is found
817823
*/
818824
public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> unknownType) {
819-
return _unknownTypeSerializer;
825+
// 23-Apr-2015, tatu: Only return shared instance if nominal type is Object.class
826+
if (unknownType == Object.class) {
827+
return _unknownTypeSerializer;
828+
}
829+
// otherwise construct explicit instance with property handled type
830+
return new UnknownSerializer(unknownType);
820831
}
821832

822833
/**
@@ -827,7 +838,17 @@ public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> unknownType) {
827838
* @since 2.5
828839
*/
829840
public boolean isUnknownTypeSerializer(JsonSerializer<?> ser) {
830-
return (ser == _unknownTypeSerializer) || (ser == null);
841+
if ((ser == _unknownTypeSerializer) || (ser == null)) {
842+
return true;
843+
}
844+
// 23-Apr-2015, tatu: "empty" serializer is trickier; needs to consider
845+
// error handling
846+
if (isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
847+
if (ser.getClass() == UnknownSerializer.class) {
848+
return true;
849+
}
850+
}
851+
return false;
831852
}
832853

833854
/*

Diff for: src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java

+20-10
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,42 @@ public class UnknownSerializer
1616
public UnknownSerializer() {
1717
super(Object.class);
1818
}
19+
20+
/**
21+
* @since 2.6
22+
*/
23+
public UnknownSerializer(Class<?> cls) {
24+
super(cls, false);
25+
}
1926

2027
@Override
21-
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
22-
throws IOException, JsonMappingException
28+
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException
2329
{
2430
// 27-Nov-2009, tatu: As per [JACKSON-201] may or may not fail...
2531
if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
2632
failForEmpty(value);
2733
}
2834
// But if it's fine, we'll just output empty JSON Object:
29-
jgen.writeStartObject();
30-
jgen.writeEndObject();
35+
gen.writeStartObject();
36+
gen.writeEndObject();
3137
}
3238

3339
@Override
34-
public final void serializeWithType(Object value, JsonGenerator jgen, SerializerProvider provider,
35-
TypeSerializer typeSer)
36-
throws IOException, JsonGenerationException
40+
public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
41+
TypeSerializer typeSer) throws IOException
3742
{
3843
if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
3944
failForEmpty(value);
4045
}
41-
typeSer.writeTypePrefixForObject(value, jgen);
42-
typeSer.writeTypeSuffixForObject(value, jgen);
46+
typeSer.writeTypePrefixForObject(value, gen);
47+
typeSer.writeTypeSuffixForObject(value, gen);
4348
}
44-
49+
50+
@Override
51+
public boolean isEmpty(SerializerProvider provider, Object value) {
52+
return true;
53+
}
54+
4555
@Override
4656
public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
4757
return null;

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ protected StdSerializer(Class<?> t, boolean dummy) {
7575
*/
7676

7777
@Override
78-
public abstract void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
78+
public abstract void serialize(T value, JsonGenerator gen, SerializerProvider provider)
7979
throws IOException, JsonGenerationException;
8080

8181
/*

Diff for: src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java

+15
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ static class Bean {
1919
public void setX(int v) { value = v; }
2020
}
2121

22+
static class EmptyBean { }
23+
2224
// for [Issue#206]
2325
@SuppressWarnings("serial")
2426
static class CustomMapper extends ObjectMapper {
@@ -200,4 +202,17 @@ public void testNonSerializabilityOfObject()
200202
// but this used to pass, incorrectly
201203
assertFalse(m.canSerialize(Object.class));
202204
}
205+
206+
// for [databind#756]
207+
public void testEmptyBeanSerializability()
208+
{
209+
// with default settings, error
210+
/*
211+
assertFalse(MAPPER.writer().with(SerializationFeature.FAIL_ON_EMPTY_BEANS)
212+
.canSerialize(EmptyBean.class));
213+
*/
214+
// but with changes
215+
assertTrue(MAPPER.writer().without(SerializationFeature.FAIL_ON_EMPTY_BEANS)
216+
.canSerialize(EmptyBean.class));
217+
}
203218
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package com.fasterxml.jackson.databind;
2+
3+
import java.io.*;
4+
import java.util.*;
5+
6+
import com.fasterxml.jackson.core.JsonFactory;
7+
import com.fasterxml.jackson.core.JsonParser;
8+
import com.fasterxml.jackson.databind.ObjectMapper;
9+
import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
10+
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
11+
import com.fasterxml.jackson.databind.node.*;
12+
import com.fasterxml.jackson.databind.type.TypeFactory;
13+
14+
public class ObjectMapperTest extends BaseMapTest
15+
{
16+
static class Bean {
17+
int value = 3;
18+
19+
public void setX(int v) { value = v; }
20+
}
21+
22+
// for [Issue#206]
23+
@SuppressWarnings("serial")
24+
static class CustomMapper extends ObjectMapper {
25+
@Override
26+
protected DefaultDeserializationContext createDeserializationContext(JsonParser jp,
27+
DeserializationConfig cfg) {
28+
return super.createDeserializationContext(jp, cfg);
29+
}
30+
}
31+
32+
@SuppressWarnings("serial")
33+
static class MyAnnotationIntrospector extends JacksonAnnotationIntrospector { }
34+
35+
/*
36+
/**********************************************************
37+
/* Test methods
38+
/**********************************************************
39+
*/
40+
41+
final static ObjectMapper MAPPER = new ObjectMapper();
42+
43+
public void testProps()
44+
{
45+
ObjectMapper m = new ObjectMapper();
46+
// should have default factory
47+
assertNotNull(m.getNodeFactory());
48+
JsonNodeFactory nf = JsonNodeFactory.instance;
49+
m.setNodeFactory(nf);
50+
assertNull(m.getInjectableValues());
51+
assertSame(nf, m.getNodeFactory());
52+
}
53+
54+
public void testSupport()
55+
{
56+
assertTrue(MAPPER.canSerialize(String.class));
57+
assertTrue(MAPPER.canDeserialize(TypeFactory.defaultInstance().constructType(String.class)));
58+
}
59+
60+
public void testTreeRead() throws Exception
61+
{
62+
String JSON = "{ }";
63+
JsonNode n = MAPPER.readTree(JSON);
64+
assertTrue(n instanceof ObjectNode);
65+
66+
n = MAPPER.readTree(new StringReader(JSON));
67+
assertTrue(n instanceof ObjectNode);
68+
69+
n = MAPPER.readTree(new ByteArrayInputStream(JSON.getBytes("UTF-8")));
70+
assertTrue(n instanceof ObjectNode);
71+
}
72+
73+
// Test to ensure that we can check property ordering defaults...
74+
public void testConfigForPropertySorting() throws Exception
75+
{
76+
ObjectMapper m = new ObjectMapper();
77+
78+
// sort-alphabetically is disabled by default:
79+
assertFalse(m.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
80+
SerializationConfig sc = m.getSerializationConfig();
81+
assertFalse(sc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
82+
assertFalse(sc.shouldSortPropertiesAlphabetically());
83+
DeserializationConfig dc = m.getDeserializationConfig();
84+
assertFalse(dc.shouldSortPropertiesAlphabetically());
85+
86+
// but when enabled, should be visible:
87+
m.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
88+
sc = m.getSerializationConfig();
89+
assertTrue(sc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
90+
assertTrue(sc.shouldSortPropertiesAlphabetically());
91+
dc = m.getDeserializationConfig();
92+
// and not just via SerializationConfig, but also via DeserializationConfig
93+
assertTrue(dc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
94+
assertTrue(dc.shouldSortPropertiesAlphabetically());
95+
}
96+
97+
98+
public void testJsonFactoryLinkage()
99+
{
100+
// first, implicit factory, giving implicit linkage
101+
assertSame(MAPPER, MAPPER.getFactory().getCodec());
102+
103+
// and then explicit factory, which should also be implicitly linked
104+
JsonFactory f = new JsonFactory();
105+
ObjectMapper m = new ObjectMapper(f);
106+
assertSame(f, m.getFactory());
107+
assertSame(m, f.getCodec());
108+
}
109+
110+
/**
111+
* Test for verifying working of [JACKSON-191]
112+
*/
113+
public void testProviderConfig() throws Exception
114+
{
115+
ObjectMapper m = new ObjectMapper();
116+
final String JSON = "{ \"x\" : 3 }";
117+
118+
assertEquals(0, m._deserializationContext._cache.cachedDeserializersCount());
119+
// and then should get one constructed for:
120+
Bean bean = m.readValue(JSON, Bean.class);
121+
assertNotNull(bean);
122+
// Since 2.6, serializer for int also cached:
123+
assertEquals(2, m._deserializationContext._cache.cachedDeserializersCount());
124+
m._deserializationContext._cache.flushCachedDeserializers();
125+
assertEquals(0, m._deserializationContext._cache.cachedDeserializersCount());
126+
127+
// 07-Nov-2014, tatu: As per [databind#604] verify that Maps also get cached
128+
m = new ObjectMapper();
129+
List<?> stuff = m.readValue("[ ]", List.class);
130+
assertNotNull(stuff);
131+
// may look odd, but due to "Untyped" deserializer thing, we actually have
132+
// 4 deserializers (int, List<?>, Map<?,?>, Object)
133+
assertEquals(4, m._deserializationContext._cache.cachedDeserializersCount());
134+
}
135+
136+
// [Issue#28]: ObjectMapper.copy()
137+
public void testCopy() throws Exception
138+
{
139+
ObjectMapper m = new ObjectMapper();
140+
assertTrue(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
141+
m.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
142+
assertFalse(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
143+
InjectableValues inj = new InjectableValues.Std();
144+
m.setInjectableValues(inj);
145+
146+
// // First: verify that handling of features is decoupled:
147+
148+
ObjectMapper m2 = m.copy();
149+
assertFalse(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
150+
m2.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
151+
assertTrue(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
152+
assertSame(inj, m2.getInjectableValues());
153+
154+
// but should NOT change the original
155+
assertFalse(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
156+
157+
// nor vice versa:
158+
assertFalse(m.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
159+
assertFalse(m2.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
160+
m.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
161+
assertTrue(m.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
162+
assertFalse(m2.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
163+
164+
// // Also, underlying JsonFactory instances should be distinct
165+
166+
assertNotSame(m.getFactory(), m2.getFactory());
167+
168+
// [Issue#122]: Need to ensure mix-ins are not shared
169+
assertEquals(0, m.getSerializationConfig().mixInCount());
170+
assertEquals(0, m2.getSerializationConfig().mixInCount());
171+
assertEquals(0, m.getDeserializationConfig().mixInCount());
172+
assertEquals(0, m2.getDeserializationConfig().mixInCount());
173+
174+
m.addMixIn(String.class, Integer.class);
175+
assertEquals(1, m.getSerializationConfig().mixInCount());
176+
assertEquals(0, m2.getSerializationConfig().mixInCount());
177+
assertEquals(1, m.getDeserializationConfig().mixInCount());
178+
assertEquals(0, m2.getDeserializationConfig().mixInCount());
179+
}
180+
181+
public void testAnnotationIntrospectorCopyin()
182+
{
183+
ObjectMapper m = new ObjectMapper();
184+
m.setAnnotationIntrospector(new MyAnnotationIntrospector());
185+
assertEquals(MyAnnotationIntrospector.class,
186+
m.getDeserializationConfig().getAnnotationIntrospector().getClass());
187+
ObjectMapper m2 = m.copy();
188+
189+
assertEquals(MyAnnotationIntrospector.class,
190+
m2.getDeserializationConfig().getAnnotationIntrospector().getClass());
191+
assertEquals(MyAnnotationIntrospector.class,
192+
m2.getSerializationConfig().getAnnotationIntrospector().getClass());
193+
}
194+
195+
// For [databind#703]
196+
public void testNonSerializabilityOfObject()
197+
{
198+
ObjectMapper m = new ObjectMapper();
199+
assertFalse(m.canSerialize(Object.class));
200+
// but this used to pass, incorrectly
201+
assertFalse(m.canSerialize(Object.class));
202+
}
203+
}

0 commit comments

Comments
 (0)