Search code examples
javaspringspring-bootjacksonfasterxml

Jackson mapper skip class name (deserialization issue)


I have this example class:

@Getter
@Setter
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
public class ExampleClass implements Serializable {
    private String name;
    private String color;
}

This class serializes to:

{
    "ExampleClass":{
        "name":"This is some name",
        "color":"This is some color"
}}

Which is the expected behavior and I shouldn't change it. But I am receiving the file first as this json:

{
    "name":"This is some name",
    "color":"This is some color"
}

And I need to map it to my ExampleClass, which usually you would do as simply as:

ExampleClass example = new ObjectMapper().readValue(jsonFile, ExampleClass.class);

But since I have specified the class with annotation @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) it is trying to map the "name" field from the received json to "ExampleClass" and it throws an error:

Could not resolve type id 'name' as a subtype of com.example.package.dto.ExampleClass: known type ids = [ExampleClass]

I know the deserialization will work if I remove @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) or I change it to use = JsonTypeInfo.Id.NONE, but then the serialized json ends up changing so I can't do that.

I also know that if I want to add a custom deserializer I will also need to add a custom serializer, since @JsonTypeInfo annotation makes jackson ignore any deserializer you have specified in the annotation. But I really would want to avoid this case as the actual class has a lot of fields and the custom mapping classes will look very ugly.

How can I tell jackson to skip the "ExampleClass" field and to map it to it's inner fields, or specify different?

I am using fasterxml.jackson, no codehaus.


Solution

  • Funny enough as I was writing down my question I came up with the solution, just had to implement it and try it out and it ended up working perfectly!

    Here is how I did it:

    @Getter
    @Setter
    @EqualsAndHashCode
    @NoArgsConstructor
    @AllArgsConstructor
    // no more @JsonTypeInfo here
    public class ExampleClass implements Serializable {
        private String name;
        private String color;
    }
    

    I removed the @JsonTypeInfo from ExampleClass as this class will be used for deserializing the received json to it. Then I created this second "wrapper" class that I will be using for serializing the data:

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
    public class ExampleClassWrapper extends ExampleClass {
        public ExampleClassWrapper(ExampleClass ex) {
            super(ex.getName(), ex.getColor());
        } 
    

    Now when the json comes I can read it in one line with no erros:

    ExampleClass example = new ObjectMapper().readValue(jsonFile, ExampleClass.class);
    

    And when I need to serialize this data (for example to send it as a request) I just send the ExampleClassWrapper:

    ExampleClassWrapper wrapper = new ExampleClassWrapper(example);
    exampleRestService.sendExampleRequest(wrapper);
    

    Feel free to correct me if you think there is a better way to solve this.

    P.S. HURRAY for the debugging duck that is writing questions in stackoverflow