简体   繁体   中英

How to deserialize a generic type with jackson?

I tried many solutions, but my case seems special. The @JsonProperty must be depending on class type:

I have JSON for two entities:

"Person": [ { "id": "452009517701", "name": "Perosn1", "address": "541-DPL-355" } ]

"Car": [ { "id": 5787544, "brand": "Toyota", "number": 12454 } ]

The entities look like :

public class Person{
    private String id:
    private String name;
    private String address:
    // Constcutors && Getters && Setters
}

public class Car{
    private Long id:
    private String brand;
    private Long number:
    // Constcutors && Getters && Setters
}

The generic class :

public class GenericEntity<T>{
    //@JsonProperty
    private List<T> myList;
    // Constcutors && Getters && Setters
}

Main class :

public static void main(String[] args) {
        ObjectMapper mapper=new ObjectMapper();
        GenericEntity p=mapper.readValue(personJson,GenericEntity.class);
        GenericEntity c=mapper.readValue(carJson,GenericEntity.class);
    }

When I debug I find that the lists inside GenericEntity are always null. I do not know how to set jsonProperty dynamically on the top of the list inside the GenericEntity .

Also, i used :

Object readValue = mapper.readValue(jsonPerson, new TypeReference<GenericEntity<Person>>() {}); 

And :

JavaType javaType = mapper.getTypeFactory().constructParametricType(GenericEntity.class, Person.class);
        Object readValue =mapper.readValue(jsonPerson, javaType);

Bu i got this :

Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.test.GenericEntity` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('Person')
 at [Source: (String)""Person": [ { "id": "452009517701", "name": "Perosn1", "address": "541-DPL-355" } ]"; line: 1, column: 1]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1032)
    at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)

The simplest option would be to use a wrapper type with a separate field per collection type, like that:

class GenericEntity {
    @JsonProperty("Car") List<Car> car; 
    @JsonProperty("Person") List<Person> person; 
}

This way you would always have one of those lists filled (according to our conversation in comments). This will work fine as long as you don't have too many types and it doesn't change too frequently :)

The more-advanced way would be to use a custom deserializer, like that:

@JsonDeserialize(using = MyDeserializer.class)
class GenericEntity<T> {
    List<T> myList;

    GenericEntity(List<T> myList) {
        this.myList = myList;
    }
}

The deserializer itself would have to create a GenericEntity on its own, but it can delegate all specific-type-deserializing job to other deserializers (so our job would be just to tell it what to deserialize and to what type):

class MyDeserializer extends JsonDeserializer<GenericEntity<?>> {

    @Override
    public GenericEntity<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        ObjectCodec codec = p.getCodec();
        JsonNode node = codec.readTree(p);
        if (node.hasNonNull("Person")) {
            JsonParser nodeParser = node.get("Person").traverse(codec);
            nodeParser.nextToken();
            Person[] people = ctxt.readValue(nodeParser, Person[].class);
            return new GenericEntity<>(asList(people));
        } else if (node.hasNonNull("Car")) {
            JsonParser nodeParser = node.get("Car").traverse(codec);
            nodeParser.nextToken();
            Car[] cars = ctxt.readValue(nodeParser, Car[].class);
            return new GenericEntity<>(asList(cars));
        }
        throw new RuntimeException("Couldn't find a type to deserialize!");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM