Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom deserialisation of JSON field using Jackson

I'm using Jackson to deserialize some JSON and I've run into some trouble while trying to use a custom deserializer for one of the fields.

class MyClass
{
    private static class SpecialPropertyDeserializer extends JsonDeserializer<SpecialProperty>
    {
        @Override
        public SpecialProperty deserialize(JsonParser jsonParser,
                                           DeserializationContext deserializationContext) throws IOException, JsonProcessingException
        {
            // do some custom deserialisation
        }
    }

    private static class SpecialProperty
    {
        private String m_foo;

        private String m_bar;

        @JsonCreator
        SpecialProperty(@JsonProperty("foo") String foo,
                        @JsonProperty("bar") String bar)
        {
            m_foo = foo;
            m_bar = bar;
        }
    }

    private String m_identifier;

    private String m_version;

    @JsonDeserialize(using = SpecialPropertyDeseializer.class)
    private SpecialProperty m_specialProperty;

    @JsonCreator
    MyClass(@JsonProperty("identifier") String identifier,
            @JsonProperty("version") String version,
            @JsonProperty("specialProperty") SpecialProperty specialProperty)
    {
        m_identifier = identifier;
        m_version = version;
        m_specialProperty = specialProperty;
    }
}

and this is the JSON I want to deserialize:

{
    "identifier" : "some-id",
    "version"    : "1.7",
    "specialProperty"    : {
        "foo" : "str1",
        "bar" : "str2"
    },
}

I invoke the mapper as follows:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);   
return objectMapper.readValue(input, MyClass.class);

I've observed the following behaviour:

  1. Without a special property it all works fine - i.e. remove all references to SpecialProperty from the code and the JSON.
  2. If I include SpecialProperty in the JSON but remove the custom deserializer for it then it also works fine. The ctor for SpecialProperty is called.
  3. With the custom deserializer it doesn't work. The ctor for SpecialProperty is called but the custom deserializer is not.

What am I doing wrong?

like image 480
ksl Avatar asked Nov 07 '22 21:11

ksl


1 Answers

@JsonDeserialize annotation can be placed on a field, a setter or a class. Jackson will take it into account if what is annotated is what it uses to set the value.

E.g.1 It will notice @JsonDeserialize over a setter if it uses the setter to set the value of a field.

E.g.2 It will notice @JsonDeserialize over a field if it directly sets this field without using a setter or a constructor.

It will tend to take it into account if it's on a class unless it's overridden by a more specific annotation on a field or setter docs. I reckon the docs could be clearer on the above details.

In your case you have the annotation over the SpecialProperty field but you are setting this field in the MyClass constructor so it's ignored.

In this case you can move @JsonDeserialize over the class instead of over the field. That's probably the simplest solution in your case. E.g.

@JsonDeserialize(using = MyClass.SpecialPropertyDeserializer.class)
private static class SpecialProperty {

Or you can skip the annotation altogether and register the deserializer on the mapper. First make SpecialProperty and SpecialPropertyDeserializer non private in MyClass and then:

ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(MyClass.SpecialProperty.class, new MyClass.SpecialPropertyDeserializer());
objectMapper.registerModule(module);

You can also get rid of constructor of MyClass and the current annotation over the SpecialProperty field will be taken into account.

like image 172
Manos Nikolaidis Avatar answered Nov 14 '22 20:11

Manos Nikolaidis