Класс-оболочка маршалинга Jackson2 с обобщениями

#java #jackson

#java #джексон

Вопрос:

Например, у меня есть объект-оболочка json

 {
  "id": 23,
  "name": "teset",
  "type": "person",
  "_data": {
    "address": 23432
  }
}
 

мой объект Java будет выглядеть следующим образом

 public class Wrapper<D>{
    private Integer id;
    private String type;
    @JsonProperty("_data")
    private D  data;
...
}
 

я не могу найти способ заставить сопоставитель объектов сделать это

Wrapper<Person> wrapped = objectMapper.readValue(jsonStream,Wrapper.class);

это не поддерживается, я не смог найти много информации о дженериках в Jackson.

Ответ №1:

В вашем коде есть несколько проблем:

  • Основная проблема заключается в том, что вы не указываете желаемый параметризованный тип Wrapper в своем readValue вызове. Вы можете исправить это, используя (упрощенную форму): Wrapper<Person> wrapped = om.readValue(json, new TypeReference<Wrapper<Person>>() {});
  • Кроме того, ваш JSON содержит name свойство, которого, по-видимому, нет в вашем Wrapper классе. Он либо у вас есть, но вы его не опубликовали, либо вы можете настроить ObjectMapper его так, чтобы игнорировать неизвестные свойства: objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Вот пример:

 public static class Wrapper<D> {
    // making fields public for simplicity, 
    // use public getters and private fields of course
    public Integer id;
    public String type;
    @JsonProperty("_data")
    public D  data;
}
public static class Person {
    // adding address field as a public int, 
    // same as above, encapsulate properly in real life
    public int address;
}
 

Затем где-нибудь в основном методе…

 ObjectMapper om = new ObjectMapper();
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

// your example JSON
String json = "{"id":23,"name":"test","type":"person","_data":"
  "{"address":23432}}";
Wrapper<Person> wrapped = om.readValue(
    json, new TypeReference<Wrapper<Person>>() {}
);

// printing class/hashCode of the resolved generic type
System.out.println(wrapped.data);
// casting as Person and printing actual property
System.out.println(((Person)wrapped.data).address);
 

Вывод (аналогично …)

 test.Main$Person@dfd3711
23432
 

Объяснение TypeReference из документов:

Этот обобщенный абстрактный класс используется для получения полной информации о типе обобщений путем подклассификации; для использования он должен быть преобразован в реализацию ResolvedType (реализованную JavaType из пакета «databind»). Класс основан на идеях из http://gafter.blogspot.com/2006/12/super-type-tokens.html , Дополнительная идея (из предложения, сделанного в комментариях к статье) состоит в том, чтобы потребовать фиктивной реализации Comparable (подойдет любой такой универсальный интерфейс, если он заставляет реализовывать метод с универсальным типом). чтобы убедиться, что аргумент типа действительно задан.

Использование осуществляется путем подклассирования: вот один из способов создания экземпляра ссылки на список общих типов:

TypeReference ref = new TypeReference<List<Integer>>() { };

который может быть передан методам, которые принимают TypeReference, или разрешен с помощью TypeFactory для получения ResolvedType .

Комментарии:

1. большое спасибо за быстрый и подробный ответ, действительно помог мне!