Spring REST 3 to Support XML and JSON

Adi Sembiring picture Adi Sembiring · Jan 16, 2011 · Viewed 38k times · Source

If we develop REST using Spring MVC, it will support XML and JSON data. I have wrote ContentNegotiationViewResorver in my spring config bean app-servlet.xml

<bean
        class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
        p:order="1">
        <property name="mediaTypes">
            <map>
                <entry key="xml" value="application/xml" />
                <entry key="json" value="application/json" />
            </map>
        </property>
        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
                    <property name="marshaller">
                        <bean class="org.springframework.oxm.xstream.XStreamMarshaller"
                            p:autodetectAnnotations="true" />
                    </property>
                </bean>
                <bean
                    class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
            </list>
        </property>
    </bean>

And my spring REST Controller is:

@Controller
@RequestMapping("/rest/customers")
class CustomerRestController {

protected Log log = LogFactory.getLog(CustomerRestController.class);

@RequestMapping(method = POST)
@ResponseStatus(CREATED)
public void createCustomer(@RequestBody Customer customer,
        HttpServletResponse response) {

    log.info(">>>" + customer.getName());
    response.setHeader("Location", String.format("/rest/customers/%s",
            customer.getNumber()));
}


@RequestMapping(value = "/{id}", method = GET)
@ResponseBody
public Customer showCustomer(@PathVariable String id) {
    Customer c = new Customer("0001", "teddy", "bean");
    return c;
}


@RequestMapping(value = "/{id}", method = PUT)
@ResponseStatus(OK)
public void updateCustomer(@RequestBody Customer customer) {
    log.info("customer: " + customer.getName());
}

I set @XStreamAlias("customer") annotation in my customer domain class. But when I try access http://localhost:8080/rest/customers/teddy.xml it always response JSON data.

I set @XmlRootElement(name="customer") annotation in my customer domain class. But when I try access http://localhost:8080/rest/customers/teddy.json it always response XML data.

Is there some thing wrong ?

Answer

Biju Kunjummen picture Biju Kunjummen · Jan 16, 2011

I think "xml" content type should be mapped to "text/xml" not to "application/xml". Also, to force content type resolvers based on extension, you can try to set the "favorPathExtension" property of "ContentNegotiatingViewResolver" to true(though it should have been true by default!)

EDIT: I have now added a working sample at this GIT location - git://github.com/bijukunjummen/mvc-samples.git, if you bring up the endpoint, using mvn tomcat:run, the json is served at http://localhost:8080/mvc-samples/rest/customers/teddy.json and xml at http://localhost:8080/mvc-samples/rest/customers/teddy.xml. This uses JAXB2 not XStream, as I am familiar with JAXB. One thing I noticed was that when my JAXB annotations were not correct in Customer class, Spring was serving out JSON and not XML the way you saw it(You can replicate it by removing the XMLRootElement annotation from Customer class), once I fixed up my annotations, I got back XML as expected. So it could be that there is something wrong with your XStream configuration.

EDIT 2: You are right!! I did not notice, once I got back xml, I assumed that json is working now. I see the problem, in AnnotationMethodHandlerAdapter, the handling for @ResponseBody is a little strange, it completely ignores the ViewResolvers, and uses the registered MessageConverters instead completely bypassing the ContentNegotiatingViewResolver, one workaround for now is to use @ModelAttribute annotation for response, instead of @ResponseBody, this way the view Resolvers are getting called. Try now using the project at [email protected]:bijukunjummen/mvc-samples.git and see if it works for you. This could be a Spring bug, you may try and bring it up in the Spring forum and see what they recommend.