Jackson map different attributes based on json in the same class

I have a class Response which has an attribute data.

A json file is mapped to this object. The data attribute can be of type TaskData or SubmitData on the json.

If the json has the object of type TaskData the object mapper must map to TaskData class or should map to `SubmitData' class.

Upvotes: 0

Views: 1628

Answers (2)

dhke
dhke

Reputation: 15388

In addition to the answer by Guillaume Polet, and if you can modify the JSON schema, this can also be done a little bit smoother using Jackson's Polymorphic (de)serialization via annotations:

@JsonTypeInfo(use=JsonTypeInfo.Id.Class, include=JsonTypeInfo.As.PROPERTY, property="@class")
class Data {}

class TaskData extends Data {}
class SubmitData extends Data {}

This will write out the full Java class name as an additional @class property. The json needs to include the @class property on input, however.

Instead of JsonTypeInfo.Id.Class it is also possible to perform explicit naming

@JsonTypeInfo(use=JsonTypeInfo.Id.Class, include=JsonTypeInfo.As.PROPERTY, property="@dataType")
@JsonSubTypes({
    JsonSubTypes.Type(value=TaskData.class, name="task"),
    JsonSubTypes.Type(value=SubmitData.class, name="submit")
})
class Data {}

@JsonTypeName("task")
class TaskData extends Data {}
@JsonTypeName("submit")
class SubmitData extends Data {}

This will yield an additional synthetic field @dataType, which will need to be present in the input.

If you cannot make the type explicit in the input JSON, you will need to stick to the manual approach.

Upvotes: 1

Guillaume Polet
Guillaume Polet

Reputation: 47608

You need to type your Response class as follows: public class Response<T>.

Then, when deserializing the input, provide a TypeReference to jackson to indicate the desired type.

See this example:

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class TestJacksonTyping {

    public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
        ObjectMapper mapper = new ObjectMapper();
        Response<TaskData> taskResponse = new Response<TaskData>();
        TaskData taskData = new TaskData();
        taskData.setTaskTitle("Some title");
        taskResponse.setData(taskData);
        Response<SubmitData> submitResponse = new Response<SubmitData>();
        SubmitData submitData = new SubmitData();
        submitData.setSubmitValue(256);
        submitResponse.setData(submitData);
        StringWriter sw = new StringWriter();
        mapper.writeValue(sw, taskResponse);
        String taskResponseJson = sw.toString();
        mapper.writeValue(sw = new StringWriter(), submitResponse);
        String submitResponseJson = sw.toString();
        Response<TaskData> deserializedTaskResponse = mapper.reader(new TypeReference<Response<TaskData>>() {
        }).readValue(new StringReader(taskResponseJson));
        Response<SubmitData> deserializedSubmitResponse = mapper.reader(new TypeReference<Response<SubmitData>>() {
        }).readValue(new StringReader(submitResponseJson));
        System.out.println(deserializedTaskResponse.getData().getTaskTitle());
        System.out.println(deserializedSubmitResponse.getData().getSubmitValue());

    }

    public static class Response<T> {
        private T data;

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }

    }

    public static class TaskData {
        private String taskTitle;

        public String getTaskTitle() {
            return taskTitle;
        }

        public void setTaskTitle(String taskTitle) {
            this.taskTitle = taskTitle;
        }
    }

    public static class SubmitData {
        private int submitValue;

        public int getSubmitValue() {
            return submitValue;
        }

        public void setSubmitValue(int submitValue) {
            this.submitValue = submitValue;
        }
    }

}

Upvotes: 0

Related Questions