简体   繁体   English

使用 Jackson 将 Json 反序列化为其他类层次结构

[英]Json deserialization into other class hierarchy using Jackson

Now i'm working with Jackson and i have some questions about it.现在我正在和杰克逊一起工作,我对此有一些疑问。

First of all.首先。 I have two services, first is data collecting and sending service and second receive this data and, for example, log it into a file.我有两个服务,第一个是数据收集和发送服务,第二个是接收这些数据,例如,将其记录到文件中。

So, first service has class hierarchy like this:因此,第一个服务具有如下类层次结构:

         +----ConcreteC
         |
Base ----+----ConcreteA
         |
         +----ConcreteB

And second service has class hierarchy like this:第二个服务有这样的类层次结构:

ConcreteAAdapter extends ConcreteA implements Adapter {}
ConcreteBAdapter extends ConcreteB implements Adapter {}
ConcreteCAdapter extends ConcreteC implements Adapter {}

The first service knows nothing about ConcreteXAdapter .第一个服务对ConcreteXAdapter一无所知。

The way i'm sending the data on the first service:我在第一个服务上发送数据的方式:

Collection<Base> data = new LinkedBlockingQueue<Base>()
JacksonUtils utils = new JacksonUtils();
data.add(new ConcreteA());
data.add(new ConcreteB());
data.add(new ConcreteC());
...
send(utils.marshall(data));
...

public class JacksonUtils {

    public byte[] marshall(Collection<Base> data) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream() {
            @Override
            public byte[] toByteArray() {
                return buf;
            }
        };

        getObjectMapper().writeValue(out, data);
        return out.toByteArray();
    }
    protected ObjectMapper getObjectMapper() {
        return new ObjectMapper();
    }

    public Object unmarshall(byte[] json) throws IOException {
        return getObjectMapper().readValue(json, Object.class);
    }

    public <T> T unmarshall(InputStream source, TypeReference<T> typeReference) throws IOException {
        return getObjectMapper().readValue(source, typeReference);
    }

    public <T> T unmarshall(byte[] json, TypeReference<T> typeReference) throws IOException {
        return getObjectMapper().readValue(json, typeReference);
    }
}

So, i want to desirialize json into Collection of ConcreteXAdapter , not into Collection of ConcreteX ( ConcreteA -> ConcreteAAdapter, ConcreteB -> ConcreteBAdapter, ConcreteC -> ConcreteCAdapter ).所以,我想desirialize JSON成集ConcreteXAdapter ,不进的收藏ConcreteXConcreteA -> ConcreteAAdapter, ConcreteB -> ConcreteBAdapter, ConcreteC -> ConcreteCAdapter )。 In the case i described i want to get:在我描述的情况下,我想得到:

Collection [ConcreteAAdapter, ConcreteBAdapter, ConcreteCAdapter]

How can i do this?我怎样才能做到这一点?

For this purpose you need to pass additional info in JSON:为此,您需要在 JSON 中传递其他信息:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, 
      include=JsonTypeInfo.As.PROPERTY, property="@type")
class Base {
...
}

Then on serialization it will add @type field:然后在序列化时它会添加@type 字段:

objectMapper.registerSubtypes(
            new NamedType(ConcreteAAdapter.class, "ConcreteA"),
            new NamedType(ConcreteBAdapter.class, "ConcreteB"),
            new NamedType(ConcreteCAdapter.class, "ConcreteC")
            );

// note, that for lists you need to pass TypeReference explicitly
objectMapper.writerWithType(new TypeReference<List<Base>>() {})
     .writeValueAsString(someList);


    {
      "@type" : "ConcreteA",
      ...
    }

on deserialization it will be:在反序列化时,它将是:

    objectMapper.registerSubtypes(
            new NamedType(ConcreteA.class, "ConcreteA"),
            new NamedType(ConcreteB.class, "ConcreteB"),
            new NamedType(ConcreteC.class, "ConcreteC")
            );
    objectMapper.readValue(....)

More info here 更多信息在这里

How I solved this problem.我是如何解决这个问题的。 Here is a class diagram for an example project:下面是一个示例项目的类图:类图

So i want to get the ConcreteAAdapter form ConcreteA after deserialization.所以我希望得到ConcreteAAdapter形式ConcreteA反序列化后。

My solution is to extend ClassNameIdResolver to add functionality to deserialize base class objects into subtype class objects (subtype classes adds no extra functionality and additional fields).我的解决方案是扩展ClassNameIdResolver以添加将基类对象反序列化为子类型类对象的功能(子类型类不添加额外的功能和附加字段)。

Here is a code which creates ObjectMapper for deserialization:这是一个为反序列化创建ObjectMapper的代码:

protected ObjectMapper getObjectMapperForDeserialization() {
        ObjectMapper mapper = new ObjectMapper();

        StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
        typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY);
        typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()) {
            private HashMap<Class, Class> classes = new HashMap<Class, Class>() {
                {
                    put(ConcreteA.class, ConcreteAAdapter.class);
                    put(ConcreteB.class, ConcreteBAdapter.class);
                    put(ConcreteC.class, ConcreteCAdapter.class);
                }
            };

            @Override
            public String idFromValue(Object value) {
                return (classes.containsKey(value.getClass())) ? value.getClass().getName() : null;
            }

            @Override
            public JavaType typeFromId(String id) {
                try {
                    return classes.get(Class.forName(id)) == null ? super.typeFromId(id) : _typeFactory.constructSpecializedType(_baseType, classes.get(Class.forName(id)));
                } catch (ClassNotFoundException e) {
                    // todo catch the e
                }
                return super.typeFromId(id);
            }
        });
        mapper.setDefaultTyping(typeResolverBuilder);
        return mapper;
    }

And here is a code which create ObjectMapper for serialization:这是一个为序列化创建ObjectMapper的代码:

protected ObjectMapper getObjectMapperForSerialization() {
    ObjectMapper mapper = new ObjectMapper();

    StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
    typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY);
    typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()));
    mapper.setDefaultTyping(typeResolverBuilder);

    return mapper;
}

Test code:测试代码:

public static void main(String[] args) throws IOException {
    JacksonUtils JacksonUtils = new JacksonUtilsImpl();

    Collection<Base> data = new LinkedBlockingQueue<Base>();
    data.add(new ConcreteA());
    data.add(new ConcreteB());
    data.add(new ConcreteC());

    String json = JacksonUtils.marshallIntoString(data);

    System.out.println(json);

    Collection<? extends Adapter> adapters = JacksonUtils.unmarshall(json, new TypeReference<ArrayList<Adapter>>() {});

    for (Adapter adapter : adapters) {
        System.out.println(adapter.getClass().getName());
    }
}

Full code of JacksonUtils class: JacksonUtils 类的完整代码:

public class JacksonUtilsImpl implements JacksonUtils {

    @Override
    public byte[] marshall(Collection<Base> data) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream() {
            @Override
            public byte[] toByteArray() {
                return buf;
            }
        };

        getObjectMapperForSerialization().writerWithType(new TypeReference<Collection<Base>>() {}).writeValue(out, data);
        return out.toByteArray();
    }

    @Override
    public String marshallIntoString(Collection<Base> data) throws IOException {
        return getObjectMapperForSerialization().writeValueAsString(data);
    }

    protected ObjectMapper getObjectMapperForSerialization() {
        ObjectMapper mapper = new ObjectMapper();

        StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
        typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY);
        typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()));
        mapper.setDefaultTyping(typeResolverBuilder);

        return mapper;
    }

    protected ObjectMapper getObjectMapperForDeserialization() {
        ObjectMapper mapper = new ObjectMapper();

        StdTypeResolverBuilder typeResolverBuilder = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
        typeResolverBuilder = typeResolverBuilder.inclusion(JsonTypeInfo.As.PROPERTY);
        typeResolverBuilder.init(JsonTypeInfo.Id.CLASS, new ClassNameIdResolver(SimpleType.construct(Base.class), TypeFactory.defaultInstance()) {
            private HashMap<Class, Class> classes = new HashMap<Class, Class>() {
                {
                    put(ConcreteA.class, ConcreteAAdapter.class);
                    put(ConcreteB.class, ConcreteBAdapter.class);
                    put(ConcreteC.class, ConcreteCAdapter.class);
                }
            };

            @Override
            public String idFromValue(Object value) {
                return (classes.containsKey(value.getClass())) ? value.getClass().getName() : null;
            }

            @Override
            public JavaType typeFromId(String id) {
                try {
                    return classes.get(Class.forName(id)) == null ? super.typeFromId(id) : _typeFactory.constructSpecializedType(_baseType, classes.get(Class.forName(id)));
                } catch (ClassNotFoundException e) {
                    // todo catch the e
                }
                return super.typeFromId(id);
            }
        });
        mapper.setDefaultTyping(typeResolverBuilder);
        return mapper;
    }

    @Override
    public Object unmarshall(byte[] json) throws IOException {
        return getObjectMapperForDeserialization().readValue(json, Object.class);
    }

    @Override
    public <T> T unmarshall(InputStream source, TypeReference<T> typeReference) throws IOException {
        return getObjectMapperForDeserialization().readValue(source, typeReference);
    }

    @Override
    public <T> T unmarshall(byte[] json, TypeReference<T> typeReference) throws IOException {
        return getObjectMapperForDeserialization().readValue(json, typeReference);
    }

    @Override
    public <T> Collection<? extends T> unmarshall(String json, Class<? extends Collection<? extends T>> klass) throws IOException {
        return getObjectMapperForDeserialization().readValue(json, klass);
    }


    @Override
    public <T> Collection<? extends T> unmarshall(String json, TypeReference typeReference) throws IOException {
        return getObjectMapperForDeserialization().readValue(json, typeReference);
    }
}

I find programmerbruce's approach to be the most clear and easy to get working (example below).我发现程序员布鲁斯的方法是最清晰、最容易上手的(下面的例子)。 I got the information from his answer to a related question: https://stackoverflow.com/a/6339600/1148030 and the related blog post: http://programmerbruce.blogspot.fi/2011/05/deserialize-json-with-jackson-into.html我从他对相关问题的回答中得到了信息: https : //stackoverflow.com/a/6339600/1148030和相关的博客文章: http : //programmerbruce.blogspot.fi/2011/05/deserialize-json-with -jackson-into.html

Also check out this friendly wiki page (also mentioned in Eugene Retunsky's answer): https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization另请查看这个友好的 wiki 页面(也在 Eugene Retunsky 的回答中提到): https : //github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization

Another nice wiki page: https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations另一个不错的维基页面: https : //github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations

Here is a short example to give you the idea:这是一个简短的示例,可以让您了解这个想法:

Configure the ObjectMapper like this:像这样配置 ObjectMapper:

    mapper.getDeserializationConfig().addMixInAnnotations(Base.class, BaseMixin.class);
    mapper.getSerializationConfig().addMixInAnnotations(Base.class, BaseMixin.class);

Example BaseMixin class (easy to define as an inner class.)示例 BaseMixin 类(易于定义为内部类。)

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
    @JsonSubTypes.Type(value=ConcreteA.class, name="ConcreteA"),
    @JsonSubTypes.Type(value=ConcreteB.class, name="ConcreteB")
})  
private static class BaseMixin {
}

On second service you could define the BaseMixin like this:在第二个服务中,您可以像这样定义 BaseMixin:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
    @JsonSubTypes.Type(value=ConcreteAAdapter.class, name="ConcreteA"),
    @JsonSubTypes.Type(value=ConcreteBAdapter.class, name="ConcreteB")
})  
private static class BaseMixin {
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM