[英]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
,不进的收藏ConcreteX
( ConcreteA -> 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(....)
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.