Spring-boot return json and xml from controllers

I had the exact same problem and I found the solution on Spring documentation website : here

In synthesis, I added the following dependency to the pom.xml of my project :

<dependency>
     <groupId>com.fasterxml.jackson.dataformat</groupId>
     <artifactId>jackson-dataformat-xml</artifactId>
 </dependency>

Then I added the following code block to the class that the service had to return :

 import javax.xml.bind.annotation.XmlRootElement;

 @XmlRootElement
 public class Greeting {...}

And it worked.


It may be better to create a new class:

public class SdnSearchResult {
  private List<Sdn> sdns;
  ...
}

Then, a slight change will be required to the existing classes as follows:

public interface SdnSearchService {
  SdnSearchResult find(SdnSearch sdnSearch);
}

@Controller
public class UISearchController {
  @Autowired
  private SdnSearchService sdnSearchService;

  @RequestMapping("/search")
  public ModelAndView search(@ModelAttribute SdnSearch sdnSearch) {
    return new ModelAndView("pages/search/results", "sdns", sdnSearchService.find(sdnSearch).getSdns());
  }
}

Once this is done, the other controller must be coded as:

@Controller
public class RemoteSearchController {
  @Autowired
  private SdnSearchService sdnSearchService;

  @RequestMapping("/remote/search")
  @ResponseBody
  public SdnSearchResult search(@RequestBody SdnSearch sdnSearch) {
    return sdnSearchService.find(sdnSearch);
  }
}

A quick explanation of the changes from your code:

  1. @RequestBody will automatically deserialize the entire HTTP request body to an SdnSearch instance. External applications will typically submit the request data as HTTP body, so @RequestBody will ensure that the deserialization to Java object happens automatically.
  2. @ResponseBody will automatically serialize the return value according to the external client's capabilities and the libraries available on the classpath. If Jackson is available on the classpath and the client has indicated that they can accept JSON, the return value will be automatically sent as JSON. If the JRE is 1.7 or higher (which means that JAXB is included with the JRE) and the client has indicated that they can accept XML, the return value will be automatically sent as XML.
  3. List<Sdn> needs to be changed to SdnSearchResult to ensure that the application can exchange JSON, XML, RSS and ATOM formats with a single controller method, since XML (and XML based formats) require a root-tag on the output, which a List<Sdn> cannot be translated to.

Once these changes are done, fire up a REST client such as the Postman extension for Chrome and submit a request to /remote/search with the following information:

  1. Request header Accepts set to application/json.
  2. Request header Content-Type set to application/json.
  3. Request body set to the JSON string { "sdnName" : "Victoria", "address" : "123 Maple Ave" }.

This will give you a JSON response.


SOLUTION: I used a combination of both answers below (thank you very much!). I am posting here in case anyone else needs help.

My modified controller:

@Controller
public class RemoteSearchController {

    @Autowired
    private SdnSearchService sdnSearchService;

    @RequestMapping(value = "/remote/search", method = RequestMethod.GET, produces = { "application/xml", "text/xml" }, consumes = MediaType.ALL_VALUE )
    @ResponseBody
    public SdnSearchResults search(@ModelAttribute SdnSearch sdnSearch) {
        List<Sdn> foundSdns = sdnSearchService.find( sdnSearch );
        SdnSearchResults results = new SdnSearchResults();
        results.setSdns( foundSdns );
        return results;
    }
}

And on my client, I set the request headers:

Content-type: application/text Accept: text/xml I think ultimately the problem was that my client headers were not being set correctly, so I may not have had to make some of these changes. But I liked the idea of a SearchResults class containing a list of results:

@XmlRootElement
public class SdnSearchResults {
    private List<Sdn> sdns;
...
}