Skip to content

Commit

Permalink
Fixed #700 (cannot change default Map, List)
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Feb 12, 2015
1 parent d22b735 commit 7fee92e
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 26 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project: jackson-databind
#649: Make `BeanDeserializer` use new `parser.nextFieldName()` and `.hasTokenId()` methods
#696: Copy constructor does not preserve `_injectableValues`
(reported by Charles A)
#700: Cannot Change Default Abstract Type Mapper from LinkedHashMap
(reported by wealdtech@github)

2.5.2 (not yet released)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public abstract class DeserializationContext
extends DatabindContext
implements java.io.Serializable
{
private static final long serialVersionUID = -4290063686213707727L;
private static final long serialVersionUID = 1L; // 2.6

/**
* Let's limit length of error messages, for cases where underlying data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,24 +178,21 @@ public final DeserializerFactory withValueInstantiators(ValueInstantiators insta

/*
/**********************************************************
/* JsonDeserializerFactory impl (partial): type mappings
/* DeserializerFactory impl (partial): type mappings
/**********************************************************
*/

@Override
public JavaType mapAbstractType(DeserializationConfig config, JavaType type)
throws JsonMappingException
public JavaType mapAbstractType(DeserializationConfig config, JavaType type) throws JsonMappingException
{
// first, general mappings
while (true) {
JavaType next = _mapAbstractType2(config, type);
if (next == null) {
return type;
}
/* Should not have to worry about cycles; but better verify since they will invariably
* occur... :-)
* (also: guard against invalid resolution to a non-related type)
*/
// Should not have to worry about cycles; but better verify since they will invariably occur... :-)
// (also: guard against invalid resolution to a non-related type)
Class<?> prevCls = type.getRawClass();
Class<?> nextCls = next.getRawClass();
if ((prevCls == nextCls) || !prevCls.isAssignableFrom(nextCls)) {
Expand Down Expand Up @@ -1437,7 +1434,17 @@ public JsonDeserializer<?> findDefaultDeserializer(DeserializationContext ctxt,
Class<?> rawType = type.getRawClass();
// Object ("untyped"), String equivalents:
if (rawType == CLASS_OBJECT) {
return new UntypedObjectDeserializer();
// 11-Feb-2015, tatu: As per [databind#700] need to be careful wrt non-default Map, List.
DeserializationConfig config = ctxt.getConfig();
JavaType lt, mt;

if (_factoryConfig.hasAbstractTypeResolvers()) {
lt = _findRemappedType(config, List.class);
mt = _findRemappedType(config, Map.class);
} else {
lt = mt = null;
}
return new UntypedObjectDeserializer(lt, mt);
}
if (rawType == CLASS_STRING || rawType == CLASS_CHAR_BUFFER) {
return StringDeserializer.instance;
Expand Down Expand Up @@ -1507,6 +1514,11 @@ public JsonDeserializer<?> findDefaultDeserializer(DeserializationContext ctxt,
return JdkDeserializers.find(rawType, clsName);
}

protected JavaType _findRemappedType(DeserializationConfig config, Class<?> rawType) throws JsonMappingException {
JavaType type = mapAbstractType(config, config.constructType(rawType));
return (type == null || type.hasRawClass(rawType)) ? null : type;
}

/*
/**********************************************************
/* Helper methods, finding custom deserializers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class UntypedObjectDeserializer
* @deprecated Since 2.3, construct a new instance, needs to be resolved
*/
@Deprecated
public final static UntypedObjectDeserializer instance = new UntypedObjectDeserializer();
public final static UntypedObjectDeserializer instance = new UntypedObjectDeserializer(null, null);

/*
/**********************************************************
Expand All @@ -56,9 +56,35 @@ public class UntypedObjectDeserializer
protected JsonDeserializer<Object> _stringDeserializer;

protected JsonDeserializer<Object> _numberDeserializer;


/**
* If {@link java.util.List} has been mapped to non-default implementation,
* we'll store type here
*
* @since 2.6
*/
protected JavaType _listType;

/**
* If {@link java.util.Map} has been mapped to non-default implementation,
* we'll store type here
*
* @since 2.6
*/
protected JavaType _mapType;

/**
* @deprecated Since 2.6 use variant takes type arguments
*/
@Deprecated
public UntypedObjectDeserializer() {
this(null, null);
}

public UntypedObjectDeserializer(JavaType listType, JavaType mapType) {
super(Object.class);
_listType = listType;
_mapType = mapType;
}

@SuppressWarnings("unchecked")
Expand All @@ -71,6 +97,8 @@ public UntypedObjectDeserializer(UntypedObjectDeserializer base,
_listDeserializer = (JsonDeserializer<Object>) listDeser;
_stringDeserializer = (JsonDeserializer<Object>) stringDeser;
_numberDeserializer = (JsonDeserializer<Object>) numberDeser;
_listType = base._listType;
_mapType = base._mapType;
}

/*
Expand Down Expand Up @@ -102,8 +130,16 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException
*/

// So: first find possible custom instances
_mapDeserializer = _findCustomDeser(ctxt, tf.constructMapType(Map.class, stringType, obType));
_listDeserializer = _findCustomDeser(ctxt, tf.constructCollectionType(List.class, obType));
if (_listType == null) {
_listDeserializer = _clearIfStdImpl(_findCustomDeser(ctxt, tf.constructCollectionType(List.class, obType)));
} else {
_listDeserializer = _findCustomDeser(ctxt, _listType);
}
if (_mapType == null) {
_mapDeserializer = _clearIfStdImpl(_findCustomDeser(ctxt, tf.constructMapType(Map.class, stringType, obType)));
} else {
_mapDeserializer = _findCustomDeser(ctxt, _mapType);
}
_stringDeserializer = _findCustomDeser(ctxt, stringType);
_numberDeserializer = _findCustomDeser(ctxt, tf.constructType(Number.class));

Expand All @@ -116,17 +152,16 @@ public void resolve(DeserializationContext ctxt) throws JsonMappingException
_numberDeserializer = (JsonDeserializer<Object>) ctxt.handleSecondaryContextualization(_numberDeserializer, null, unknown);
}

@SuppressWarnings("unchecked")
protected JsonDeserializer<Object> _findCustomDeser(DeserializationContext ctxt, JavaType type)
throws JsonMappingException
{
// Since we are calling from `resolve`, we should NOT try to contextualize yet;
// contextualization will only occur at a later point
JsonDeserializer<?> deser = ctxt.findNonContextualValueDeserializer(type);
if (ClassUtil.isJacksonStdImpl(deser)) {
return null;
}
return (JsonDeserializer<Object>) deser;
return ctxt.findNonContextualValueDeserializer(type);
}

protected JsonDeserializer<Object> _clearIfStdImpl(JsonDeserializer<Object> deser) {
return ClassUtil.isJacksonStdImpl(deser) ? null : deser;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.util.*;


import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand Down Expand Up @@ -65,22 +64,34 @@ public void testMapDefaultingBasic() throws Exception
assertEquals(TreeMap.class, result.getClass());
}

/* 11-Feb-2015, tatu: too tricky to fix in 2.5.x; move to 2.6
// [databind#700]
public void testMapDefaultingRecursive() throws Exception
public void testDefaultingRecursive() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
// default is HashMap, so:

// defaults: LinkedHashMap, ArrayList
mod.addAbstractTypeMapping(Map.class, TreeMap.class);
mod.addAbstractTypeMapping(List.class, LinkedList.class);

mapper.registerModule(mod);
Object result = mapper.readValue("[ {} ]", Object.class);
assertEquals(ArrayList.class, result.getClass());
Object result;

result = mapper.readValue("[ {} ]", Object.class);
assertEquals(LinkedList.class, result.getClass());
Object v = ((List<?>) result).get(0);
assertNotNull(v);
assertEquals(TreeMap.class, v.getClass());

result = mapper.readValue("{ \"x\": [ 3 ] }", Object.class);
assertEquals(TreeMap.class, result.getClass());
Map<?,?> map = (Map<?,?>) result;
assertEquals(1, map.size());
v = map.get("x");
assertNotNull(v);
assertEquals(LinkedList.class, v.getClass());
assertEquals(1, ((List<?>) v).size());
}
*/

public void testInterfaceDefaulting() throws Exception
{
Expand Down

0 comments on commit 7fee92e

Please sign in to comment.