Jackson报错org.codehaus.jackson.map.JsonMappingException

在使用Spring MVC + Jackson-1.9.12时报的一个异常,本文暂时也只是描述一下如何绕开这个异常,并简单说明一下出错原因。希望如果有大神碰巧看过这个问题,不吝赐教。


异常详细描述

主要是Jackson的异常:


org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: No serializer found for class org.springframework.validation.DefaultMessageCodesResolver and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.validation.support.BindingAwareModelMap["org.springframework.validation.BindingResult.GStation"]->org.springframework.validation.BeanPropertyBindingResult["messageCodesResolver"]); nested exception is org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.springframework.validation.DefaultMessageCodesResolver and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.validation.support.BindingAwareModelMap["org.springframework.validation.BindingResult.GStation"]->org.springframework.validation.BeanPropertyBindingResult["messageCodesResolver"])
    org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.writeInternal(MappingJacksonHttpMessageConverter.java:194)
    org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:179)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodInvoker.writeWithMessageConverters(AnnotationMethodHandlerAdapter.java:1037)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodInvoker.handleResponseBody(AnnotationMethodHandlerAdapter.java:995)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodInvoker.getModelAndView(AnnotationMethodHandlerAdapter.java:944)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:441)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:428)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

因为使用Spring MVC框架控制器处理请求,所以同时会报Spring的异常:


org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.springframework.validation.DefaultMessageCodesResolver and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.validation.support.BindingAwareModelMap["org.springframework.validation.BindingResult.GStation"]->org.springframework.validation.BeanPropertyBindingResult["messageCodesResolver"])
    org.codehaus.jackson.map.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:52)
    org.codehaus.jackson.map.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:25)
    org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
    org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
    org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
    org.codehaus.jackson.map.ser.std.MapSerializer.serializeFields(MapSerializer.java:262)
    org.codehaus.jackson.map.ser.std.MapSerializer.serialize(MapSerializer.java:186)
    org.codehaus.jackson.map.ser.std.MapSerializer.serialize(MapSerializer.java:23)
    org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:610)
    org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256)
    org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1613)
    org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.writeInternal(MappingJacksonHttpMessageConverter.java:191)
    org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:179)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodInvoker.writeWithMessageConverters(AnnotationMethodHandlerAdapter.java:1037)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodInvoker.handleResponseBody(AnnotationMethodHandlerAdapter.java:995)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodInvoker.getModelAndView(AnnotationMethodHandlerAdapter.java:944)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:441)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:428)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

代码

 前台代码使用jQuery的ajax异步调用,代码中利用Spring MVC可以直接将data数据赋值到pojo对象属性中。


$.ajax({
    type : "post",
    url : "/businesssite/updStation",
    data : station,
    dataType : "json",
    success : function(data) {
        if(data && data.msg == "s") {
            alert("成功");
        } else {
            alert("失败");
        }
    },
    error : function(e) {
        alert("错误:" + e.responseText);
    }
});

 后台代码Spring MVC的注解定义控制器请求路径及参数,station为需要获取ajax参数的对象,map为需要返回的对象(此处的map默认情况下会是Spring定义的BindingAwareModelMap的实例,Map、Model、ModelMap都是一样的。但由于Model接口没有clear方法,会影响其中一种处理bug的方法,下面会讲到。)


@RequestMapping(value = "/updStation", method = POST)
@ResponseBody
public Map<String, Object> updStation(GStation station, Map<String, Object> map) {
    try {
        log.debug(station);
        map.put("msg", "s");
    } catch (Exception e) {
        log.error(e.getMessage(), e);
        map.put("msg", "e");
    }
    return map;
}

此时就会报出本文开头提到的异常。


现象

 通过打断点,查看进入控制器的方法中各个对象实例的状态,发现正常程序与异常程序之间的区别:


把自定义类换成Java自带的封装类都能够正确运行;

当程序正常运行时,map是空的(不是null)。而异常程序的map包含两个值,一个是GStation对象的值,一个是org.springframework.validation.BindingResult.GStation=org.springframework.validation.BeanPropertyBindingResult: 0 errors。

 有图为证:


        1. 出现异常的代码中map或model的值



Jackson报错org.codehaus.jackson.map.JsonMappingException

        2. 未出现异常的代码中map或model的值

Jackson报错org.codehaus.jackson.map.JsonMappingException

 因此做出大胆假设,出错的原因是ajax传递多值至对象中时,Spring MVC会在map里面增加两个值(一个是传输的数据对象,一个是对于对象属性绑定的验证结果),而这两个值是Jackson将map处理成json格式时出错 。通过Spring文档中的内容也验证了这一说法,内容如下:

Command or form objects to bind request parameters to bean properties (via setters) or directly to fields, with customizable type conversion, depending on@InitBinder methods and/or the HandlerAdapter configuration. See thewebBindingInitializer property on RequestMappingHandlerAdapter. Such command objects along with their validation results will be exposed as model attributes by default, using the command class class name - e.g. model attribute "orderAddress" for a command object of type "some.package.OrderAddress". The ModelAttribute annotation can be used on a method argument to customize the model attribute name used.


所以,只要map是空的(非null)就行了。


解决办法

 既然找到了解决思路,那方法就有多种了:


通过map.clear()方法将map清空(此方法适用于Map及MedalMap);


@RequestMapping(value = "/updStation", method = POST)
@ResponseBody
public Map<String, Object> updStation(GStation station, Map<String, Object> map) {
    map.clear();
    try {
        log.debug(station);
        map.put("msg", "s");
    } catch (Exception e) {
        log.error(e.getMessage(), e);
        map.put("msg", "e");
    }
    return map;
}

不需要Spring帮忙创建map,自己new一个。


@RequestMapping(value = "/updStation", method = POST)
@ResponseBody
public Map<String, Object> updStation(GStation station) {
    Map<String, Object> map = new HashMap<String, Object>();
    try {
        log.debug(station);
        map.put("msg", "s");
    } catch (Exception e) {
        log.error(e.getMessage(), e);
        map.put("msg", "e");
    }
    return map;
}

注:Spring MVC帮助定义的对象是BindingAwareModelMap,通过源码或API可以知道,这个类是LinkedHashMap的子类,也算是HashMap的子类,所以这的地方创建HashMap对象不算错。


后语

 这个异常开始的时候感觉是Jackson的异常,但是起因似乎还是Spring MVC。在Map中多出的数据是Spring MVC对于向对象中赋值时增加的校验信息,当这些校验信息提交给Jackson时,Jackson无法将其转换成json数据,所以报出异常。


 如果有大神路过,跪求指正。


上一篇:在某龙芯平台上编译FFMPEG报错:ERROR: xxx not found using pkg-config


下一篇:《思科数据中心I/O整合》一第2章 相关技术2.1 引言