如何在 Spring Boot 2 中使用内容协商建立自定义 xml 序列化

Posted

技术标签:

【中文标题】如何在 Spring Boot 2 中使用内容协商建立自定义 xml 序列化【英文标题】:How to establish a custom xml serialization with content negotiation in spring boot 2 【发布时间】:2019-12-20 05:11:51 【问题描述】:

我正在重构一个 REST-API,并希望向它添加内容协商。到目前为止有效。 现在我尝试为所有请求添加自定义 xml 序列化程序。但它没有或没有正确调用。

问题是,我无权访问需要在导入时对其进行序列化的类 (Catalog.class)。因此我需要一个外部解决方案。注释类不起作用。

我尝试使用ObjectMapper。但这并没有直接帮助我。目标是让 spring boot 在发送响应时自动使用我的 xml 解析器。

我已经尝试了以下方法:

创建了一个 XmlMapper

 @Bean
    public XmlMapper getXmlMapper() 
        XmlMapper mapper = new XmlMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(Catalog.class, new CatalogV2Resource.Serializer());
        mapper.registerModule(module);
        mapper.findAndRegisterModules();
        mapper.setSerializerProvider(new DefaultSerializerProvider.Impl());
        return mapper;
    

尝试使用 RestTemplate 将其添加为默认值

@Bean
    public RestTemplate restTemplate() 
        RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
        List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();

        for (HttpMessageConverter<?> converter : converters) 
            if (converter instanceof MappingJackson2XmlHttpMessageConverter) 
                MappingJackson2XmlHttpMessageConverter jsonConverter = (MappingJackson2XmlHttpMessageConverter) converter;
                jsonConverter.setObjectMapper(xmlMapper);
            
        

        return restTemplate;
    

这是我的序列化器

public static class Serializer extends StdSerializer<com.allianz.kb.Catalog> 

        @Override
        public void serialize(Catalog catalog, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException 
            if (jsonGenerator instanceof ToXmlGenerator) 
                jsonGenerator.writeRaw(new KbParser().toXML(catalog)); //use my own parser
            
        
    

还有一个简化版的控制器。

@PostMapping(value = "/newcatalog", produces = MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Catalog> doPostXML(@RequestParam String name) 

        //removed the stuff here for simplicity reasons
            Catalog catalog = nc.getNewCatalog();
            return ResponseEntity.ok(catalog);
    

我希望,只要收到带有“接受应用程序/xml”的请求,就会自动调用我的解析器。所以我的 XML 解析器被调用来处理 XML 请求,而标准 JSON 解析器被调用来处理 JSON 请求。

对此的任何帮助都非常感谢。 提前非常感谢!

【问题讨论】:

【参考方案1】:

对于自动 XML 序列化和反序列化,您需要注册 MappingJackson2XmlHttpMessageConverter 类型的 bean,如下所示 -

@Bean("mappingJackson2XmlHttpMessageConverter")
public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter()

    return new MappingJackson2XmlHttpMessageConverter(new XmlMapper());

【讨论】:

感谢您的快速回答。不幸的是,我的 XML Parser 没有被调用。我是否必须更改退货声明中的某些内容?我认为spring boot会在'return ResponseEntity.ok(catalog);'上调用相应的映射器; 是的,只要您使用标头Accept: application/xml 发出请求,Spring 应该自动将catalog 序列化为XML。 你得到什么回应? 我得到了整个对象。不是我期待的回应。实际结果是&lt;Catalog&gt; &lt;name/&gt; &lt;caption/&gt; &lt;kbLoader/&gt; &lt;declaredImports/&gt; &lt;declaredComponents/&gt; &lt;allComponents/&gt; &lt;importedComponents/&gt; &lt;/Catalog&gt; 预期结果应该是这样的:&lt;Catalog&gt; &lt;name/&gt; &lt;caption/&gt; &lt;/Catalog&gt; 您可能希望扩展Catalog 并排除带有@JsonIgnoreProperties(value = "kbLoader" ) 的其他属性。见baeldung.com/jackson-ignore-properties-on-serialization【参考方案2】:

我最终使用了以下课程。好像我错过了 @EnableWebMvc 注释以及 implements WebMvcConfigurer

    @Configuration
    @EnableWebMvc
    public class CustomSerializerConfiguration implements WebMvcConfigurer 

        @Bean
        public Module catalogSerializer() 
            SimpleModule module = new SimpleModule();
            module.addSerializer(com.allianz.kb.Catalog.class, new CustomCatalogSerializer(Catalog.class));
            return module;
        

        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) 
            Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
            // more customization available
    //      builder.serializationInclusion(JsonInclude.Include.NON_NULL);
    //      builder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
    //      builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
            builder.modulesToInstall(catalogSerializer());
            converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
            converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
        

    

【讨论】:

以上是关于如何在 Spring Boot 2 中使用内容协商建立自定义 xml 序列化的主要内容,如果未能解决你的问题,请参考以下文章

spring ContentNegotiationManagerFactoryBean 内容协商

如何在 Spring Boot 2.2.6 中提供静态内容?

Spring Boot Cassandra:c.d.o.d.i.core.session.DefaultSession:[s0] 协商的协议版本 V5 而不是 v4

如何在 Spring Boot 中明确检查请求内容类型是不是与实际内容匹配?

spring cloud config-server .properties 内容协商失败

如何在使用 DataJpaTest 的 Spring Boot 2.0 测试中访问 H2 控制台