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
.
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:
Collection [ConcreteAAdapter, ConcreteBAdapter, ConcreteCAdapter]
How can i do this?
For this purpose you need to pass additional info in JSON:
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME,
include=JsonTypeInfo.As.PROPERTY, property="@type")
class Base {
...
}
Then on serialization it will add @type field:
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(....)