Tharsan Sivakumar
Tharsan Sivakumar

Reputation: 6531

Jackson deserialize child JSON to parent attribute

This is my requirement. I have below POJO.

class Car {
  private String brandName;
  private String color;
  private Model model;
}

class Model {
 private String modelName;
 private String year;
}

If I get input json like below, then it should be de-serialized and mapped to both classes.

String json = "{\"brandName\" : \"Toyoto\", \"color\" : \"Silver\", \"model\" : {\"modelName\": \"Corolla\", \"year\": \"2019\"}}"
ObjectMapper mapper = new ObjectMapper();
Car car = mapper.readValue(json, Car.class);
assertEquals("Corolla", car.getModel().getModelName()); 

This case is fine.

But if I pass child json, that also should work without changing mapping class.

String json = "{\"modelName\": \"Corolla\", \"year\": \"2019\"}"
ObjectMapper mapper = new ObjectMapper();
Car car = mapper.readValue(json, Car.class);
assertEquals("Corolla", car.getModel().getModelName()); 

Any idea to solve this problem

Upvotes: 4

Views: 2777

Answers (1)

zaerymoghaddam
zaerymoghaddam

Reputation: 3127

You can implement your custom deserializer for Car class like:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

/**
 * @author Ehsan Zaery Moghaddam
 */
public class CarDeserializer extends StdDeserializer<Car> {

    public CarDeserializer() {
        this(null);
    }

    public CarDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Car deserialize(JsonParser jp, DeserializationContext dctx)
            throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        Car c = new Car();
        Model cModel = new Model();

        if(node.has("brandName")) {
            //  the JSON string contains both car and model details
            c.setBrandName(node.get("brandName").asText());
            c.setColor(node.get("color").asText());

            JsonNode modelNode = node.get("model");
            cModel.setModelName(modelNode.get("modelName").asText());
            cModel.setYear(modelNode.get("year").asText());
        } else {
            // the JSON string just has model details
            cModel.setModelName(node.get("modelName").asText());
            cModel.setYear(node.get("year").asText());
        }

        c.setModel(cModel);

        return c;
    }
}

and when you're going to call the Jackson API to do the actual deserialization, register your deserializer in advance:

String json = "{\"modelName\": \"Corolla\", \"year\": \"2019\"}";

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Car.class, new CarDeserializer());
mapper.registerModule(module);

Car car = mapper.readValue(json, Car.class);
assertEquals("Corolla", car.getModel().getModelName()); 

This doesn't require to alter your POJOs. However, if you could do that, you had an option to register your custom deseriallizers using an annotation in your POJO class as below:

@JsonDeserialize(using = CarDeserializer.class)
public class Car { ... }

Upvotes: 2

Related Questions