How use jackson ObjectMapper inside custom deserializer? How use jackson ObjectMapper inside custom deserializer? json json

How use jackson ObjectMapper inside custom deserializer?


The immediate problem seems to be that the @JsonDeserialize(using=...) is being picked up for your implementations of MyInterface as well as MyInterface itself: hence the endless loop.

You can fix this my overriding the setting in each implementation:

@JsonDeserialize(using=JsonDeserializer.None.class)public static class MySuccess implements MyInterface {}

Or by using a module instead of an annotation to configure the deserialization (and removing the annotation from MyInterface):

mapper.registerModule(new SimpleModule() {{    addDeserializer(MyInterface.class, new MyDeserializer());}});

On a side-note, you might also consider extending StdNodeBasedDeserializer to implement deserialization based on JsonNode. For example:

@Overridepublic MyInterface convert(JsonNode root, DeserializationContext ctxt) throws IOException {    java.lang.reflect.Type targetType;    if (root.has("custom_field")) {        targetType = MyFailure.class;    } else {        targetType = MySuccess.class;    }    JavaType jacksonType = ctxt.getTypeFactory().constructType(targetType);    JsonDeserializer<?> deserializer = ctxt.findRootValueDeserializer(jacksonType);    JsonParser nodeParser = root.traverse(ctxt.getParser().getCodec());    nodeParser.nextToken();    return (MyInterface) deserializer.deserialize(nodeParser, ctxt);}

There are a bunch of improvements to make to this custom deserializer, especially regarding tracking the context of the deserialization etc., but this should provide the functionality you're asking for.


In order to use your own ObjectMapper inside a custom deserializer, you can use Jackson Mix-in Annotations (the DefaultJsonDeserializer interface) to dynamically remove the custom deserializer from the POJO classes, avoiding the StackOverflowError that would otherwise be thrown as a result of objectMapper.readValue(JsonParser, Class<T>).

public class MyDeserializer extends JsonDeserializer<MyInterface> {    private static final ObjectMapper objectMapper = new ObjectMapper();    static {        objectMapper.addMixIn(MySuccess.class, DefaultJsonDeserializer.class);        objectMapper.addMixIn(MyFailure.class, DefaultJsonDeserializer.class);    }    @Override    public MyInterface deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {        if (jp.getCodec().<JsonNode>readTree(jp).has("custom_field")) {            return objectMapper.readValue(jp, MyFailure.class);        } else {            return objectMapper.readValue(jp, MySuccess.class);        }               }    @JsonDeserialize    private interface DefaultJsonDeserializer {        // Reset default json deserializer    }}


This did the trick for me:

ctxt.readValue(node, MyFailure.class)