Search code examples
xmlspring-bootserializationjacksoncontent-negotiation

How to establish a custom xml serialization with content negotiation in spring boot 2


I'm refactoring a REST-API and want to add content negotiation to it. That works so far. Now I tried adding a custom xml serializer for all requests. But it is not or not properly called.

The Problem is, that I do not have access to the Class (Catalog.class) that needs to be serialized as it is imported. Therefore I need an external solution. Annotating the class does not work.

I tried using an ObjectMapper. But that did not help me directly. The goal is to let spring boot automatically use my xml parser whenever a response is send.

I already tried the following:

Created a 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;
    }

tried to add it as default with the 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;
    }

This is my Serializer

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
            }
        }
    }

And a simplified version of the controller.

@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);
    }

I would expect, that my parser is automatically called whenever a request with 'Accept application/xml' is received. So my XML-parser being called for a XML request and the standard JSON parser called for a JSON request.

Any help on this is highly appreciated. Thanks a lot in advance!


Solution

  • I ended up using following class. Seems like I was missing the @EnableWebMvc Annotation as well as the 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()));
            }
    
        }