使用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 extends Adapter> 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 extends T> unmarshall(String json, Class extends Collection extends T>> klass) throws IOException { return getObjectMapperForDeserialization().readValue(json, klass); } @Override public Collection extends T> 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 { }
- 如何使用Jackson或任何其他api反序列化对象数组?
- jackson – 动态地抑制属性的序列化(写入)
- Jackson ObjectMapper DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT
- jackson动态改变JsonIgnore
- 用Jackson以两种不同的方式序列化一个class级
- 使用Jackson序列化一个Double到2的小数位
- XMLWriter使用zdef扩展属性名称?
- 如何针对HTML和JSON请求以不同方式处理Spring MVC中的exception
- SpringMVC Jackson2HttpMessageConverter定制不起作用