Home > database >  processing json array containing more than one object class with Jackson
processing json array containing more than one object class with Jackson

Time:11-13

I am receiving from the web service JSON array that contains 2 types of objects - base class and class extending the base (it contains some extra attributes). How can I process it with Jackson that will produce at the end a list of base class in Java. For example, I have a class Car and class Truck which contains attribute maxLoad (and some others) that Car does not have. At the end I need to get List<Car> that will contain instances of both Car and Truck. Here is the code:

public class Car {
    private String make;
    private String model;
    private short  year;
    private String bodyStyle;
    private String engineType;
    private int    horsepower;
// setters and getters are omitted
}

public class Truck extends Car {
    private double  maxLoad;
    private double  clearance;
// setters and getters are omitted
}

And here is the test JSON file and code for test:

  [
    {
        "make": "Ford",
        "model": "Focus",
        "year": 2018,
        "engineType": "T4",
        "bodyStyle": "hatchback",
        "horsepower": 225
    },
    {
        "make": "Toyota",
        "model": "Prius",
        "year": 2021,
        "engineType": "T4",
        "bodyStyle": "hatchback",
        "horsepower": 121        
    },
    {
        "make": "Toyota",
        "model": "RAV4",
        "year": 2020,
        "engineType": "V6",
        "bodyStyle": "SUV",
        "horsepower": 230        
    },
    {
        "make": "Toyota",
        "model": "Tacoma",
        "year": 2021,
        "engineType": "V6",
        "bodyStyle": "pickup",
        "horsepower": 278,
        "maxLoad": 1050,
        "clearance": 9.4
    },
    {
        "make": "Ford",
        "model": "T150",
        "year": 2017,
        "horsepower": 450,
        "bodyStyle": "pickup",
        "maxLoad": 2320,
        "clearance": 8.4
    }   
 ]

public class Test {
    private static final ObjectMapper mapper = new ObjectMapper();

    @SuppressWarnings("CallToPrintStackTrace")
    public static void main(String[] args) {
        InputStream src = Test.class.getClassLoader().getResourceAsStream("cars.json");
        try {
            List<Car> cars = mapper.readValue(src, new TypeReference<List<Car>>() {});
            for (Car car : cars) {
                System.out.println(car.getClass().getName());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

When I run this test program, I am getting an exception:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "maxLoad" (class org.garik.test.Car), not marked as ignorable (6 known properties: "horsepower", "model", "engineType", "year", "make", "bodyStyle"]) at [Source: (BufferedInputStream); line: 33, column: 24] (through reference chain: java.util.ArrayList[3]->org.garik.test.Car["maxLoad"]) at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61) at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:1127) at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:2023) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1700) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1678) at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:320) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:355) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28) at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3674) at org.garik.test.Test.main(Test.java:21)

If I replace List with List it completes without errors, but create me instances of Truck only, but I need in this list instances of both Car and Truck.

CodePudding user response:

As per my understanding, you want Car object only, so need to trim the extra fields in the process. Use this annotation above Car class to ignore extra properties of the json field, as you want the Car object only.

@JsonIgnoreProperties(ignoreUnknown = true) 
public class Car{
 //your fields
 }

CodePudding user response:

Your problem can be solved annotating the Car superclass with the JsonTypeInfo annotation and the JsonSubTypes annotation like below:

@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION, defaultImpl = Car.class)
@JsonSubTypes(@JsonSubTypes.Type(value = Truck.class))
public class Car {/*Car fields */}

public class Truck extends Car {/* specific Truck fields */}

//deserialize to a list containing Car and Truck objects
List<Car> cars = mapper.readValue(json, new TypeReference<List<Car>>() {});

The JsonTypeInfo.Id.DEDUCTION means that jackson tries to deserialize the json object to one of defined subtypes (the only Truck class in this case) and when there is no perfect match with every of the proposed subtypes it tries to serialize the json object to the default class (The Car class).

  • Related