Jackson的多态序列化/反序列化和自定义序列化/解串器

时间:2017-08-08 08:33:42

标签: java json jackson

我是一个可以通过许多具体类型实现的OID接口:

public interface OID {
}
public abstract class OIDBase
           implements OID {
   private String _id;
}
public class MyOID 
     extends OIDBase {
    public MyOID() {
        // default no-args constructor
    }
}
public class MyOtherOID 
     extends OIDBase {
    public MyOtherOID() {
        // default no-args constructor
    }
}

现在我有一个包含两个字段的对象,一个使用抽象接口类型(OID)定义,另一个使用具体类型(MyOID)定义

public class MyBean {
    private OID _absOid;
    private MyOID _concreteOid;

    public MyBean(final OID absOid,final MyOID concreteOid) {
        _absOid = absOid;
        _concreteOid = concreteOid;
    }
}

我想使用jackson以不同方式序列化/反序列化字段,无论它们是使用抽象接口类型还是使用具体类型定义的:

{
    "_absOid": {
                    "type" : "MyOtherOID",
                    "id" : "__an_id__"
               },
    "_concreteOid" : "__another_id___"
}

请注意_absOid已序列化,包括类型信息(多态序列化),_concreteOid序列化为文本

为此,我将OID界面注释为:

@JsonSubTypes({
    @JsonSubTypes.Type(MyOID.class),
    @JsonSubTypes.Type(MyOtherOID.class)
})
public interface OID {
}

并为每个具体类型分配了一个类型ID:

@JsonTypeName("MyOID")
public class MyOID 
     extends OIDBase {
    ...
}
@JsonTypeName("MyOtherOID")
public class MyOtherOID 
     extends OIDBase {
    ...
}

最后,容器bean中的抽象定义字段被注释为使jackson包含类型信息:

public class MyBean {
    @JsonTypeInfo(include = JsonTypeInfo.As.PROPERTY,
                  use = JsonTypeInfo.Id.NAME,
                  property = "type")
    private OID _absOid;

    private MyOID _concreteOid;
}

到目前为止,非常好,但是如果使用抽象类型(OID)定义字段并且如果使用具体类型(MyOID)定义字段,则以不同方式序列化,我必须创建自定义序列化器:< / p>

首先注释使用序列化器/反序列化器的具体类型:

@JsonTypeName("MyOID")
@JsonSerialize(using=OIDSerializer.class) @JsonDeserialize(using=OIDDeSerializer.class)
public class MyOID 
     extends OIDBase {
    ...
}
@JsonTypeName("MyOtherOID")
@JsonSerialize(using=OIDSerializer.class) @JsonDeserialize(using=OIDDeSerializer.class)
public class MyOtherOID 
     extends OIDBase {
    ...
}

...序列化/解串器代码:

public static class OIDSerializer 
            extends JsonSerializer<OID> {
    @Override
    public void serialize(final OID value,
                          final JsonGenerator jgen,
                          final SerializerProvider provider) throws IOException,JsonProcessingException {
        // **** used when serializing a concrete type
        jgen.writeString(value.toString());
    }
    @Override
    public void serializeWithType(final OID value, 
                                  final JsonGenerator jgen,
                                  final SerializerProvider provider,
                                  final TypeSerializer typeSer) throws IOException {
        // **** used when serializing a polymorphic type
        // guess the type id
        WritableTypeId typeId = typeSer.typeId(value,JsonToken.START_OBJECT);

        // type prefix
        typeSer.writeTypePrefix(jgen,
                                typeId);

        // object
        jgen.writeFieldName("id");
        jgen.writeString(value.toString());

        // type suffix 
        typeId.wrapperWritten = !jgen.canWriteTypeId();
        typeSer.writeTypeSuffix(jgen,
                                typeId);
    }
}

问题在反序列化 json字符串时出现,我使用了以下自定义反序列化器:

public static class OIDDeSerializer 
            extends StdDeserializer<OID> { 

    public MyOIDDeSerializer() { 
        super(MyOID.class); 
    } 
    @Override
    public OID deserialize(final JsonParser jp,
                           final DeserializationContext ctxt) throws IOException,JsonProcessingException {            
        JsonNode node = jp.getCodec().readTree(jp); 

        // [1] - Read the id depending on the serialized format
        String oidStr = null;           
        // text node > concrete oid impl serialized as [value]
        if (node.getNodeType() == JsonNodeType.STRING) {
            oidStr = ((TextNode)node).asText();
        }
        // oid=value > abstract oid impl serialized as {typeId=[type],oid={value]} 
        else {
            ObjectNode objNode = (ObjectNode)node;
            oidStr = objNode.findValue("id").asText();
        }

        // [2] - Read tye type id 
        String typeId = objNode.findValue("type").asText()

        // PROBLEM!!!!!!
        // how get the type from the typeId in order to create the concrete instance
        // how access the type resolver????
        Class<? extends OID> oidType = RESOLVE TYPE FROM THE ID
        return oidType.newInstance();
    }
}

所以问题 如何在自定义反序列化 ???

中访问类型ID解析中的类型

2 个答案:

答案 0 :(得分:2)

你可以试试这个:

AnnotationIntrospector annotationInspector = new JacksonAnnotationIntrospector();
AnnotatedClass annotatedClass = AnnotatedClass.constructWithoutSuperTypes(OID.class,
                new ObjectMapper().getSerializationConfig());
List<NamedType> subtypes = annotationInspector.findSubtypes(annotatedClass);

for(NamedType type: subtypes){
    if(type.getType().getName().contains(typeId)){
        return type.getClass().newInstance();   
    }

}

答案 1 :(得分:1)

序列化程序必须注意这个字段可能与类型信息一起序列化(当使用抽象类型声明字段时) ...所以序列化器必须覆盖serializeWithType()

public static class OIDSerializer 
            extends JsonSerializer<OID> {
    @Override
    public void serialize(final OID value,
                          final JsonGenerator jgen,
                          final SerializerProvider provider) throws IOException,JsonProcessingException {
        jgen.writeString(value.asString());
    }
    @Override
    public void serializeWithType(final OID value,
                                  final JsonGenerator jgen,
                                  final SerializerProvider provider,
                                  final TypeSerializer typeSer) throws IOException {
        // guess the type id
        WritableTypeId typeId = typeSer.typeId(value,JsonToken.START_OBJECT);

        // type prefix
        typeSer.writeTypePrefix(jgen,
                                typeId);

        // object
        jgen.writeFieldName("idStr");
        jgen.writeString(value.asString());

        // type suffix 
        typeId.wrapperWritten = !jgen.canWriteTypeId();
        typeSer.writeTypeSuffix(jgen,
                                typeId);
        }
}

另一方面,反序列化器的有趣部分是使用@ Sachin在_oidTypeFromId()方法中的尖头解决方案从typeId解析类型的位置:

public static class OIDDeSerializer 
             extends StdDeserializer<OID> 
         implements ContextualDeserializer {

    // property being deserialized
    private BeanProperty _property;

    public OIDDeSerializer() { 
        super(OID.class); 
    }      
    @Override
    public JsonDeserializer<?> createContextual(final DeserializationContext ctxt,
                                                final BeanProperty property) throws JsonMappingException {
        _property = property;
        return this;
    }
    @Override
    public OID deserialize(final JsonParser parser,
                             final DeserializationContext ctxt) throws IOException,
                                                                       JsonProcessingException {
        String idStr = null;                        // the oid
        Class<? extends OID> oidType = null;      // the oid type

        JsonNode node = parser.getCodec().readTree(parser); 

        // [1] - Read the id depending on the serialized format
        // text node > concrete oid impl serialized as [value]
        if (node.getNodeType() == JsonNodeType.STRING) {
            idStr = ((TextNode)node).asText();
            oidType = (Class<? extends OID>)_property.getType().getRawClass();
        }
        // oid=value > abstract oid impl serialized as {typeId=[type],oid={value]} 
        else {
            ObjectNode objNode = (ObjectNode)node;
            idStr = objNode.findValue("idStr").asText();
            String typeId = objNode.findValue("typeId").asText();
            oidType = (Class<? extends OID>)_oidTypeFromId(ctxt.getAnnotationIntrospector(),
                                                                ctxt.getConfig(),
                                                                typeId);
        }            
        // [2] - Get the oid type
        OID outOid = ReflectionUtils.createInstanceFromString(oidType,idStr);
        return outOid;
    }

    private Class<? extends OID> _oidTypeFromId(final AnnotationIntrospector annotationIntrospector,
                                                final DeserializationConfig cfg,
                                                final String typeId) {
        Class<? extends OID> outType = null;
        AnnotatedClass annotatedClass = AnnotatedClassResolver.resolveWithoutSuperTypes(cfg,
                                                                                        OID.class);
        List<NamedType> subtypes = annotationIntrospector.findSubtypes(annotatedClass);
        for (NamedType type: subtypes){
            if (type.getName().equals(typeId)){
                outType = (Class<? extends OID>)type.getType();   
            }
        }
        return outType;
    }
}