Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson - @JsonTypeInfo property is being mapped as null?

I have this response:

{  
    "id":"decaa828741611e58bcffeff819cdc9f",
    "statement":"question statement",
    "exercise_type":"QUESTION"
}

Then, based on exercise_type attribute, I want to instantiate different objects instances (subclasses of ExerciseResponseDTO), so I create this mix in:

@JsonTypeInfo(  
    use = JsonTypeInfo.Id.NAME,  
    include = JsonTypeInfo.As.PROPERTY,  
    property = "exercise_type")  
@JsonSubTypes({  
    @Type(value = ExerciseChoiceResponseDTO.class, name = "CHOICE"),  
    @Type(value = ExerciseQuestionResponseDTO.class, name = "QUESTION")})  
public abstract class ExerciseMixIn  
{}  

public abstract class ExerciseResponseDTO {

    private String id;
    private String statement;
    @JsonProperty(value = "exercise_type") private String exerciseType;

    // Getters and setters 
}

public class ExerciseQuestionResponseDTO
    extends ExerciseResponseDTO {}

public class ExerciseChoiceResponseDTO
    extends ExerciseResponseDTO {}

So I create my ObjectMapper as follows

ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(ExerciseResponseDTO.class, ExerciseMixIn.class);

My test:

ExerciseResponseDTO exercise = mapper.readValue(serviceResponse, ExerciseResponseDTO.class)
Assert.assertTrue(exercise.getClass() == ExerciseQuestionResponseDTO.class); // OK
Assert.assertEquals("decaa828741611e58bcffeff819cdc9f" exercise.getId()); // OK
Assert.assertEquals("question statement", exercise.getStatement()); // OK
Assert.assertEquals("QUESTION", exercise.getExerciseType()); // FAIL. Expected: "QUESTION", actual: null 

The problem is that, for some reason, the exercise_type attribute being used as property on @JsonTypeInfo is being mapped as null. Any idea how i can solve this?

like image 863
jscherman Avatar asked Nov 09 '15 14:11

jscherman


People also ask

What is jackson annotation?

Jackson Annotations - @JsonCreator @JsonCreator is used to fine tune the constructor or factory method used in deserialization. We'll be using @JsonProperty as well to achieve the same. In the example below, we are matching an json with different format to our class by defining the required property names.

What is JsonSubTypes?

@JsonSubTypes – indicates sub-types of the annotated type. @JsonTypeName – defines a logical type name to use for annotated class.

What is @JsonTypeInfo?

@JsonTypeInfo is used to indicate details of type information which is to be included in serialization and de-serialization.


2 Answers

Finally, I've found the solution in the API Doc

Note on visibility of type identifier: by default, deserialization (use during reading of JSON) of type identifier is completely handled by Jackson, and is not passed to deserializers. However, if so desired, it is possible to define property visible = true in which case property will be passed as-is to deserializers (and set via setter or field) on deserialization.

So the solution was simply adding the 'visible' attribute as follows

@JsonTypeInfo(  
    use = JsonTypeInfo.Id.NAME,  
    include = JsonTypeInfo.As.PROPERTY,  
    property = "exercise_type",
    visible = true)  
@JsonSubTypes({  
    @Type(value = ExerciseChoiceResponseDTO.class, name = "CHOICE"),  
    @Type(value = ExerciseQuestionResponseDTO.class, name = "QUESTION")})  
public abstract class ExerciseMixIn  
{}  

Hope this helps someone else.

like image 132
jscherman Avatar answered Nov 01 '22 16:11

jscherman


As per @jscherman answer by setting, 'visible' true in JsonTypeInfo will help in accessing exercise_type as a field.

If you use the same class to serialize also then resulting JSON will have exercise_type appear twice. So it's better to also update include to JsonTypeInfo.As.EXISTING_PROPERTY

And it's also worth looking at all other options for include.

like image 6
Shashank Avatar answered Nov 01 '22 16:11

Shashank