com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jackson-databind Show documentation
Show all versions of jackson-databind Show documentation
General data-binding functionality for Jackson: works on core streaming API
package com.fasterxml.jackson.databind.jsontype.impl;
import java.io.IOException;
import java.util.*;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.ClassUtil;
/**
* {@link com.fasterxml.jackson.databind.jsontype.TypeIdResolver} implementation
* that converts between fully-qualified
* Java class names and (JSON) Strings.
*/
public class ClassNameIdResolver
extends TypeIdResolverBase
{
private final static String JAVA_UTIL_PKG = "java.util.";
public ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory) {
super(baseType, typeFactory);
}
@Override
public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.CLASS; }
public void registerSubtype(Class type, String name) {
// not used with class name - based resolvers
}
@Override
public String idFromValue(Object value) {
return _idFrom(value, value.getClass(), _typeFactory);
}
@Override
public String idFromValueAndType(Object value, Class type) {
return _idFrom(value, type, _typeFactory);
}
@Override
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
return _typeFromId(id, context);
}
protected JavaType _typeFromId(String id, DatabindContext ctxt) throws IOException
{
/* 30-Jan-2010, tatu: Most ids are basic class names; so let's first
* check if any generics info is added; and only then ask factory
* to do translation when necessary
*/
TypeFactory tf = ctxt.getTypeFactory();
if (id.indexOf('<') > 0) {
// note: may want to try combining with specialization (esp for EnumMap)?
// 17-Aug-2017, tatu: As per [databind#1735] need to ensure assignment
// compatibility -- needed later anyway, and not doing so may open
// security issues.
JavaType t = tf.constructFromCanonical(id);
if (!t.isTypeOrSubTypeOf(_baseType.getRawClass())) {
// Probably cleaner to have a method in `TypeFactory` but can't add in patch
throw new IllegalArgumentException(String.format(
"Class %s not subtype of %s", t.getRawClass().getName(), _baseType));
}
return t;
}
Class cls;
try {
cls = tf.findClass(id);
} catch (ClassNotFoundException e) {
// 24-May-2016, tatu: Ok, this is pretty ugly, but we should always get
// DeserializationContext, just playing it safe
if (ctxt instanceof DeserializationContext) {
DeserializationContext dctxt = (DeserializationContext) ctxt;
// First: we may have problem handlers that can deal with it?
return dctxt.handleUnknownTypeId(_baseType, id, this, "no such class found");
}
// ... meaning that we really should never get here.
return null;
} catch (Exception e) {
throw new IllegalArgumentException("Invalid type id '"+id+"' (for id type 'Id.class'): "+e.getMessage(), e);
}
return tf.constructSpecializedType(_baseType, cls);
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
protected String _idFrom(Object value, Class cls, TypeFactory typeFactory)
{
// Need to ensure that "enum subtypes" work too
if (Enum.class.isAssignableFrom(cls)) {
if (!cls.isEnum()) { // means that it's sub-class of base enum, so:
cls = cls.getSuperclass();
}
}
String str = cls.getName();
if (str.startsWith(JAVA_UTIL_PKG)) {
// 25-Jan-2009, tatu: There are some internal classes that we cannot access as is.
// We need better mechanism; for now this has to do...
// Enum sets and maps are problematic since we MUST know type of
// contained enums, to be able to deserialize.
// In addition, EnumSet is not a concrete type either
if (value instanceof EnumSet) { // Regular- and JumboEnumSet...
Class enumClass = ClassUtil.findEnumType((EnumSet) value);
// not optimal: but EnumSet is not a customizable type so this is sort of ok
str = typeFactory.constructCollectionType(EnumSet.class, enumClass).toCanonical();
} else if (value instanceof EnumMap) {
Class enumClass = ClassUtil.findEnumType((EnumMap) value);
Class valueClass = Object.class;
// not optimal: but EnumMap is not a customizable type so this is sort of ok
str = typeFactory.constructMapType(EnumMap.class, enumClass, valueClass).toCanonical();
} else {
// 17-Feb-2010, tatus: Another such case: result of Arrays.asList() is
// named like so in Sun JDK... Let's just plain old ArrayList in its place.
// ... also, other similar cases exist...
String suffix = str.substring(JAVA_UTIL_PKG.length());
if (isJavaUtilCollectionClass(suffix, "List")) {
str = ArrayList.class.getName();
} else if (isJavaUtilCollectionClass(suffix, "Map")){
str = HashMap.class.getName();
} else if (isJavaUtilCollectionClass(suffix, "Set")){
str = HashSet.class.getName();
}
}
} else if (str.indexOf('$') >= 0) {
/* Other special handling may be needed for inner classes,
* The best way to handle would be to find 'hidden' constructor; pass parent
* value etc (which is actually done for non-anonymous static classes!),
* but that is just not possible due to various things. So, we will instead
* try to generalize type into something we will be more likely to be able
* construct.
*/
Class outer = ClassUtil.getOuterClass(cls);
if (outer != null) {
// one more check: let's actually not worry if the declared static type is
// non-static as well; if so, deserializer does have a chance at figuring it all out.
Class staticType = _baseType.getRawClass();
if (ClassUtil.getOuterClass(staticType) == null) {
// Is this always correct? Seems like it should be...
cls = _baseType.getRawClass();
str = cls.getName();
}
}
}
return str;
}
@Override
public String getDescForKnownTypeIds() {
return "class name used as type id";
}
private static boolean isJavaUtilCollectionClass(String clz, String type)
{
if (clz.startsWith("Collections$")) {
// 02-Jan-2017, tatu: As per [databind#1868], may need to leave Unmodifiable variants as is
return (clz.indexOf(type) > 0)
&& !clz.contains("Unmodifiable");
}
if (clz.startsWith("Arrays$")) {
return (clz.indexOf(type) > 0);
}
return false;
}
}