如果您查看 Jackson Api,AsPropertyTypeDeserializer 是负责使用属性识别子类型的类。如果您查看该类,则有一个名为deserializeTypedFromObject 的方法将使用JsonTypeIdResolver 标识子类。我们可以扩展这个类并覆盖方法deserializeTypedFromObject 和forProperty。
package com.dilipkumarg.tutorials.dynamicsubtype;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer;
import com.fasterxml.jackson.databind.node.TreeTraversingParser;
import com.fasterxml.jackson.databind.type.SimpleType;
public class CustomTypeDeserializer extends AsPropertyTypeDeserializer {
public CustomTypeDeserializer(
final JavaType bt, final TypeIdResolver idRes,
final String typePropertyName, final boolean typeIdVisible, final Class<?> defaultImpl) {
super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
}
public CustomTypeDeserializer(
final AsPropertyTypeDeserializer src, final BeanProperty property) {
super(src, property);
}
@Override
public TypeDeserializer forProperty(
final BeanProperty prop) {
return (prop == _property) ? this : new CustomTypeDeserializer(this, prop);
}
@Override
public Object deserializeTypedFromObject(
final JsonParser jp, final DeserializationContext ctxt) throws IOException {
JsonNode node = jp.readValueAsTree();
Class<?> subType = findSubType(node);
JavaType type = SimpleType.construct(subType);
JsonParser jsonParser = new TreeTraversingParser(node, jp.getCodec());
if (jsonParser.getCurrentToken() == null) {
jsonParser.nextToken();
}
/* 16-Dec-2010, tatu: Since nominal type we get here has no (generic) type parameters,
* we actually now need to explicitly narrow from base type (which may have parameterization)
* using raw type.
*
* One complication, though; can not change 'type class' (simple type to container); otherwise
* we may try to narrow a SimpleType (Object.class) into MapType (Map.class), losing actual
* type in process (getting SimpleType of Map.class which will not work as expected)
*/
if (_baseType != null && _baseType.getClass() == type.getClass()) {
type = _baseType.narrowBy(type.getRawClass());
}
JsonDeserializer<Object> deser = ctxt.findContextualValueDeserializer(type, _property);
return deser.deserialize(jsonParser, ctxt);
}
protected Class<?> findSubType(JsonNode node) {
Class<? extends ParentClass> subType = null;
String cat = node.get("header").get("category").asText();
if (cat.equals("CATEGORY1")) {
subType = Category1Class.class;
} else if (cat.equals("CATEGORY2")) {
subType = Category2Class.class;
}
return subType;
}
}
在扩展类中,我们使用 idResolver 绕过子类型识别,而是使用 header 字段的 category 字段动态识别。
我们需要TypeResolverBuilder 来创建新的CustomTypeDeserializer 实例。
package com.dilipkumarg.tutorials.dynamicsubtype;
import java.util.Collection;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
public class CustomTypeResolver extends StdTypeResolverBuilder {
@Override
public TypeDeserializer buildTypeDeserializer(final DeserializationConfig config, final JavaType baseType, final Collection<NamedType> subtypes) {
return new CustomTypeDeserializer(baseType, null,
_typeProperty, _typeIdVisible, _defaultImpl);
}
}
现在我们有一个CustomTypeResolver 用于子类型识别,但是当Jackon 找到“ParentClass”时,它如何知道查看这个类?
我们可以通过两种方式做到这一点:
使用自定义配置扩展 JackonAnnotationInterceptor 并在创建 ObjectMapper 时对其进行配置。
使用@JsonTypeResolver 注释。这是推荐的方法,因为我们不需要配置任何东西。
包含类型解析器后,我们的新 ParentClass 类将是:
package com.dilipkumarg.tutorials.dynamicsubtype;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonTypeResolver;
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
@JsonTypeResolver(CustomTypeResolver.class)
public class ParentClass {
private Header header;
private String nextField;
...
}
见here