I'd like to migrate my ObjectMapper configuration from one that includes type information, to one that does not.
Currently, my ObjectMapper is configured like so:
ObjectMapper objectMapper = new ObjectMapper().enableDefaultTypingAsProperty(
ObjectMapper.DefaultTyping.NON_FINAL,
"@class"
);
Which allows me to deserialize json objects that look like this:
{
"@class": "com.foo.BarClass",
"barProp": "bar"
"barList": [
[
"java.util.ArrayList",
[
{ "@class": "com.foo.BarListElement", ... },
{ "@class": "com.foo.BarListElement", ... },
...
]
],
...
}
With the following POJO:
public class BarClass {
private String barProp;
// assume BarListElement to be a POJO with sensible getters/setters
List<BarListElement> barList;
// assume some other trivial fields and corresponding getters/setters
}
However, I would like remove the call to enableDefaultTypingAsProperty
as my application code already knows the class during deserialization, therefore it unnecessarily ties the deserialization code to the fully qualified class name.
I tried to achieve this by removing the call to enableDefaultTypingAsProperty
and calling configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
on the ObjectMapper instead, with the intention of ignoring the "@class"
property present on the json. Unfortunately, that only works for json that does not contain arrays, since the json array I receive contains the concrete List type information (i.e. the "java.util.ArrayList"
element in the array in the example).
I would like to ask if there is any way to configure the ObjectMapper
such that the collection type on the json array is also ignored.
You need to customise collection deserialiser which will take care about extra type information. Array with type has a general structure: ["java.util.collectionType", [...]]
. To do that we need to use BeanDeserializerModifier
and custom CollectionDeserializer
. Let's start from custom deserialiser:
class OmitListTypeJsonDeserializer extends CollectionDeserializer {
public OmitListTypeJsonDeserializer(CollectionDeserializer src) {
super(src);
}
@Override
public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
p.nextToken(); // Omit array type
p.nextToken(); // Omit comma and start array
Collection<Object> collection = super.deserialize(p, ctxt);
p.nextToken(); // Omit end of array
return collection;
}
@Override
public CollectionDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
return new OmitListTypeJsonDeserializer(super.createContextual(ctxt, property));
}
}
Bean deserializer modifier looks like below:
class OmitListTypeDeserializerModifier extends BeanDeserializerModifier {
@Override
public JsonDeserializer<?> modifyCollectionDeserializer(DeserializationConfig config, CollectionType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if (deserializer instanceof CollectionDeserializer) {
return new OmitListTypeJsonDeserializer((CollectionDeserializer) deserializer);
}
return deserializer;
}
}
Example usage:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.std.CollectionDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.CollectionType;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
ObjectMapper typeAwareMapper = new ObjectMapper();
typeAwareMapper.enable(SerializationFeature.INDENT_OUTPUT);
typeAwareMapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL, "@class");
String json = typeAwareMapper.writeValueAsString(new BarClass());
System.out.println(json);
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new OmitListTypeDeserializerModifier());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
System.out.println(mapper.readValue(json, BarClass.class));
}
}
class BarClass {
private String barProp = "PROP1";
private List<BarListElement> barList = Arrays.asList(new BarListElement(), new BarListElement());
private Integer zet = 123;
// getters, setters
}
class BarListElement {
private Double sid = Math.random();
// getters, setters
}
Above code prints below JSON
:
{
"@class" : "com.celoxity.BarClass",
"barProp" : "PROP1",
"barList" : [ "java.util.Arrays$ArrayList", [ {
"@class" : "com.celoxity.BarListElement",
"sid" : 0.09525693290513237
}, {
"@class" : "com.celoxity.BarListElement",
"sid" : 0.689909415561781
} ] ],
"zet" : 123
}
And deserialised object:
BarClass{barProp='PROP1', barList=[BarListElement{sid=0.09525693290513237}, BarListElement{sid=0.689909415561781}], zet=123}