Spring/json: Преобразование типизированной коллекции, например List <MyPojo>
Я пытаюсь упорядочить список: List<Pojo>
объектов через шаблон Spring Rest.
Я могу передать простые объекты Pojo
, но я не могу найти документацию, описывающую, как отправить объекты List<Pojo>
.
Spring использует Jackson JSON для реализации HttpMessageConverter
. Документация Jackson описывает это:
В дополнение к привязке к POJO и "простых" типов, есть один дополнительный вариант: привязка к общие (типизированные) контейнеры. Это дело требует специальной обработки из-за так называемый тип Erasure (используемый Java для реализации дженериков в несколько обратный совместимый способ), который препятствует вам использовать что-то вроде Collection<String>.class
(что делает не компилировать).
Итак, если вы хотите привязать данные к Map<String,User>
вам нужно будет использовать:
Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() {});
где TypeReference
требуется только определение общего типа (через любой внутренний класс в этом случае): важная часть <Map<String,User>>
, который определяет тип для привязки к.
Можно ли это сделать в шаблоне Spring? Я взглянул на код, и это делает меня не так, но, может быть, я просто не знаю какой-то трюк.
Решение
Конечным решением, благодаря полезным ответам ниже, было не отправлять список, а скорее отправить один объект, который просто расширяет список, например: class PojoList extends ArrayList<Pojo>
. Spring может успешно маршалировать этот объект, и он выполняет то же самое, что и отправку List<Pojo>
, хотя он будет немного менее чистым от решения. Я также разместил JIRA в Spring, чтобы устранить этот недостаток в интерфейсе HttpMessageConverter
.
Ответы
Ответ 1
Если я правильно прочитал docs для MappingJacksonHttpMessageConverter
, вам нужно будет создать и зарегистрировать подкласс MappingJacksonHttpMessageConverter
и переопределить getJavaType(Class<?>)
метод:
Возвращает JavaType Jackson для определенного класса. Реализация по умолчанию возвращается TypeFactory.type(java.lang.reflect.Type), но это можно переопределить в подклассы, чтобы общая обработка коллекции. Для пример:
protected JavaType getJavaType(Class<?> clazz) {
if (List.class.isAssignableFrom(clazz)) {
return TypeFactory.collectionType(ArrayList.class, MyBean.class);
} else {
return super.getJavaType(clazz);
}
}
Ответ 2
В Spring 3.2 теперь поддерживаются общие типы, используя новые exchange()
-методы на RestTemplate
:
ParameterizedTypeReference<List<MyBean>> typeRef = new ParameterizedTypeReference<List<MyBean>>() {};
ResponseEntity<List<MyBean>> response = template.exchange("http://example.com", HttpMethod.GET, null, typeRef);
Работает как шарм!
Ответ 3
Один из способов обеспечить включение общих параметров типа - это фактически тип подкласса или тип карты, так что у вас есть что-то вроде:
static class MyStringList extends ArrayList<String> { }
и вернуть экземпляр этого списка.
Так почему же это имеет значение? Поскольку информация общего типа сохраняется только в нескольких местах: метод и объявления полей, а также объявления типа супер. Таким образом, в то время как "raw" List не содержит никакой информации о типе времени выполнения, определение класса "MyStringList" делает через свои объявления супертипа.
Обратите внимание, что присваивания, казалось бы, типизированных переменных не помогают: он просто создает больше синтаксического сахара времени компиляции: информация о реальном типе передается только с экземплярами класса (или расширениями, предоставляемыми lib, такими как JavaType и TypeReference в случае с Джексоном).
Кроме этого, вам нужно будет выяснить, как передать Джексону либо JavaType
, либо TypeReference
для сопровождения значения.