public JsonDeserializer<?> createMapDeserializer(DeserializationConfig config, MapType type, DeserializerProvider p)
throws JsonMappingException
{
Class<?> mapClass = type.getRawClass();
BasicBeanDescription beanDesc = config.introspectForCreation(type);
// Explicit deserializer to use? (@JsonDeserialize.using)
JsonDeserializer<Object> deser = findDeserializerFromAnnotation(config, beanDesc.getClassInfo());
if (deser != null) {
return deser;
}
// If not, any type modifiers? (@JsonDeserialize.as)
type = modifyTypeByAnnotation(config, beanDesc.getClassInfo(), type, null);
JavaType keyType = type.getKeyType();
JavaType contentType = type.getContentType();
// First: is there annotation-specified deserializer for values?
@SuppressWarnings("unchecked")
JsonDeserializer<Object> contentDeser = (JsonDeserializer<Object>) contentType.getValueHandler();
if (contentDeser == null) { // nope...
// 'null' -> maps have no referring fields
contentDeser = p.findValueDeserializer(config, contentType, type, null);
}
/* Value handling is identical for all,
* but EnumMap requires special handling for keys
*/
if (EnumMap.class.isAssignableFrom(mapClass)) {
Class<?> kt = keyType.getRawClass();
if (kt == null || !kt.isEnum()) {
throw new IllegalArgumentException("Can not construct EnumMap; generic (key) type not available");
}
return new EnumMapDeserializer(constructEnumResolver(kt, config), contentDeser);
}
// Otherwise, generic handler works ok.
// Ok: need a key deserializer (null indicates 'default' here)
KeyDeserializer keyDes = (KeyDeserializer) keyType.getValueHandler();
if (keyDes == null) {
keyDes = (TYPE_STRING.equals(keyType)) ? null : p.findKeyDeserializer(config, keyType);
}
/* But there is one more twist: if we are being asked to instantiate
* an interface or abstract Map, we need to either find something
* that implements the thing, or give up.
*
* Note that we do NOT try to guess based on secondary interfaces
* here; that would probably not work correctly since casts would
* fail later on (as the primary type is not the interface we'd
* be implementing)
*/
if (type.isInterface() || type.isAbstract()) {
@SuppressWarnings("unchecked")
Class<? extends Map> fallback = _mapFallbacks.get(mapClass.getName());
if (fallback == null) {
throw new IllegalArgumentException("Can not find a deserializer for non-concrete Map type "+type);
}
mapClass = fallback;
type = (MapType) type.forcedNarrowBy(mapClass);
// But if so, also need to re-check creators...
beanDesc = config.introspectForCreation(type);
}
// [JACKSON-153]: allow use of @JsonCreator
boolean fixAccess = config.isEnabled(DeserializationConfig.Feature.CAN_OVERRIDE_ACCESS_MODIFIERS);
// First, locate the default constructor (if one available)
@SuppressWarnings("unchecked")
Constructor<Map<Object,Object>> defaultCtor = (Constructor<Map<Object,Object>>) beanDesc.findDefaultConstructor();
if (defaultCtor != null) {
if (fixAccess) {
ClassUtil.checkAndFixAccess(defaultCtor);
}
}
// Then optional type info (1.5); either attached to type, or resolve separately:
TypeDeserializer contentTypeDeser = contentType.getTypeHandler();
// but if not, may still be possible to find:
if (contentTypeDeser == null) {
contentTypeDeser = findTypeDeserializer(config, contentType);
}
MapDeserializer md = new MapDeserializer(type, defaultCtor, keyDes, contentDeser, contentTypeDeser);
md.setIgnorableProperties(config.getAnnotationIntrospector().findPropertiesToIgnore(beanDesc.getClassInfo()));
md.setCreators(findMapCreators(config, beanDesc));
return md;
}