Spring REST Web服务序列化为多种JSON格式

时间:2015-10-23 18:56:08

标签: spring rest jackson

我有一个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休息服务,或者帮助我弄清楚如何“注销”任何已注册的模块在设置最新的所需序列化器之前在对象映射器上?

2 个答案:

答案 0 :(得分: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);
    }
}

上面的代码是我自己的应用程序的片段。我希望它能提出这个想法。

答案 1 :(得分: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)..

我确信,在春季流程中集成类似解决方案可以获得相同结果的更简洁方法,现在无法更好地查看。