Java Spring MVC Rest控制器@RequestBody解析

Java Spring MVC Rest控制器@RequestBody解析,java,json,spring,rest,spring-mvc,Java,Json,Spring,Rest,Spring Mvc,我有一个SpringRESTWeb应用程序,它包含一个通用的rest控制器,如下所示。通过Jackson将对象序列化为JSON,GET方法工作正常。但是,当我尝试调用save方法时,RequestBody参数将转换为LinkedHashMap,而不是T泛型类型定义的类型 @RestController public abstract class CrudAPI<T extends Object, ID extends Serializable>{ @Transactiona

我有一个SpringRESTWeb应用程序,它包含一个通用的rest控制器,如下所示。通过Jackson将对象序列化为JSON,GET方法工作正常。但是,当我尝试调用save方法时,RequestBody参数将转换为LinkedHashMap,而不是T泛型类型定义的类型

@RestController
public abstract class CrudAPI<T extends Object, ID extends Serializable>{

    @Transactional
    @RequestMapping(method = RequestMethod.POST, consumes = "application/json")
    public ResponseEntity<Void> save(@RequestBody T entity){
        service.save(entity);
        return new ResponseEntity(HttpStatus.CREATED);
    }

}
我得到以下错误:

HTTP状态500-请求处理失败;嵌套异常是 org.springframework.beans.NotReadablePropertyException:无效 bean类[java.util.LinkedHashMap]的属性“version”:无法 在回退访问期间查找属性字段

弹簧配置:

@Configuration
@Import(Application.class)
@EnableWebMvc
@ComponentScan(basePackages = {"br.com.doc2cloud"})
public class WebConfig extends WebMvcConfigurerAdapter implements WebApplicationInitializer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    private MappingJackson2HttpMessageConverter jacksonConverter() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Hibernate5Module());
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.setDateFormat(new ISO8601DateFormat());
        mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
        mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();
        jacksonConverter.setObjectMapper(mapper);

        return jacksonConverter;
    }

 @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(jacksonConverter());
        super.configureMessageConverters(converters);
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        servletContext.setInitParameter("javax.servlet.jsp.jstl.fmt.localizationContext", "messages");
        EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);
        FilterRegistration.Dynamic characterEncoding = servletContext.addFilter("characterEncoding", characterEncodingFilter);
        characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
    }
}
@配置
@导入(Application.class)
@EnableWebMvc
@组件扫描(basePackages={“br.com.doc2cloud”})
公共类WebConfig扩展WebMVCConfigureAdapter实现WebApplicationInitializer{
@凌驾
公共无效配置DefaultServletHandling(DefaultServletHandlerConfigurer-configurer){
configurer.enable();
}
私有映射Jackson2HttpMessageConverter jacksonConverter(){
ObjectMapper mapper=新的ObjectMapper();
registerModule(新的Hibernate5Module());
configure(SerializationFeature.WRITE_DATES_作为时间戳,false);
setDateFormat(新的ISO8601DateFormat());
mapper.setVisibility(PropertyAccessor.ALL,Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD,Visibility.ANY);
configure(在未知属性上反序列化feature.FAIL,false);
MappingJackson2HttpMessageConverter jacksonConverter=新的MappingJackson2HttpMessageConverter();
jacksonConverter.setObjectMapper(映射器);
返回插孔转换器;
}
@凌驾

public void configureMessageConverters(列表我认为您无法实现您想要的功能。Java泛型机制仅在编译时起作用。编译后泛型类型被删除并替换为实际类型(
Object
,在您的情况下)。您的控制器无法理解您尝试解析JSON数据的类型


获取
LinkedHashMap
的原因是Jackson在默认情况下将其用于“未知”类型。典型的JSON数据实际上是一个键值映射,Jackson保留属性顺序-这就是为什么使用linked哈希映射的原因。

您使用的是哪个版本的Jackson?我升级到了2.7.3,并且在使用泛型时(我有一个基本控制器,具有用于保存、列出等的通用逻辑)我也有同样的问题。回滚到2.6.5允许我继续使用我的泛型基类。我还没有研究问题的原因,但回滚为我解决了它。

糟糕的风格。控制器永远不应该是
@Transactional
!谢谢你的建议!spring 4.3发布后应该会解决这个问题:完全支持Jackson 2.7的新类型解析算法不完全正确。泛型对象实例的泛型会被删除(例如,你找不到ArrayList实例的泛型类型)。但是,如果你有一个扩展泛型类的类(我假设文章的作者正在这样做,因为他的泛型类是抽象的),在运行时知道该类的泛型类型。这就是为什么像Guava这样的类能够确定有关类的泛型类型的信息。
@Configuration
@Import(Application.class)
@EnableWebMvc
@ComponentScan(basePackages = {"br.com.doc2cloud"})
public class WebConfig extends WebMvcConfigurerAdapter implements WebApplicationInitializer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    private MappingJackson2HttpMessageConverter jacksonConverter() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Hibernate5Module());
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.setDateFormat(new ISO8601DateFormat());
        mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
        mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();
        jacksonConverter.setObjectMapper(mapper);

        return jacksonConverter;
    }

 @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(jacksonConverter());
        super.configureMessageConverters(converters);
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        servletContext.setInitParameter("javax.servlet.jsp.jstl.fmt.localizationContext", "messages");
        EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);
        FilterRegistration.Dynamic characterEncoding = servletContext.addFilter("characterEncoding", characterEncodingFilter);
        characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
    }
}