簡體   English   中英

具有嵌套在對象中的type屬性的Jackson多態反序列化

[英]Jackson polymorphic deserialization with type property that is nested in object

我試圖找到一種方法來使用jackson的多態反序列化功能,它將基於嵌套在頭/控件對象中的屬性反序列化我的對象:

JSON 1 - CATEGORY1:

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

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

在jackson中是否有開箱即用的功能可以讓我進行這種反序列化或者我錯過了什么?

如果你看一下Jackson Api AsPropertyTypeDeserializer是負責使用屬性進行子類型識別的類。 如果你看一下這個類有一個名為方法deserializeTypedFromObject將使用標識子JsonTypeIdResolver 我們可以擴展這個類並覆蓋方法deserializeTypedFromObjectforProperty

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用於子類型識別,但CustomTypeResolver在找到'ParentClass'時會怎么知道這個類?
我們可以通過兩種方式實現:

  1. 使用自定義配置擴展JackonAnnotationInterceptor並在創建ObjectMapper對其進行配置。

  2. 使用@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;
    ...
}

看到這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM