Spring、Jackson 和定制(例如 CustomDeserializer)

发布于 2024-09-16 20:17:29 字数 481 浏览 7 评论 0原文

由于对 Spring 还是有点不熟悉,我遇到了一个 问题 这使得有必要为 Jackson 实现一个自定义解串器。该过程在一个小教程中进行了描述,但是,我坚持使用Spring。我不明白,

 ObjectMapper mapper = new ObjectMapper();

当通过控制器类的方法反序列化 json 时,Spring MVC 中是在哪里执行的。所以我不知道该怎么做才能用自定义解串器替换默认解串器。

任何建议都非常欢迎。

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 described in a small tutorial, however, I am stuck with Spring. I do not understand, where

 ObjectMapper mapper = new ObjectMapper();

in Spring MVC is carried out when json is deserializes by a method of a controller class. So I do not know, what to do in order to replace the default deserializer by a custom deserialiser.

Any suggestions most welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

乄_柒ぐ汐 2024-09-23 20:17:29

您没有说明如何在 Spring 中使用 Jackson,所以我假设您通过 @RequestBody< 使用它/code> 和/或 @ResponseBody 注释。

所做的事情之一是注册一个 AnnotationMethodHandlerAdapter bean,它带有许多预配置的 HttpMessageConverter< /code> beans,包括 MappingJacksonHttpMessageConverter,它处理与 Jackson 注解的模型类之间的编组。

现在,MappingJacksonHttpMessageConverter 有一个 setObjectMapper() 方法,它允许您覆盖默认的 ObjectMapper。但由于 MappingJacksonHttpMessageConverter 是由 在幕后创建的,因此您无法访问它。

然而,只是一个方便的捷径。声明您自己的 AnnotationMethodHandlerAdapter bean,将您自己的 MappingJacksonHttpMessageConverter bean 注入其中(通过 messageConverters 属性),然后注入您的自己定制的ObjectMapper到其中。

然后,您会遇到如何构建自定义 ObjectMapper 的问题,因为它不是一个对 Spring 非常友好的类。我建议写你的 自己的 FactoryBean 的简单实现。

所以你最终会得到这样的结果:

<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>

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>
扬花落满肩 2024-09-23 20:17:29

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/

允许您执行以下操作:

<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>

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>
月亮是我掰弯的 2024-09-23 20:17:29

Rakesh 引用的解决方案可能适用于 Spring MVC 3.0,但适用于 3.1 的一些 MVC 基础设施已更改。因此,您可能没有在应用程序上下文中注册 AnnotationMethodHandlerAdapter bean,并且最终会在初始化时出现 BeanCreationException

对于 Spring MVC 3.1,mvc:annotation-driven 元素将创建一个 RequestMappingHandlerAdapter 适合您,因此您应该自动装配该类型。它仍然提供对已注册 HttpMessageConverter 列表的访问,并允许您在 MappingJacksonHttpMessageConverter 上设置 ObjectMapper 属性。这还需要在 init 中进行一些细微的更改。方法到 HttpMessageConverters 引用的类型。

更新后的类如下所示:

@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;
    }
}

更新:事实证明,使用 Spring 3.1 做的最简单的事情就是向 MVC 配置添加一些额外的配置:

<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>

这将添加 MappingJacksonHttpMessageConverter< 的新实例/code> 与任何默认 HttpMessageConverters 之前的自定义 ObjectMapper(由于 register-defaults="true" 而仍然存在)。

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").

水溶 2024-09-23 20:17:29

就我而言(Spring 3.2.4 和 Jackson 2.3.1),自定义序列化器的 XML 配置:

<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>

以无法解释的方式被某些东西覆盖回默认值。

这对我有用:

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;
    }
}

我的解决方案中不需要 XML 配置 ((...)) 。

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.

小鸟爱天空丶 2024-09-23 20:17:29

我希望我能更好地了解 Spring MVC,但是对于像 Jersey 和 RESTeasy 这样的 Jax-RS 实现,需要注册提供者。也许 Spring 也做了类似的事情?

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

淡莣 2024-09-23 20:17:29

MappingJacksonHttpMessageConverter 的 spring 文档状态:

2.4.5 MappingJacksonHttpMessageConverter

一个 HttpMessageConverter 实现,可以使用 Jackson JSON Processor 的 ObjectMapper 读取和写入 JSON。 JSON 映射可以根据需要通过使用 Jackson 提供的注释进行自定义。当需要进一步控制时,可以通过 ObjectMapper 属性注入自定义 ObjectMapper,以应对需要为特定类型提供自定义 JSON 序列化器/反序列化器的情况。默认情况下,此转换器支持 (application/json)。

难道您不能自动连接对 ObjectMapper 的访问以修改它的行为吗?

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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文