Как десериализовать универсальный тип с Джексоном? - PullRequest
0 голосов
/ 15 апреля 2019

Я пробовал много решений, но мой случай кажется особенным.@JsonProperty должно зависеть от типа класса:

У меня есть JSON для двух сущностей:

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

"Автомобиль": [{"id": 5787544, "brand": "Toyota", "number": 12454}]

Объекты выглядят так:

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
}

Общий класс:

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

Основной класс:

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

КогдаЯ отлаживаю, я обнаружил, что списки внутри GenericEntity всегда нулевые.Я не знаю, как динамически установить jsonProperty в верхней части списка внутри GenericEntity.

Также я использовал:

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

И:

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

Бу я получил это:

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)

1 Ответ

2 голосов
/ 15 апреля 2019

Самый простой вариант - использовать тип-оболочку с отдельным полем для каждого типа коллекции, например:

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

Таким образом, вы всегда будете заполнять один из этих списков (согласно нашему разговору в комментариях). Это будет хорошо работать, если у вас не слишком много типов и оно не меняется слишком часто:)

Более продвинутый способ - использовать специальный десериализатор, например:

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

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

Сам десериализатор должен был бы создать GenericEntity сам по себе, но он может делегировать всю работу по десериализации определенного типа другим десериализаторам (поэтому наша работа заключается в том, чтобы просто сказать ему, что десериализовать и к какому типу) :

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!");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...