REST与Spring和Jackson完全数据绑定
我正在使用Spring MVC来处理JSON POST请求。 在封面下面,我使用了构build在Jackson JSON处理器上的MappingJacksonHttpMessageConverter,并在使用mvc:annotation驱动时启用。
我的一项服务收到一个行动清单:
@RequestMapping(value="/executeActions", method=RequestMethod.POST) public @ResponseBody String executeActions(@RequestBody List<ActionImpl> actions) { logger.info("executeActions"); return "ACK"; }
我发现Jackson将requestBody映射到java.util.LinkedHashMap项目列表(简单数据绑定)。 相反,我希望将请求绑定到types化对象列表(在本例中为“ActionImpl”)。
我知道如果直接使用Jackson的ObjectMapper,这很容易:
List<ActionImpl> result = mapper.readValue(src, new TypeReference<List<ActionImpl>>() { });
但是我想知道当使用Spring MVC和MappingJacksonHttpMessageConverter时,最好的方法是什么。 任何提示?
谢谢
我怀疑问题是由于types擦除,即而不是传递generics参数types,也许只有actions.getClass()传递; 这会给types相当于List <?>。
如果这是真的,那么一种可能性是使用中间子类,如:
public class ActionImplList extends ArrayList<ActionImpl> { }
因为即使只有类被传递,这也将保留types信息。 那么:
public @ResponseBody String executeActions(@RequestBody ActionImplList actions)
会做的伎俩。 不是最优的,但应该工作。
我希望有更多的Spring MVC知识的人可以阐明为什么参数types不被传递(也许是一个bug),但至less有一个解决方法。
我发现你也可以通过使用数组作为@RequestBody而不是集合来解决types擦除问题。 例如,以下将工作:
public @ResponseBody String executeActions(@RequestBody ActionImpl[] actions) { //... }
有关您的信息,该function将在Spring 3.2中提供(请参阅https://jira.springsource.org/browse/SPR-9570 )
我只是在当前的M2上进行了testing,它就像一个开箱即用的魅力(不需要提供额外的注释来提供参数化types,它将由新的MessageConverter自动parsing)
这个问题已经很老了,但我想我可以贡献一下。
像StaxMan指出的,这是由于types擦除。 这绝对应该是可能的,因为你可以通过方法定义的reflection来获得generics参数。 但是,问题是HttpMessageConverter的API:
T read(Class<? extends T> clazz, HttpInputMessage inputMessage);
在这里,只有List.class将被传递给方法。 所以,正如你所看到的,通过查看方法参数types来实现一个HttpMessageConverter来计算真实types是不可能的,因为这是不可用的。
不过,可以编写自己的解决方法 – 你只是不会使用HttpMessageConverter。 Spring MVC允许你编写自己的WebArgumentResolver ,它们在标准parsing方法之前开始。 例如,你可以使用自己的自定义注释(@JsonRequestBody?)直接使用ObjectMapper来parsing你的值。 您将能够从方法提供参数types:
final Type parameterType= method.getParameterTypes()[index]; List<ActionImpl> result = mapper.readValue(src, new TypeReference<Object>>() { @Override public Type getType() { return parameterType; } });
TypeReference并不是我想要的,但是ObjectMapper并没有提供更合适的方法。
你有没有试过声明这个方法:
executeActions(@RequestBody TypeReference<List<ActionImpl>> actions)
我还没有尝试过,但基于你的问题,这是我会尝试的第一件事。