Spring HATEOAS (w Spring Boot) JAXB marshal error when returning a Resources<T> or PagedResources<T> result
Asked Answered
H

3

5

I've got something like this in my controller:

@RequestMapping
@ResponseBody
public HttpEntity<PagedResources<PromotionResource>> promotions(
        @PageableDefault(size = RestAPIConfig.DEFAULT_PAGE_SIZE, page = 0) Pageable pageable,
        PagedResourcesAssembler<Promotion> assembler
){

    PagedResources<PromotionResource> r = assembler.toResource(this.promoService.find(pageable), this.promoAssembler);

    return new ResponseEntity<PagedResources<PromotionResource>>(r, HttpStatus.OK);
}

If i navigate to the URL mapped to that controller method i get a 500 error with a root cause of:

com.sun.istack.internal.SAXException2: unable to marshal type "commerce.api.rest.resources.PromotionResource " as an element because it is missing an @XmlRootElement annotation 

If i throw a @XmlRootElement annotation on my resource it becomes this error:

com.sun.istack.internal.SAXException2: unable to marshal type "commerce.api.rest.resources.PromotionResource " as an element because it is not known to this context.

Everything is fine if the accept header is application/json or application/hal+json. The problem is caused only when the client (in this case chrome) is looking for application/xml (which makes sense as HATEOAS is following the clients requests. I'm using spring boot's @EnableAutoConfiguration which is adding the XML message converter to the list and thus enabling XML content types.

I'm guessing i have at least 2 options: 1. fix the jaxb error 2. remove xml as a supported content type

not sure how to do either, or maybe there's another option.

Hundred answered 5/5, 2014 at 19:45 Comment(1)
See this question.Granth
P
3

If you don't actually want to produce XML try using the produces attribute of the @RequestMapping annotation. Something like: @RequestMapping(produces=MediaType.APPLICATION_JSON_VALUE)

Alternatively you could exclude jaxb from you classpath or look at adding your own org.springframework.boot.autoconfigure.web.HttpMessageConverters bean to take complete control of the registered HttpMessageConverter's. See WebMvcConfigurationSupport.addDefaultHttpMessageConverters to see what Spring will add by default.

Pentosan answered 6/5, 2014 at 8:42 Comment(4)
i do not want to have to annotate all my actions with thatHundred
any change you can be more specific in your answer...i'm trying stuff but there is nightmare chain of message converters going on here...all i want to do is disable 1 specific converter without having to rewrite the worldHundred
OK, it turns out it is actually quite a pain to remove items from HttpMessageConverters with the 1.1.5 release. I've addressed this for 1.1.6 (should be released this week). See github.com/spring-projects/spring-boot/issues/1482 and the test case github.com/spring-projects/spring-boot/blob/…Pentosan
that is easier, check my answer to how i ended up getting it working, although i wasn't confident it was a good solutionHundred
H
1

Not sure this is a good technique, and it looks like in 1.1.6 there's a different approach. Here's what i did:

@Configuration
public class WebMVCConfig extends WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //Remove the Jaxb2 that is automatically added because some other dependency brings it into the classpath
        List<HttpMessageConverter<?>> baseConverters = new ArrayList<HttpMessageConverter<?>>();
        super.configureMessageConverters(baseConverters);

        for(HttpMessageConverter<?> c : baseConverters){
            if(!(c instanceof Jaxb2RootElementHttpMessageConverter)){
                converters.add(c);
            }
        }
    }

}
Hundred answered 2/9, 2014 at 16:34 Comment(0)
W
0

if you don't want to support XML converter, you can extend spring WebMvcConfigurer to exclude XML message converters.

@Configuration
public class WebMVCConfig extends WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(c -> c instanceof AbstractXmlHttpMessageConverter<?>);
    }

}
Wertheimer answered 18/6, 2019 at 6:36 Comment(1)
sure today you can do it this easily...but only because of this question which prompted this enhancement see #23481017Hundred

© 2022 - 2024 — McMap. All rights reserved.