具有嵌套在对象中的type属性的Jackson多态反序列化 [英] Jackson polymorphic deserialization with type property that is nested in object

查看:1924
本文介绍了具有嵌套在对象中的type属性的Jackson多态反序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图找到一种方法来使用jackson的多态反序列化功能,它将基于嵌套在头/控件对象中的属性反序列化我的对象:

I'm trying to find out a way to use the polymorphic deserialization feature of jackson in a way that it will deserialize my object based on a property that is nested in header/control object:

JSON 1 - CATEGORY1:

JSON 1 - CATEGORY1:

{
 "id":"someId",
 "header":{
           "category":"CATEGORY1",
           "somOtherProperty":"someValue"
          }
 "nextField":"nextValue",
 ...
}

JSON 2 - CATEGORY2

JSON 2 - CATEGORY2

{
 "id":"someId",
 "header":{
           "category":"CATEGORY2",
           "somOtherProperty":"someValue"
          }
 "nextField":"nextValue",
 ...
}

父类(注释类似于此)

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "category")
@JsonSubTypes({
        @Type(value = Category1Class.class, name = "CATEGORY1"), 
        @Type(value = Category2Class.class, name = "CATEGORY2") })
public class ParentClass{
    private Header header;
    private String nextField;
    ...
}

public class Header{
    private String category;
    private String somOtherProperty;
    ...
}

子课程

@JsonTypeName("CATEGORY1")
public class Category1Class extends ParentClass{
    ...
}

@JsonTypeName("CATEGORY2")
public class Category2Class extends ParentClass{
    ...
}

杰克逊是否有开箱即用的功能可以让我进行这种反序列化或者我错过了什么?

Is there an out of the box functionality in jackson that would enable me to do this kind of deserialization or am I missing something?

推荐答案

如果查看Jackson Api AsPropertyTypeDeserializer 是负责使用属性进行子类型识别的类。如果查看该类,则会有一个名为 deserializeTypedFromObject 的方法,该方法将使用 JsonTypeIdResolver 来标识子类。我们可以扩展这个类并重写方法 deserializeTypedFromObject forProperty

If you look at the Jackson Api AsPropertyTypeDeserializer is the class responsible for sub type identification using property. If you look at that class there is a method called deserializeTypedFromObject which will identify the subclass using JsonTypeIdResolver. We can extend this class and override methods deserializeTypedFromObject and 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绕过子类型标识我们使用类别字段标题字段动态识别。

我们需要 TypeResolverBuilder 创建新的 CustomTypeDeserializer 实例。

In extended class we bypassed sub type identification using idResolver instead we are identifying dynamically with category field of header field.
We need TypeResolverBuilder to create new CustomTypeDeserializer instance.

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'时会如何看待这个类?

我们可以通过两种方式实现:

Now we have a CustomTypeResolver for sub type identification, but how Jackon will know to look this class when it found 'ParentClass'?
We can do it by two ways:


  1. 使用自定义配置扩展 JackonAnnotationInterceptor 并在创建 ObjectMapper时配置它

使用 @JsonTypeResolver 注释。这是推荐的方法,因为我们不需要配置任何东西。

Using @JsonTypeResolver annotation. This is the recommended approach as we don’t need to configure any thing.

包含类型解析器后我们的新 ParentClass 类将是:

After including type resolver our new ParentClass class will be:

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;
    ...
}

参见这里

这篇关于具有嵌套在对象中的type属性的Jackson多态反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆