Skip to content

Commit c7c40d9

Browse files
committed
Merge branch '2.9'
2 parents af19d3e + 33e7d89 commit c7c40d9

File tree

8 files changed

+364
-104
lines changed

8 files changed

+364
-104
lines changed

src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java

+28-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
1515
import com.fasterxml.jackson.databind.deser.impl.CreatorCandidate;
1616
import com.fasterxml.jackson.databind.deser.impl.CreatorCollector;
17+
import com.fasterxml.jackson.databind.deser.impl.JavaUtilCollectionsDeserializers;
1718
import com.fasterxml.jackson.databind.deser.std.*;
1819
import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
1920
import com.fasterxml.jackson.databind.ext.jdk8.Jdk8OptionalDeserializer;
@@ -272,7 +273,7 @@ public ValueInstantiator findValueInstantiator(DeserializationContext ctxt,
272273
}
273274
}
274275

275-
// Sanity check: does the chosen instantatior have incomplete creators?
276+
// Sanity check: does the chosen ValueInstantiator have incomplete creators?
276277
if (instantiator.getIncompleteParameter() != null) {
277278
final AnnotatedParameter nonAnnotatedParam = instantiator.getIncompleteParameter();
278279
final AnnotatedWithParams ctor = nonAnnotatedParam.getOwner();
@@ -286,9 +287,23 @@ private ValueInstantiator _findStdValueInstantiator(DeserializationConfig config
286287
BeanDescription beanDesc)
287288
throws JsonMappingException
288289
{
289-
if (beanDesc.getBeanClass() == JsonLocation.class) {
290+
Class<?> raw = beanDesc.getBeanClass();
291+
if (raw == JsonLocation.class) {
290292
return new JsonLocationInstantiator();
291293
}
294+
// [databind#1868]: empty List/Set/Map
295+
if (Collection.class.isAssignableFrom(raw)) {
296+
if (Collections.EMPTY_SET.getClass() == raw) {
297+
return new ConstantValueInstantiator(Collections.EMPTY_SET);
298+
}
299+
if (Collections.EMPTY_LIST.getClass() == raw) {
300+
return new ConstantValueInstantiator(Collections.EMPTY_LIST);
301+
}
302+
} else if (Map.class.isAssignableFrom(raw)) {
303+
if (Collections.EMPTY_MAP.getClass() == raw) {
304+
return new ConstantValueInstantiator(Collections.EMPTY_MAP);
305+
}
306+
}
292307
return null;
293308
}
294309

@@ -1218,6 +1233,11 @@ public JsonDeserializer<?> createCollectionDeserializer(DeserializationContext c
12181233
if (type.hasRawClass(ArrayBlockingQueue.class)) {
12191234
return new ArrayBlockingQueueDeserializer(type, contentDeser, contentTypeDeser, inst);
12201235
}
1236+
// 10-Jan-2017, tatu: `java.util.Collections` types need help:
1237+
deser = JavaUtilCollectionsDeserializers.findForCollection(ctxt, type);
1238+
if (deser != null) {
1239+
return deser;
1240+
}
12211241
}
12221242
// Can use more optimal deserializer if content type is String, so:
12231243
if (contentType.hasRawClass(String.class)) {
@@ -1357,6 +1377,12 @@ public JsonDeserializer<?> createMapDeserializer(DeserializationContext ctxt,
13571377
}
13581378
deser = AbstractDeserializer.constructForNonPOJO(beanDesc);
13591379
}
1380+
} else {
1381+
// 10-Jan-2017, tatu: `java.util.Collections` types need help:
1382+
deser = JavaUtilCollectionsDeserializers.findForMap(ctxt, type);
1383+
if (deser != null) {
1384+
return deser;
1385+
}
13601386
}
13611387
if (deser == null) {
13621388
ValueInstantiator inst = findValueInstantiator(ctxt, beanDesc);

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ public JsonDeserializer<Object> createBeanDeserializer(DeserializationContext ct
104104
if (type.isThrowable()) {
105105
return buildThrowableDeserializer(ctxt, type, beanDesc);
106106
}
107-
/* Or, for abstract types, may have alternate means for resolution
108-
* (defaulting, materialization)
109-
*/
107+
// Or, for abstract types, may have alternate means for resolution
108+
// (defaulting, materialization)
109+
110110
// 29-Nov-2015, tatu: Also, filter out calls to primitive types, they are
111111
// not something we could materialize anything for
112112
if (type.isAbstract() && !type.isPrimitive() && !type.isEnumType()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package com.fasterxml.jackson.databind.deser.impl;
2+
3+
import java.util.*;
4+
5+
import com.fasterxml.jackson.databind.DeserializationContext;
6+
import com.fasterxml.jackson.databind.JavaType;
7+
import com.fasterxml.jackson.databind.JsonDeserializer;
8+
import com.fasterxml.jackson.databind.JsonMappingException;
9+
import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
10+
import com.fasterxml.jackson.databind.type.TypeFactory;
11+
import com.fasterxml.jackson.databind.util.Converter;
12+
13+
/**
14+
* Helper class used to contain logic for deserializing "special" containers
15+
* from {@code java.util.Collections} and {@code java.util.Arrays}. This is needed
16+
* because they do not have usable no-arguments constructor: however, are easy enough
17+
* to deserialize using delegating deserializer.
18+
*
19+
* @since 2.9.4
20+
*/
21+
public abstract class JavaUtilCollectionsDeserializers
22+
{
23+
private final static int TYPE_SINGLETON_SET = 1;
24+
private final static int TYPE_SINGLETON_LIST = 2;
25+
private final static int TYPE_SINGLETON_MAP = 3;
26+
27+
private final static int TYPE_UNMODIFIABLE_SET = 4;
28+
private final static int TYPE_UNMODIFIABLE_LIST = 5;
29+
private final static int TYPE_UNMODIFIABLE_MAP = 6;
30+
31+
public final static int TYPE_AS_LIST = 7;
32+
33+
// 10-Jan-2018, tatu: There are a few "well-known" special containers in JDK too:
34+
35+
private final static Class<?> CLASS_AS_ARRAYS_LIST = Arrays.asList(null, null).getClass();
36+
37+
private final static Class<?> CLASS_SINGLETON_SET = Collections.singleton(Boolean.TRUE).getClass();
38+
private final static Class<?> CLASS_SINGLETON_LIST = Collections.singletonList(Boolean.TRUE).getClass();
39+
private final static Class<?> CLASS_SINGLETON_MAP = Collections.singletonMap("a", "b").getClass();
40+
41+
public static JsonDeserializer<?> findForCollection(DeserializationContext ctxt,
42+
JavaType type)
43+
throws JsonMappingException
44+
{
45+
JavaUtilCollectionsConverter conv;
46+
47+
// 10-Jan-2017, tatu: Some types from `java.util.Collections`/`java.util.Arrays` need bit of help...
48+
if (type.hasRawClass(CLASS_AS_ARRAYS_LIST)) {
49+
conv = converter(TYPE_AS_LIST, type);
50+
} else if (type.hasRawClass(CLASS_SINGLETON_LIST)) {
51+
conv = converter(TYPE_SINGLETON_LIST, type);
52+
} else if (type.hasRawClass(CLASS_SINGLETON_SET)) {
53+
conv = converter(TYPE_SINGLETON_SET, type);
54+
} else {
55+
return null;
56+
}
57+
return new StdDelegatingDeserializer<Object>(conv);
58+
}
59+
60+
public static JsonDeserializer<?> findForMap(DeserializationContext ctxt,
61+
JavaType type)
62+
throws JsonMappingException
63+
{
64+
JavaUtilCollectionsConverter conv;
65+
66+
// 10-Jan-2017, tatu: Some types from `java.util.Collections`/`java.util.Arrays` need bit of help...
67+
if (type.hasRawClass(CLASS_SINGLETON_MAP)) {
68+
conv = converter(TYPE_SINGLETON_MAP, type);
69+
} else {
70+
return null;
71+
}
72+
return new StdDelegatingDeserializer<Object>(conv);
73+
}
74+
75+
static JavaUtilCollectionsConverter converter(int kind, JavaType concreteType)
76+
{
77+
JavaType inputType;
78+
79+
switch (kind) {
80+
case TYPE_SINGLETON_SET:
81+
case TYPE_UNMODIFIABLE_SET:
82+
inputType = concreteType.findSuperType(Set.class);
83+
break;
84+
85+
case TYPE_SINGLETON_MAP:
86+
case TYPE_UNMODIFIABLE_MAP:
87+
inputType = concreteType.findSuperType(Map.class);
88+
break;
89+
90+
case TYPE_SINGLETON_LIST:
91+
case TYPE_UNMODIFIABLE_LIST:
92+
case TYPE_AS_LIST:
93+
default:
94+
inputType = concreteType.findSuperType(List.class);
95+
break;
96+
}
97+
return new JavaUtilCollectionsConverter(kind, inputType);
98+
}
99+
100+
/**
101+
* Implementation used for converting from various generic container
102+
* types ({@link java.util.Set}, {@link java.util.List}, {@link java.util.Map})
103+
* into more specific implementations accessible via {@code java.util.Collections}.
104+
*/
105+
private static class JavaUtilCollectionsConverter implements Converter<Object,Object>
106+
{
107+
private final JavaType _inputType;
108+
109+
private final int _kind;
110+
111+
private JavaUtilCollectionsConverter(int kind, JavaType inputType) {
112+
_inputType = inputType;
113+
_kind = kind;
114+
}
115+
116+
@Override
117+
public Object convert(Object value) {
118+
if (value == null) { // is this legal to get?
119+
return null;
120+
}
121+
122+
switch (_kind) {
123+
case TYPE_SINGLETON_SET:
124+
{
125+
Set<?> set = (Set<?>) value;
126+
_checkSingleton(set.size());
127+
return Collections.singleton(set.iterator().next());
128+
}
129+
case TYPE_SINGLETON_LIST:
130+
{
131+
List<?> list = (List<?>) value;
132+
_checkSingleton(list.size());
133+
return Collections.singletonList(list.get(0));
134+
}
135+
case TYPE_SINGLETON_MAP:
136+
{
137+
Map<?,?> map = (Map<?,?>) value;
138+
_checkSingleton(map.size());
139+
Map.Entry<?,?> entry = map.entrySet().iterator().next();
140+
return Collections.singletonMap(entry.getKey(), entry.getValue());
141+
}
142+
143+
case TYPE_UNMODIFIABLE_SET:
144+
return Collections.unmodifiableSet((Set<?>) value);
145+
case TYPE_UNMODIFIABLE_LIST:
146+
return Collections.unmodifiableList((List<?>) value);
147+
case TYPE_UNMODIFIABLE_MAP:
148+
return Collections.unmodifiableMap((Map<?,?>) value);
149+
150+
case TYPE_AS_LIST:
151+
default:
152+
// Here we do not actually care about impl type, just return List as-is:
153+
return value;
154+
}
155+
}
156+
157+
@Override
158+
public JavaType getInputType(TypeFactory typeFactory) {
159+
return _inputType;
160+
}
161+
162+
@Override
163+
public JavaType getOutputType(TypeFactory typeFactory) {
164+
// we don't actually care, so:
165+
return _inputType;
166+
}
167+
168+
private void _checkSingleton(int size) {
169+
if (size != 1) {
170+
// not the best error ever but... has to do
171+
throw new IllegalArgumentException("Can not deserialize Singleton container from "+size+" entries");
172+
}
173+
}
174+
}
175+
176+
}

src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public void resolve(DeserializationContext ctxt)
121121
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
122122
throws JsonMappingException
123123
{
124-
// First: if already got serializer to delegate to, contextualize it:
124+
// First: if already got deserializer to delegate to, contextualize it:
125125
if (_delegateDeserializer != null) {
126126
JsonDeserializer<?> deser = ctxt.handleSecondaryContextualization(_delegateDeserializer,
127127
property, _delegateType);

src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java

+2-25
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,9 @@ protected String _idFrom(Object value, Class<?> cls, TypeFactory typeFactory)
8888
Class<?> valueClass = Object.class;
8989
// not optimal: but EnumMap is not a customizable type so this is sort of ok
9090
str = typeFactory.constructMapType(EnumMap.class, enumClass, valueClass).toCanonical();
91-
} else {
92-
// 17-Feb-2010, tatus: Another such case: result of Arrays.asList() is
93-
// named like so in Sun JDK... Let's just plain old ArrayList in its place.
94-
// ... also, other similar cases exist...
95-
String suffix = str.substring(JAVA_UTIL_PKG.length());
96-
if (isJavaUtilCollectionClass(suffix, "List")) {
97-
str = ArrayList.class.getName();
98-
} else if (isJavaUtilCollectionClass(suffix, "Map")){
99-
str = HashMap.class.getName();
100-
} else if (isJavaUtilCollectionClass(suffix, "Set")){
101-
str = HashSet.class.getName();
102-
}
10391
}
92+
// 10-Jan-2018, tatu: Up until 2.9.4 we used to have other conversions for `Collections.xxx()`
93+
// and `Arrays.asList(...)`; but it was changed to be handled on receiving end instead
10494
} else if (str.indexOf('$') >= 0) {
10595
/* Other special handling may be needed for inner classes,
10696
* The best way to handle would be to find 'hidden' constructor; pass parent
@@ -128,17 +118,4 @@ protected String _idFrom(Object value, Class<?> cls, TypeFactory typeFactory)
128118
public String getDescForKnownTypeIds() {
129119
return "class name used as type id";
130120
}
131-
132-
private static boolean isJavaUtilCollectionClass(String clz, String type)
133-
{
134-
if (clz.startsWith("Collections$")) {
135-
// 02-Jan-2017, tatu: As per [databind#1868], may need to leave Unmodifiable variants as is
136-
return (clz.indexOf(type) > 0)
137-
&& !clz.contains("Unmodifiable");
138-
}
139-
if (clz.startsWith("Arrays$")) {
140-
return (clz.indexOf(type) > 0);
141-
}
142-
return false;
143-
}
144121
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.fasterxml.jackson.databind.util;
2+
3+
import java.io.IOException;
4+
5+
import com.fasterxml.jackson.databind.DeserializationContext;
6+
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
7+
8+
/**
9+
* Trivial {@link ValueInstantiator} implementation that will simply return constant
10+
* {@code Object} it is configured with. May be used as-is, or as base class to override
11+
* simplistic behavior further.
12+
*
13+
* @since 2.9.4
14+
*/
15+
public class ConstantValueInstantiator extends ValueInstantiator
16+
{
17+
protected final Object _value;
18+
19+
public ConstantValueInstantiator(Object value) {
20+
_value = value;
21+
}
22+
23+
@Override
24+
public Class<?> getValueClass() {
25+
return _value.getClass();
26+
}
27+
28+
@Override // yes, since default ctor works
29+
public boolean canInstantiate() { return true; }
30+
31+
@Override
32+
public boolean canCreateUsingDefault() { return true; }
33+
34+
@Override
35+
public Object createUsingDefault(DeserializationContext ctxt) throws IOException {
36+
return _value;
37+
}
38+
}

0 commit comments

Comments
 (0)