Search code examples
javajsonjacksonjackson-databind

Can a non-nested value be deserialized using a custom Jackson JsonDeserializer?


I'm working with a project that is using a custom Jackson JsonDeserializer to deserialize an object with a field containing a third-party type from JSON that cannot be deserialized directly. This deserializer is used by annotating the field with @JsonDeserialize(using = CustomTypeDeserializer.class).

public static class OuterType {
    @JsonDeserialize(using = CustomTypeDeserializer.class)
    private CustomType customType;

    public CustomType getCustomType() { return customType; }
    public void setCustomType(CustomType customType) { this.customType = customType; }
}

This works fine when the type to be deserialized is a property of another type, since there is a place to put the @JsonDeserialize annotation. One thing it does not work for is deserializing the type directly.

new ObjectMapper().readValue(json, CustomType.class); // Doesn't work.

It doesn't look like I can use the deserializer directly, as it does not appear to be written in a way that it can directly deserialize values outside of the context of an ObjectMapper.

Note that the type to be deserialized is a third-party type, and therefore I cannot modify it with Jackson annotations.

Is there a way to deserialize a non-nested type using a custom deserializer?


Solution

  • One approach would be to write a custom Module containing the deserializer to the module, and add the module to the ObjectMapper.

    public static class CustomTypeModule extends SimpleModule {
        public CustomTypeModule() {
            addDeserializer(CustomType.class, new CustomTypeDeserializer());
        }
    }
    
    JsonMapper objectMapper = JsonMapper.builder().addModule(new CustomTypeModule()).build();
    CustomType value = objectMapper.readValue(json, CustomType.class);
    

    If you don't want to modify your global application ObjectMapper (making the module available to all requests), you can apply this to a copy of the ObjectMapper used just for this operation.

    JsonMapper objectMapper = JsonMapper.builder().build();
    
    // ...
    
    JsonMapper customTypeMapper = objectMapper.rebuild().addModule(new CustomTypeModule()).build();
    // OR
    ObjectMapper customTypeMapper = objectMapper.copy().registerModule(new CustomTypeModule())
    
    CustomType value = customTypeMapper.readValue(json, CustomType.class);