Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StdDeserializer for abstract class and it's subtypes

Tags:

java

json

jackson

Let there be an abstract class A with a property a and three non-abstract subclasses B, C and D. B has no additional property, C contains the property c and D contains both properties c and d.

I would like to subclass StdDeserializer for the abstract class A to be able to decide based on the existence of properties to be deserialized which subclass to choose.

I did that with some Jackson release from Codehaus before and it was working fine using the following implementation:

class AbstractADeserializer extends StdDeserializer<A> {
    AbstractADeserializer () {
        super(A.class);
    }

    @Override
    public A deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        ObjectMapper mapper = (ObjectMapper) jp.getCodec();
        ObjectNode root = (ObjectNode) mapper.readTree(jp);
        Class<? extends A> requestClass = null;

        // root.getFieldNames() is an iterator over all field names
        JsonNode cValue = root.findValue("c");
        JsonNode dValue = root.findValue("d");

        /*
         * Check for existence of required fields and choose the
         * corresponding request class.
         */
        logger.debug(Boolean.toString(c != null));
        logger.debug(Boolean.toString(d != null));

        if(c != null && d == null) {
            logger.debug("Found C");
            requestClass = C.class;
        } else if(c != null && d != null) {
            logger.debug("Found D");
            requestClass = D.class;
        } else {
            logger.debug("Found B");
            requestClass = B.class;
        }

        return mapper.readValue(root, requestClass);
    }
}

This worked fine but after migration to Jackson 2.4 from FasterXML ObjectMapper does not allow ObjectNode as a parameter for it's readValue method.

When I modified the code to use return mapper.readValue(jp, requestClass); I always get

    com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input
     at [Source: org.apache.catalina.connector.CoyoteInputStream@1286ec89; line: 1, column: 559]
        at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
        at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:3095)
        at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3009)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1637)

What alternatives do you see the flexible determine the class of a given input without needing to deserialize the object manually? To me it looks complicated to clone the JsonParser to avoid the exhaustion of it's input source.

like image 478
Christoph Avatar asked Oct 01 '22 00:10

Christoph


1 Answers

Just ran into similar issues, and for me it was all fixed by replacing mapper.readValue(jp, requestClass) with mapper.treeToValue(root, requestClass)

like image 133
Dave Avatar answered Oct 05 '22 22:10

Dave