Spring, Джексон и настройка (например, CustomDeserializer)
Будучи еще немного незнакомым с Spring, я столкнулся с проблемой , что делает необходимым внедрение моего пользовательского десеризатора для Jackson. Процедура описана в небольшом учебнике, однако я застрял с Spring. Я не понимаю, где
ObjectMapper mapper = new ObjectMapper();
in Spring MVC выполняется, когда json десериализуется с помощью метода класса контроллера. Поэтому я не знаю, что делать, чтобы заменить десериализатор по умолчанию на пользовательский десериализатор.
Любые предложения приветствуются.
Ответы
Ответ 1
Вы не говорите, как вы используете Джексона в Spring, поэтому я предполагаю, что вы используете его через <mvc:annotation-driven/>
и аннотации @RequestBody
и/или @ResponseBody
.
Одна из вещей, которую выполняет <mvc:annotation-driven/>
, - это зарегистрировать AnnotationMethodHandlerAdapter
bean, который поставляется с рядом предварительно настроенных HttpMessageConverter
beans, включая MappingJacksonHttpMessageConverter
, который обрабатывает маршаллинг в и из Джексона -номинированные классы моделей.
Теперь MappingJacksonHttpMessageConverter
имеет метод setObjectMapper()
, который позволяет переопределить значение по умолчанию ObjectMapper
. Но поскольку MappingJacksonHttpMessageConverter
создается за кулисами <mvc:annotation-driven/>
, вы не можете добраться до него.
Однако <mvc:annotation-driven/>
является просто удобным сокращением. Это справедливо для объявления вашего собственного 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>
Ответ 2
Новый способ сделать это в 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>
Ответ 3
Решение, на которое ссылается Rakesh, вероятно, работает с Spring MVC 3.0, но с 3.1 некоторыми из инфраструктуры MVC изменилось. В результате у вас может не быть AnnotationMethodHandlerAdapter
bean, зарегистрированного в вашем контексте приложения, и вы закончите с BeanCreationException
во время инициализации.
Для Spring MVC 3.1 элемент mvc:annotation-driven
создаст для вас RequestMappingHandlerAdapter, поэтому вы должны использовать этот тип вместо этого. Он по-прежнему будет предоставлять доступ к списку зарегистрированных HttpMessageConverters и позволит вам установить свойство ObjectMapper в MappingJacksonHttpMessageConverter
. Это также требует небольшого изменения в пределах 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
с пользовательским ObjectMapper до любого из HttpMessageConverters по умолчанию (которые все еще присутствуют из-за 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 лучше, но с реализациями Jax-RS, такими как Jersey и RESTeasy, один регистрирует поставщиков. Может быть, Spring делает что-то подобное?
Ответ 6
Документы spring для состояния MappingJacksonHttpMessageConverter:
2.4.5 MappingJacksonHttpMessageConverter
Реализация HttpMessageConverter, которая может считывать и записывать JSON с использованием ObjectMapper процессора Jackson JSON. Отображение JSON можно настроить по мере необходимости с помощью аннотаций, предоставленных Джексоном. Когда требуется дополнительный контроль, пользовательский ObjectMapper может быть введен через свойство ObjectMapper для случаев, когда для определенных типов должны быть предусмотрены специализированные сериализаторы/десериализаторы JSON. По умолчанию этот конвертер поддерживает (application/json).
Не удалось ли вам просто открыть доступ к ObjectMapper для изменения его поведения?