Spring, Jackson and Customization (e.g. CustomDeserializer)

前端 未结 6 1657
情深已故
情深已故 2020-11-30 02:42

Being still a little unfamiliar with Spring, I have encountered a problem that makes it necessary implementing my a custom deserialzer for Jackson. The procedure is describe

相关标签:
6条回答
  • 2020-11-30 03:01

    I wish I knew Spring MVC better, but with Jax-RS implementations like Jersey and RESTeasy, one registers providers. Maybe Spring does something similar?

    0 讨论(0)
  • 2020-11-30 03:02

    In my case (Spring 3.2.4 and Jackson 2.3.1), XML configuration for custom serializer:

    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="false">
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                        <property name="serializers">
                            <array>
                                <bean class="com.example.business.serializer.json.CustomObjectSerializer"/>
                            </array>
                        </property>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    

    was in unexplained way overwritten back to default by something.

    This worked for me:

    CustomObject.java

    @JsonSerialize(using = CustomObjectSerializer.class)
    public class CustomObject {
    
        private Long value;
    
        public Long getValue() {
            return value;
        }
    
        public void setValue(Long value) {
            this.value = value;
        }
    }
    

    CustomObjectSerializer.java

    public class CustomObjectSerializer extends JsonSerializer<CustomObject> {
    
        @Override
        public void serialize(CustomObject value, JsonGenerator jgen,
            SerializerProvider provider) throws IOException,JsonProcessingException {
            jgen.writeStartObject();
            jgen.writeNumberField("y", value.getValue());
            jgen.writeEndObject();
        }
    
        @Override
        public Class<CustomObject> handledType() {
            return CustomObject.class;
        }
    }
    

    No XML configuration (<mvc:message-converters>(...)</mvc:message-converters>) is needed in my solution.

    0 讨论(0)
  • 2020-11-30 03:03

    New way to do this in Spring 3.1:
    http://magicmonster.com/kb/prg/java/spring/webmvc/mvc_spring_config_namespace.html

    http://blog.springsource.org/2011/02/21/spring-3-1-m1-mvc-namespace-enhancements-and-configuration/

    Allows you to do something like this:

    <mvc:annotation-driven>
          <mvc:message-converters>
              <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                  <property name="objectMapper" ref="customObjectMapper"/>
              </bean>
          </mvc:message-converters>
      </mvc:annotation-driven>
    
    0 讨论(0)
  • 2020-11-30 03:08

    The solution referenced by Rakesh likely works with Spring MVC 3.0 but with 3.1 some of the MVC infrastructure has changed. As a result you may not have an AnnotationMethodHandlerAdapter bean registered in your application context and you'll end up with a BeanCreationException at initialization time.

    For Spring MVC 3.1 the mvc:annotation-driven element will create a RequestMappingHandlerAdapter for you, so you should autowire that type instead. It will still provide access to the list of registered HttpMessageConverters and allow you to set the ObjectMapper property on the MappingJacksonHttpMessageConverter. This also requires a slight change within the init. method to the type of the HttpMessageConverters reference.

    The updated class looks like:

    @Component
    public class JacksonFix {
        private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
        private CustomObjectMapper objectMapper;
    
        @PostConstruct
        public void init() {
            List<HttpMessageConverter<?>> messageConverters = requestMappingHandlerAdapter.getMessageConverters();
            for (HttpMessageConverter<?> messageConverter : messageConverters) {
                if (messageConverter instanceof MappingJacksonHttpMessageConverter) {
                    MappingJacksonHttpMessageConverter m = (MappingJacksonHttpMessageConverter) messageConverter;
                    m.setObjectMapper(objectMapper);
                }
            }
        }
    
        // this will exist due to the <mvc:annotation-driven/> bean
        @Autowired
        public void setRequestMappingHandlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
            this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
        }
    
        @Autowired
        public void setObjectMapper(CustomObjectMapper objectMapper) {
            this.objectMapper = objectMapper;
        }
    }
    

    UPDATE: It turns out the absolute easiest thing to do with Spring 3.1 is to add some additional configuration to your MVC configuration:

    <mvc:annotation-driven conversion-service="applicationConversionService">
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper" ref="customObjectMapper" />
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    

    That will add a new instance of MappingJacksonHttpMessageConverter with the custom ObjectMapper before any of the default HttpMessageConverters (which are still present due to register-defaults="true").

    0 讨论(0)
  • 2020-11-30 03:14

    The spring docs for MappingJacksonHttpMessageConverter state:

    2.4.5 MappingJacksonHttpMessageConverter

    An HttpMessageConverter implementation that can read and write JSON using Jackson JSON Processor's ObjectMapper. JSON mapping can be customized as needed through the use of Jackson's provided annotations. When further control is needed, a custom ObjectMapper can be injected through the ObjectMapper property for cases where custom JSON serializers/deserializers need to be provided for specific types. By default this converter supports (application/json).

    Couldn't you just autowire access to the ObjectMapper in order to modify it's behaviour?

    0 讨论(0)
  • 2020-11-30 03:16

    You don't say how you're using Jackson in Spring, so I'll assume you're using it through <mvc:annotation-driven/> and the @RequestBody and/or @ResponseBody annotations.

    One of the things that <mvc:annotation-driven/> does is to register a AnnotationMethodHandlerAdapter bean which comes with a number of pre-configured HttpMessageConverter beans, including MappingJacksonHttpMessageConverter, which handles marshalling to and from Jackson-annotated model classes.

    Now MappingJacksonHttpMessageConverter has a setObjectMapper() method, which allows you to override the default ObjectMapper. But since MappingJacksonHttpMessageConverter is created behind the scenes by <mvc:annotation-driven/>, you can't get to it.

    However, <mvc:annotation-driven/> is just a convenient short-cut. It's just as a valid to declare your own AnnotationMethodHandlerAdapter bean, injecting into it your own MappingJacksonHttpMessageConverter bean (via the messageConverters property), and injecting your own customized ObjectMapper into that.

    You then have the problem of how to build a custom ObjectMapper, since it's not a very Spring-friendly class. I suggest writing your own simple implementation of FactoryBean.

    So you'd end up with something like this:

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
       <property name="messageConverters">
          <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
             <property name="objectMapper">
                <bean class="com.x.MyObjectMapperFactoryBean"/>
             </property>
          </bean>
       </property>
    </bean>
    
    0 讨论(0)
提交回复
热议问题