【问题标题】:Spring, Jackson and Customization (e.g. CustomDeserializer)Spring、Jackson 和自定义(例如 CustomDeserializer)
【发布时间】:2011-04-05 05:06:32
【问题描述】:
由于对 Spring 还有些陌生,我遇到了一个 problem,这使得我有必要为 Jackson 实现一个自定义解串器。该过程在一个小的tutorial 中进行了描述,但是,我坚持使用 Spring。我不明白,在哪里
ObjectMapper mapper = new ObjectMapper();
在 Spring MVC 中,当 json 通过控制器类的方法反序列化时执行。所以我不知道该怎么做才能用自定义反序列化器替换默认反序列化器。
欢迎提出任何建议。
【问题讨论】:
标签:
web-services
json
spring
spring-mvc
jackson
【解决方案1】:
你没有说你在 Spring 中是如何使用 Jackson 的,所以我假设你是通过 <mvc:annotation-driven/> 和 @RequestBody 和/或 @ResponseBody 注释来使用它的。
<mvc:annotation-driven/> 所做的一件事是注册一个 AnnotationMethodHandlerAdapter bean,它带有许多预配置的 HttpMessageConverter bean,包括 MappingJacksonHttpMessageConverter,它处理与 Jackson 注释的模型类之间的编组.
现在MappingJacksonHttpMessageConverter 有一个setObjectMapper() 方法,它允许您覆盖默认的ObjectMapper。但由于MappingJacksonHttpMessageConverter 是<mvc:annotation-driven/> 在幕后创建的,所以您无法访问它。
但是,<mvc:annotation-driven/> 只是一个方便的捷径。声明您自己的 AnnotationMethodHandlerAdapter bean,将您自己的 MappingJacksonHttpMessageConverter bean 注入其中(通过 messageConverters 属性),并将您自己的自定义 ObjectMapper 注入其中。
然后你就会遇到如何构建自定义ObjectMapper 的问题,因为它不是一个对 Spring 非常友好的类。我建议写你的own simple implementation of 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>
【解决方案3】:
Rakesh 引用的解决方案可能适用于 Spring MVC 3.0,但适用于 3.1 的一些 MVC 基础架构has changed。因此,您可能没有在应用程序上下文中注册 AnnotationMethodHandlerAdapter bean,并且您最终会在初始化时得到 BeanCreationException。
对于 Spring MVC 3.1,mvc:annotation-driven 元素将为您创建一个 RequestMappingHandlerAdapter,因此您应该改为自动装配该类型。它仍将提供对已注册 HttpMessageConverters 列表的访问权限,并允许您在 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>
这将在任何默认 HttpMessageConverters 之前添加一个带有自定义 ObjectMapper 的 MappingJacksonHttpMessageConverter 的新实例(由于 register-defaults="true" 而仍然存在)。
【解决方案4】:
就我而言(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 配置 (<mvc:message-converters>(...)</mvc:message-converters>)。
【解决方案5】:
我希望我更了解 Spring MVC,但是对于像 Jersey 和 RESTeasy 这样的 Jax-RS 实现,需要注册提供程序。也许 Spring 会做类似的事情?
【解决方案6】:
MappingJacksonHttpMessageConverter 状态的 Spring 文档:
2.4.5 映射JacksonHttpMessageConverter
可以使用 Jackson JSON 处理器的 ObjectMapper 读取和写入 JSON 的 HttpMessageConverter 实现。 JSON 映射可以通过使用 Jackson 提供的注释根据需要进行自定义。当需要进一步控制时,可以通过 ObjectMapper 属性注入自定义 ObjectMapper,以应对需要为特定类型提供自定义 JSON 序列化器/反序列化器的情况。默认情况下,此转换器支持 (application/json)。
难道你不能自动访问 ObjectMapper 来修改它的行为吗?