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}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With