Сериализация веб-сервиса Spring REST для нескольких форматов JSON
У меня есть веб-сервис Spring REST, который заполняет общий объект на основе данных, которые мы имеем в базе данных, цель состоит в том, чтобы пользователи передали в веб-сервис параметр, чтобы указать формат, в котором они хотят выводить данные. На основе их ввод мы будем использовать правильный JSONSerializer, чтобы дать им то, что они хотят.
Я настроил свой веб-сервис следующим образом: в своем файле spring-ws-servlet.xml я настроил нашу компанию ObjectMapper для использования в mvc:message-converter, я также установил его на RestController, чтобы он мог настроить ObjectMapper. зарегистрировать сериализатор. Это выглядит так:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="endpoint" class="org.company.Controller">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
<bean id="jacksonObjectMapper" class="org.company.CompanyObjectMapper" />
</beans>
Контроллер выглядит так:
@RestController
public class Controller {
private ObjectMapper objectMapper;
@RequestMapping(...)
public GenericObject getObject(@PathVariables ...) {
//Get Object from database, just creating an object for example
GenericObject object = new GenericObject();
//Based on the user input we will pick out
//a Serializer that extends JsonSerializer<GenericObject>
BaseSerializer serializer = getSerializer();
//Create a simpleModule and use it to register our serializer
SimpleModule module = new SimpleModule();
module.addSerializer(GenericObject.class, serializer);
//get module and register the serializer
ObjectMapper mapper = getObjectMapper();
mapper.registerModule(module);
return object;
}
public ObjectMapper getObjectMapper() {
return objectMapper;
}
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
}
Проблема в том, что когда я публикую свое веб-приложение, первый запрос работает правильно, если я укажу format=format1, я получу вывод в формате format1. Однако после этого я могу получить только format1. Я могу указать format=format2, но все равно получить вывод в format1. Я полагаю, что проблема заключается в том, что ObjectMapper все еще имеет зарегистрированный модуль с первого запроса. Я читал, что могу избежать этой проблемы, каждый раз создавая новый ObjectMapper, но я не уверен, как установить его для использования Spring при выводе JSON.
Может ли кто-нибудь помочь мне найти решение для создания нового ObjectMapper при каждом запуске кода и установки этого ObjectMapper в службу отдыха Spring, или помочь мне выяснить, как я могу "отменить регистрацию" любых модулей, зарегистрированных в объекте маппер перед установкой последнего желаемого сериализатора?
2 ответа
Идея может состоять в том, чтобы создать и настроить все необходимые средства отображения во время запуска в качестве пружинных компонентов.
Затем создайте сопоставитель объектов по умолчанию, который будет работать в качестве диспетчера для других сопоставителей объектов (или в качестве резервного), и он может знать о текущем HTTP-запросе. Вы можете зарегистрировать все преобразователи в этом объектном преобразователе, зарегистрируйте этот преобразователь, чтобы он использовал его по умолчанию весной.
Что-то вроде этого может быть:
public class RequestAwareObjectMapper extends ObjectMapper{
private Map<String, ObjectMapper > mappers = new HashMap<>();
@Override
public String writeValueAsString(Object value) throws JsonProcessingException{
HttpServletRequest req = null;//get request from spring context, if any, this is a managed spring bean it wont be a prorblem
String param = null; // read the param from the query
ObjectMapper mapper = mappers.get(param);
if(mapper == null){
mapper = this;
}
return mapper.writeValueAsString(value);
}
public void registerMapper(String key, ObjectMapper mapper){...}
}
таким образом, вы не собираетесь загрязнять ваш контроллер ссылками на объектный маппер, и вы можете продолжать использовать @ResponseBody (благодаря @RestController)..
Я уверен, что есть более чистый способ достичь того же результата, интегрируя аналогичное решение в поток пружины, и сейчас не могу найти что-то лучшее.
Создайте свой класс customObjectMapper и автоматически подключите его к контроллеру с помощью аннотации @Autowire. Затем вы можете создавать разные методы для создания разных форматированных объектов.
Вы также можете отправить сериализатор в качестве параметров.
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
super();
super.setSerializationInclusion(JsonInclude.Include.ALWAYS);
super.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
..... etc.....
super.setDateFormat(df);
}
public byte[] generateJsonFormat1(Object value, BaseSerializer serializer) throws IOException, JsonGenerationException, JsonMappingException {
Hibernate4Module hm = new Hibernate4Module();
hm.configure(Hibernate4Module.Feature.USE_TRANSIENT_ANNOTATION, false);
hm.configure(Hibernate4Module.Feature.FORCE_LAZY_LOADING, false);
.....
.....
hm.addSerializer(Object.class, serializer);
return super.registerModule(hm).writeValueAsBytes(value);
}
public byte[] generateJsonFormat2(Object value, BaseSerializer serialiser) throws IOException, JsonGe nerationException, JsonMappingException {
SimpleModule sm = new SimpleModule();
sm.addSerializer(Object.class, serialiser);
return super.registerModule(hm).writeValueAsBytes(value);
}
}
Выше код является фрагментом из моего собственного приложения. Я надеюсь, что это дает идею.