Json反序列化使用Jackson的另一个类层次结构。

时间:2021-05-28 18:03:23

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:

因此,first服务有这样的类层次结构:

         +----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.

第一个服务对具象适配器一无所知。

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). In the case i described i want to get:

因此,我希望将json设计为具象适配器的集合,而不是具象适配器的集合(具象->具象适配器,具象->具象适配器,具象-具象- b2具象适配器)。在我描述的案例中,我想获得:

Collection [ConcreteAAdapter, ConcreteBAdapter, ConcreteCAdapter]

How can i do this?

我该怎么做呢?

3 个解决方案

#1


28  

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 here: http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

更多:http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

#2


16  

How I solved this problem. Here is a class diagram for an example project: Json反序列化使用Jackson的另一个类层次结构。

我是如何解决这个问题的。下面是一个示例项目的类图:

So i want to get the ConcreteAAdapter form ConcreteA after deserialization.

所以我想在反序列化后,得到混凝土适配器。

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

#3


9  

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://*.com/a/6339600/1148030 and the related blog post: http://programmerbruce.blogspot.fi/2011/05/deserialize-json-with-jackson-into.html

我发现programmerbruce的方法是最清晰、最容易使用的(示例如下)。我从他对一个相关问题的回答中得到了信息:https://*.com/a/6339600/1148030以及相关的博客文章:http://programmerbruce.blogspot.fi1/05/deserialize -json-with-jackson-into.html

Also check out this friendly wiki page (also mentioned in Eugene Retunsky's answer): http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

还可以查看这个友好的wiki页面(尤金·雷顿斯基的回答中也提到了):http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

Another nice wiki page: http://wiki.fasterxml.com/JacksonMixInAnnotations

另一个不错的wiki页面:http://wiki.fasterxml.com/jacksonmixinxinxinxinannotations

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

#1


28  

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 here: http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

更多:http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

#2


16  

How I solved this problem. Here is a class diagram for an example project: Json反序列化使用Jackson的另一个类层次结构。

我是如何解决这个问题的。下面是一个示例项目的类图:

So i want to get the ConcreteAAdapter form ConcreteA after deserialization.

所以我想在反序列化后,得到混凝土适配器。

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

#3


9  

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://*.com/a/6339600/1148030 and the related blog post: http://programmerbruce.blogspot.fi/2011/05/deserialize-json-with-jackson-into.html

我发现programmerbruce的方法是最清晰、最容易使用的(示例如下)。我从他对一个相关问题的回答中得到了信息:https://*.com/a/6339600/1148030以及相关的博客文章:http://programmerbruce.blogspot.fi1/05/deserialize -json-with-jackson-into.html

Also check out this friendly wiki page (also mentioned in Eugene Retunsky's answer): http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

还可以查看这个友好的wiki页面(尤金·雷顿斯基的回答中也提到了):http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

Another nice wiki page: http://wiki.fasterxml.com/JacksonMixInAnnotations

另一个不错的wiki页面:http://wiki.fasterxml.com/jacksonmixinxinxinxinannotations

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