【问题标题】:Spring REST webservice serializing to multiple JSON formatsSpring REST webservice 序列化为多种 JSON 格式
【发布时间】:2016-01-23 09:59:09
【问题描述】:

我有一个 Spring REST Web 服务,它根据我们在数据库中的数据填充一个通用对象,目标是让用户将参数传递给 Web 服务以指示他们希望输出的格式. 根据他们的输入,我们将使用正确的 JSONSerializer 给他们想要的东西。

我已经如下设置了我的 web 服务,在我的 spring-ws-servlet.xml 我已经设置了我们公司的 ObjectMapper 以供 mvc:message-converters 使用,我还在 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;
    }
}

问题是当我发布我的 webapp 时,第一个查询可以正常工作,如果我指定 format=format1,我将得到 format1 的输出。但是,在那之后我只能收到format1。我可以指定 format=format2,但仍以 format1 获得输出。我认为问题在于 ObjectMapper 仍然从第一个查询中注册了模块。我已经读过我可以通过每次创建一个新的 ObjectMapper 来避免这个问题,但我不确定如何设置它以供 Spring 在输出 JSON 时使用。

有人可以帮我想出一个解决方案,要么在我每次运行代码时创建一个新的 ObjectMapper 并将该 ObjectMapper 设置为 Spring 休息服务,要么帮我弄清楚如何“取消注册”任何已注册的模块在设置最新的所需序列化程序之前在对象映射器上?

【问题讨论】:

  • 你确定这不是缓存相关的吗?如果您通过检查接受标头来选择内容类型,Vary: Accept 可能会阻止这种情况。

标签: spring rest jackson


【解决方案1】:

一个想法可能是在启动时创建和配置您需要的所有映射器作为 spring bean。

然后创建默认对象映射器,它将作为其他对象映射器的调度程序(或作为后备映射器),它可能知道当前的 http 请求。 你可以在这个对象映射器中注册所有的映射器,注册这个映射器作为spring的默认映射器。

可能是这样的:

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

我确信有一种更简洁的方法可以在弹簧流中集成一个类似的解决方案来实现相同的结果,现在找不到更好的方法了。

【讨论】:

    【解决方案2】:

    创建您的 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);
        }
    }
    

    以上代码是我自己的应用程序中的一个 sn-p。我希望它给出了这个想法。

    【讨论】:

      猜你喜欢
      • 2018-10-27
      • 1970-01-01
      • 2017-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-19
      相关资源
      最近更新 更多