Mohan February 2016

Accepting / returning XML/JSON request and response - Spring MVC

I need to write a rest service which accepts XML/JSON as a input (POST method) and XML/JSON as a output (based on the input format). I have tried a below approach to achieve this but doesn't helped out.Endpoint method accepts both XML/JSON but while responding it always gives either JSON or XML based on the order specified in @RequestMapping -produces.Any help will be really appreciated.

My endpoint method:

@RequestMapping(value = "/getxmljson", method = RequestMethod.POST,produces={"application/json","application/xml"},
        consumes={"application/json", "application/xml"})
public @ResponseBody Student processXMLJsonRequest(@RequestBody Student student)
        throws Exception {
    System.out.println("*************Inside Controller");
    return student;
}

POJO Class: Student.java

import java.io.Serializable;
import java.util.ArrayList;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@XmlRootElement(name = "student")
@XmlType(propOrder = {"id", "name", "graduationTime", "courses"})
@JsonPropertyOrder({"id", "name", "graduationTime", "courses"})
public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private int id;
    private String name;
    private String graduationTime;
    private ArrayList<Course> courses = new ArrayList<Course>();

    @XmlElement
    public int getId() { return id; }
    @XmlElement
    public String getName() { return name; }
    @XmlElement
    public String getGraduationTime() { return graduationTime; }
    @XmlElement
    public ArrayList<Course> getCourses() { return courses; }

    public void setId(int value) { this.id = value; }
    public void setName(String value) { this.name = value; }
    public void        

Answers


Ali Dehghani February 2016

Register a filter that intercepts each request, warp the HttpServletRequest into an implementation of HttpServletRequestWrapper and returns the Content-Type value for Accept header. For example, you can register a filter named SameInSameOutFilter like following:

@Component
public class SameInSameOutFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        SameInSameOutRequest wrappedRequest = new SameInSameOutRequest((HttpServletRequest) request);
        chain.doFilter(wrappedRequest, response);
    }
}

It wraps current request in a SameInSameOutRequest:

public class SameInSameOutRequest extends HttpServletRequestWrapper {
    public SameInSameOutRequest(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getHeader(String name) {
        if (name.equalsIgnoreCase("accept")) {
            return getContentType();
        }

        return super.getHeader(name);
    }
}

This wrapper tells spring mvc to select a HttpMessageConverter based on request's Content-Type value. If request body's Content-Type is application/xml, then the response would be an XML. Otherwise, the response would be JSON.

The other solution is to manually set the Accept header along with Content-Type in each request and avoid all these hacks.


manish February 2016

The best practice for handling different data formats with the same controller is to let the framework do all the work of figuring out the marshalling and unmarshalling mechanisms.

Step 1: Use minimal controller configuration

@RequestMapping(value = "/getxmljson", method = RequestMethod.POST)
@ResponseBody
public Student processXMLJsonRequest(@RequestBody Student student) {
  return student;
}

There is no need to specify consumes and produces here. As an example, consider that you may want this same method to handle other formats in the future such as Google Protocol Buffers, EDI, etc. Keeping the controllers free of consumes and produces will let you add data formats through global configuration instead of having to modify the controller code.

Step 2: Use ContentNegotiatingViewResolver instead of RequestMappingHandlerAdapter

  <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="defaultViews">
      <list>
        <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
      </list>
    </property>
  </bean>

Let the view resolver decide how to read incoming data and how to write it back.

Step 3: Use Accepts and Content-Type HTTP headers

Hitting your controller with the correct HTTP header values will force ContentNegotiatingViewResolver to marshal and unmarshal data automatically using the appropriate data representations.

If you want to exchange data in JSON format, set both headers to application/json. If you want XML instead, set both to application/xml.

Post Status

Asked in February 2016
Viewed 3,295 times
Voted 5
Answered 2 times

Search




Leave an answer