How use jackson ObjectMapper inside custom deserializer?

前端 未结 3 550
野性不改
野性不改 2020-12-31 02:56

I try to write custom jackson deserializer. I want \"look\" at one field and perform auto deserialization to class, see example below:

import com.fasterxml.j         


        
相关标签:
3条回答
  • 2020-12-31 03:15

    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
        }
    
    }
    
    0 讨论(0)
  • 2020-12-31 03:17

    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:

    @Override
    public 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.

    0 讨论(0)
  • 2020-12-31 03:17

    This did the trick for me:

    ctxt.readValue(node, MyFailure.class)
    
    0 讨论(0)
提交回复
热议问题