user1335014
user1335014

Reputation: 431

How to use an explicitly specified marshaller in Spring Boot

I'm trying to create a REST service that is able to produce XML output (I have a custom class that is wrapped inside a HATEOAS object). Mapping is like this:

@GetMapping("/customclass")
Resource<CustomClass> custom() {
    return new Resource<CustomClass>(new CustomClass());
}

Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not marshal [Resource { content: CustomClass(a=10, string=abc), links: [] }]: null; nested exception is javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.internal.SAXException2: class test.CustomClass nor any of its super class is known to this context.
javax.xml.bind.JAXBException: class test.CustomClass nor any of its super class is known to this context.]]

I'm pretty sure that there is nothing wrong with my CustomClass. If I use the following mapping instead

@GetMapping("/customclass")
CustomClass custom() {
    return (new CustomClass());
}

then it works fine.

It also works fine if I try to marshal things manually (by settings things up inside of a main method and then running it). It's also fine then if I wrap the instance of CustomClass inside of a Resource instance.

As far I understand the issue is that the marshaller in SpringApplication is using context that just knows about HATEOAS Resource and I need to some how make it aware of CustomClass.

I tried to use something like this (from https://stackoverflow.com/a/40398632)

@Configuration
public class ResponseResolver  {
    @Bean
    public Marshaller marshaller() {
        try {
            System.out.println("getting marshaller");
            JAXBContext context = JAXBContext.newInstance(CustomClass.class, Resource.class);
            return context.createMarshaller();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }
}

but that didn't work (there was a lot of guessing on my part here, since I don't know that much about the inner workings of Spring Boot).

A promising reply was also in https://stackoverflow.com/a/14073899 , but ContextResolver wasn't in my projects classpath.

I also considered wrapping Resource inside of a another class and then using XmlSeeAlso annotation, but that would mess up my XML and would be somewhat ugly hack.

So is it possible to define a custom JAXBContext that SpringApplication would be able to pick up?

Upvotes: 4

Views: 6427

Answers (1)

Anil
Anil

Reputation: 374

From the Spring Boot Documentation Spring Message message converters

Spring MVC uses the HttpMessageConverter interface to convert HTTP requests and responses. Sensible defaults are included out of the box. For example, objects can be automatically converted to JSON (by using the Jackson library) or XML (by using the Jackson XML extension, if available, or by using JAXB if the Jackson XML extension is not available). By default, Jaxb2RootElementHttpMessageConverter – converts Java objects to/from XML (added only if JAXB2 is present on the classpath)

Custom Converters Configuration

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(
      List<HttpMessageConverter<?>> converters) {

        messageConverters.add(createXmlHttpMessageConverter());
        messageConverters.add(new MappingJackson2HttpMessageConverter());
    }
    private HttpMessageConverter<Object> createXmlHttpMessageConverter() {
        MarshallingHttpMessageConverter xmlConverter = 
          new MarshallingHttpMessageConverter();

        XStreamMarshaller xstreamMarshaller = new XStreamMarshaller();
        xmlConverter.setMarshaller(xstreamMarshaller);
        xmlConverter.setUnmarshaller(xstreamMarshaller);

        return xmlConverter;
    }
}

Upvotes: 2

Related Questions