Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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 setGraduationTime(String value) { this.graduationTime = value; }     public void setCourses(ArrayList<Course> value) { this.courses = value; }      @JsonIgnore     public String toString() {         return this.name + " - "                 + graduationTime == null? "Unknown" : graduationTime.toString();     }      public Student() {}     public Student(int id, String name, String graduationTime) {         this.id = id;         this.name = name;         this.graduationTime = graduationTime;     } } 

POJO Class: Course.java

import java.io.Serializable;  import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType;  import com.fasterxml.jackson.annotation.JsonPropertyOrder;  @XmlRootElement(name = "course") @XmlType(propOrder = {"courseName", "score"}) @JsonPropertyOrder({"courseName", "score"}) public class Course implements Serializable {     private static final long serialVersionUID = 1L;      private String courseName;     private Integer score;      public @XmlElement String getCourseName() { return courseName; }     public @XmlElement Integer getScore() { return score; }      public void setCourseName(String value) { courseName = value; }     public void setScore(Integer value) { score = value; }      public Course() {}     public Course(String courseName, Integer score) {         this.courseName = courseName;         this.score = score;     } } 

spring-config.xml

<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/mvc"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"     xmlns:context="http://www.springframework.org/schema/context"     xmlns:sws="http://www.springframework.org/schema/web-services"     xmlns:jee="http://www.springframework.org/schema/jee" xmlns:oxm="http://www.springframework.org/schema/oxm"     xmlns:util="http://www.springframework.org/schema/util"     xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd             http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd             http://www.springframework.org/schema/web-services         http://www.springframework.org/schema/web-services/web-services-2.0.xsd             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd             http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd        http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.0.xsd        http://www.springframework.org/schema/util             http://www.springframework.org/schema/util/spring-util-2.5.xsd">      <!-- DispatcherServlet Context: defines this servlet's request-processing          infrastructure -->      <!-- Enables the Spring MVC @Controller programming model -->     <annotation-driven />      <!-- Handles HTTP GET requests for /resources/** by efficiently serving          up static resources in the ${webappRoot}/resources directory -->     <resources mapping="/resources/**" location="/resources/" />      <!-- Configure to plugin JSON as request and response in method handler -->     <beans:bean         class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">         <beans:property name="messageConverters">             <beans:list>                 <beans:ref bean="jsonMessageConverter" />                 <beans:ref bean="xmlMessageConverter" />             </beans:list>         </beans:property>     </beans:bean>      <!-- Configure bean to convert JSON to POJO and vice versa -->     <beans:bean id="jsonMessageConverter"         class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">     </beans:bean>      <beans:bean id="xmlMessageConverter"         class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter">     </beans:bean>      <beans:bean id="restTemplate" class="org.springframework.web.client.RestTemplate">      </beans:bean>      <beans:bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />     <context:component-scan base-package="com.test" />  </beans:beans> 

Json Input:

{ "id":2014, "name":"test", "graduationtime":"09/05/2014", "courses":[ { "courseName":"Math", "score":150 }, { "courseName":"Che", "score":150 } ] } 

XML Input:

<?xml version="1.0" encoding="UTF-8" ?> <student> <id>2014</id> <name>test</name> <graduationTime>09/05/2014</graduationTime> <courses>     <courseName>Math</courseName>     <score>150</score> </courses> <courses>     <courseName>Che</courseName>     <score>150</score> </courses> </student> 
like image 235
Mohan Avatar asked Feb 08 '16 13:02

Mohan


2 Answers

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.

If you do not want to use HTTP headers (which ideally you should), you can simply add .json or .xml to the URL and ContentNegotiatingViewResolver will do the rest.


You can check out my sample app that I created using your code snippets that works fine for JSON and XML.

like image 182
manish Avatar answered Oct 11 '22 01:10

manish


Adding to Manish's answer above, if you don't wanna use xml based configuration use this java based configuration instead-

@Bean public ViewResolver contentNegotiatingViewResolver() {     ContentNegotiatingViewResolver resolver =             new ContentNegotiatingViewResolver();      List<View> views = new ArrayList<>();     views.add(new MappingJackson2XmlView());     views.add(new MappingJackson2JsonView());      resolver.setDefaultViews(views);     return resolver; } 
like image 28
Gagandeep Kalra Avatar answered Oct 11 '22 02:10

Gagandeep Kalra