REST с полной привязкой данных Spring и Jackson - PullRequest
38 голосов
/ 14 декабря 2010

Я использую Spring MVC для обработки запросов JSON POST.Под обложками я использую MappingJacksonHttpMessageConverter, построенный на процессоре Jackson JSON и включенный при использовании mvc: annotation-driven.

Одна из моих служб получает список действий:

@RequestMapping(value="/executeActions", method=RequestMethod.POST)
    public @ResponseBody String executeActions(@RequestBody List<ActionImpl> actions) {
        logger.info("executeActions");
        return "ACK";
    }

Я обнаружил, что Джексон отображает requestBody в список элементов java.util.LinkedHashMap (простая привязка данных).Вместо этого я хотел бы, чтобы запрос был привязан к списку типизированных объектов (в данном случае «ActionImpl»).

Я знаю, что это легко сделать, если вы используете ObjectMapper Джексона напрямую:

List<ActionImpl> result = mapper.readValue(src, new TypeReference<List<ActionImpl>>() { }); 

, но мне было интересно, как лучше всего добиться этого при использовании Spring MVC и MappingJacksonHttpMessageConverter.Есть подсказки?

Спасибо

Ответы [ 5 ]

42 голосов
/ 10 мая 2012

Я обнаружил, что вы также можете обойти проблему стирания типов, используя массив как @RequestBody вместо коллекции.Например, будет работать следующее:

public @ResponseBody String executeActions(@RequestBody ActionImpl[] actions) { //... }
27 голосов
/ 19 декабря 2010

Я подозреваю, что проблема связана с стиранием типа, т.е. вместо передачи универсального типа параметра, возможно, передается только actions.getClass ();и это дало бы эквивалент типа List <?>.

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

public class ActionImplList extends ArrayList<ActionImpl> { }

, потому что этосохранить информацию о типе, даже если передан только класс.И тогда:

public @ResponseBody String executeActions(@RequestBody ActionImplList actions)

сделает свое дело.Не оптимально, но должно работать.

Я надеюсь, что кто-то с большим знанием Spring MVC сможет пролить свет на то, почему тип параметра не передается (возможно, это ошибка?), Но, по крайней мере, есть обходной путь.

8 голосов
/ 26 сентября 2012

К вашему сведению, эта функция будет доступна в Spring 3.2 (см. https://jira.springsource.org/browse/SPR-9570)

. Я только что проверил ее на текущем M2, и она работает как чудо из коробки (нет необходимости предоставлять дополнительную аннотацию)чтобы обеспечить параметризованный тип, он будет автоматически разрешен новым MessageConverter)

2 голосов
/ 17 декабря 2011

Этот вопрос уже старый, но я думаю, что могу внести свой вклад в любом случае.

Как указывал StaxMan, это связано с удалением типа.Это определенно должно быть возможным, потому что вы можете получить общие аргументы посредством отражения из определения метода.Однако проблема заключается в API HttpMessageConverter :

T read(Class<? extends T> clazz, HttpInputMessage inputMessage);

Здесь только метод List.class будет передан методу.Таким образом, как вы можете видеть, невозможно реализовать HttpMessageConverter, который вычисляет реальный тип, глядя на тип параметра метода, поскольку он недоступен.

Тем не менее, можно кодировать собственный обходной путь -вы просто не будете использовать HttpMessageConverter.Spring MVC позволяет вам написать свой собственный WebArgumentResolver , который запускается раньше, чем стандартные методы разрешения.Например, вы можете использовать свою собственную пользовательскую аннотацию (@JsonRequestBody?), Которая напрямую использует ObjectMapper для анализа вашего значения.Вы сможете предоставить тип параметра из метода:

final Type parameterType= method.getParameterTypes()[index];
List<ActionImpl> result = mapper.readValue(src, new TypeReference<Object>>() {
    @Override
    public Type getType() {
        return parameterType;
    }
});

Не совсем так, как предполагалось использовать TypeReference, я полагаю, но ObjectMapper не предоставляет более подходящий метод.

0 голосов
/ 15 декабря 2010

Вы пытались объявить метод как:

executeActions(@RequestBody TypeReference<List<ActionImpl>> actions)

Я не пробовал, но по вашему вопросу это первое, что я попытался бы попробовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...