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

现在我和jackson合作,我对此有一些疑问。

首先。 我有两个服务,第一个是数据收集和发送服务,第二个接收这些数据,例如,将其记录到文件中。

所以,第一个服务有类层次结构,如下所示:

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

第二个服务有类层次结构,如下所示:

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

第一个服务对ConcreteXAdapter一无所知。

我在第一个服务上发送数据的方式:

 Collection data = new LinkedBlockingQueue() 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 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 unmarshall(InputStream source, TypeReference typeReference) throws IOException { return getObjectMapper().readValue(source, typeReference); } public  T unmarshall(byte[] json, TypeReference typeReference) throws IOException { return getObjectMapper().readValue(json, typeReference); } } 

所以,我想将json想到了ConcreteXAdapter的Collection,而不是在ConcreteX Collection( ConcreteA -> ConcreteAAdapter, ConcreteB -> ConcreteBAdapter, ConcreteC -> ConcreteCAdapter )中。 在我描述的情况下我想得到:

 Collection [ConcreteAAdapter, ConcreteBAdapter, ConcreteCAdapter] 

我怎样才能做到这一点?

为此,您需要在JSON中传递其他信息:

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

然后在序列化时,它将添加@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>() {}) .writeValueAsString(someList); { "@type" : "ConcreteA", ... } 

反序列化将是:

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

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

我是怎么解决这个问题的。 这是一个示例项目的类图: 类图

所以我想在反序列化后获得ConcreteAAdapter表单ConcreteA

我的解决方案是扩展ClassNameIdResolver以添加将基类对象反序列化为子类型对象的function(子类型不添加额外的function和其他字段)。

这是一个创建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 classes = new HashMap() { { 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; } 

这是一个创建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; } 

测试代码:

 public static void main(String[] args) throws IOException { JacksonUtils JacksonUtils = new JacksonUtilsImpl(); Collection data = new LinkedBlockingQueue(); data.add(new ConcreteA()); data.add(new ConcreteB()); data.add(new ConcreteC()); String json = JacksonUtils.marshallIntoString(data); System.out.println(json); Collection adapters = JacksonUtils.unmarshall(json, new TypeReference>() {}); for (Adapter adapter : adapters) { System.out.println(adapter.getClass().getName()); } } 

JacksonUtils类的完整代码:

 public class JacksonUtilsImpl implements JacksonUtils { @Override public byte[] marshall(Collection data) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream() { @Override public byte[] toByteArray() { return buf; } }; getObjectMapperForSerialization().writerWithType(new TypeReference>() {}).writeValue(out, data); return out.toByteArray(); } @Override public String marshallIntoString(Collection 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 classes = new HashMap() { { 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 unmarshall(InputStream source, TypeReference typeReference) throws IOException { return getObjectMapperForDeserialization().readValue(source, typeReference); } @Override public  T unmarshall(byte[] json, TypeReference typeReference) throws IOException { return getObjectMapperForDeserialization().readValue(json, typeReference); } @Override public  Collection unmarshall(String json, Class> klass) throws IOException { return getObjectMapperForDeserialization().readValue(json, klass); } @Override public  Collection unmarshall(String json, TypeReference typeReference) throws IOException { return getObjectMapperForDeserialization().readValue(json, typeReference); } } 

我发现programmerbruce的方法是最清晰,最容易上手的(下面的例子)。 我从他对相关问题的回答中得到了相关信息: https : //stackoverflow.com/a/6339600/1148030和相关的博文: http : //programmerbruce.blogspot.fi/2011/05/deserialize-json-with -jackson-into.html

另请查看这个友好的维基页面(也在Eugene Retunsky的回答中提到): http ://wiki.fasterxml.com/JacksonPolymorphicDeserialization

另一个不错的维基页面: http : //wiki.fasterxml.com/JacksonMixInAnnotations

这是一个简短的例子,可以给你一个想法:

像这样配置ObjectMapper:

  mapper.getDeserializationConfig().addMixInAnnotations(Base.class, BaseMixin.class); mapper.getSerializationConfig().addMixInAnnotations(Base.class, BaseMixin.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 { } 

在第二个服务上,您可以像这样定义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 { }