Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java

Support Both Json and XML Serializations in Spring MVC Applications

4.91/5 (4 votes)
24 Oct 2014CPOL6 min read 61.4K   718  
This article presents an example Spring MVC Rest service application that supports both Json and XML serializations.

Introduction

This article presents an example Spring MVC Rest service application that supports both Json and XML serializations.

Background

There are two popular serialization methods to work with Rest services, Json and XML. This article presents an example Rest service that supports both Json and XML serializations in a single Spring MVC application. The following is the example Maven project in the Eclipse's project explorer.

Image 1

  • The "com.song.data.model" package implements the MVC application's data model;
  • The "com.song.web.controller.api" package implements the MVC application's controller;
  • The "com.song.web.filter.NocacheFilter" class implements a servlet filter that disables the client and proxy caching on the web contents;
  • The "index.jsp" is a jsp page used as a test client of the Rest service.

This web application has been tested with Java 1.7, Tomcat 7, and Spring 4.0.7.RELEASE in a windows environment. The development environment used for this example is Eclipse Java EE IDE for Web Developers, version Kepler Service Release 2.

The Maven POM

The POM file of this Maven application is implemented as the following.

XML
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
        http://maven.apache.org/xsd/maven-4.0.0.xsd">
        
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.song.example</groupId>
    <artifactId>spring-web-application</artifactId>
    <version>0.0.1-SNAPSHOT</version>
      
    <packaging>war</packaging>
      
    <properties>
        <spring.version>4.0.7.RELEASE</spring.version>
        <jackson.version>1.9.13</jackson.version>
        <tomcat.version>7.0.55</tomcat.version>
    </properties>
          
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>${jackson.version}</version>
        </dependency>
            
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>${tomcat.version}</version>
            <scope>provided</scope>
        </dependency>
        
    </dependencies>
      
    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                <source>1.7</source>
                <target>1.7</target>
                </configuration>
            </plugin>
                      
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                <warSourceDirectory>WebContent</warSourceDirectory>
                <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  • The "spring-core", "spring-web", "spring-webmvc" dependencies are the minimum set of dependencies to build a Spring MVC application;
  • The  "jackson-mapper-asl" is the Jackson dependency that provide the classes used for data serialization;
  • The "tomcat-servlet-api" is the dependency that used for compiling the code that uses any servlet functionalities. Since most of run-time environments, such as Tomcat have the classes in this package, the dependency is scoped as "provided".

The "NocacheFilter" Servlet Filter

In many cases, it is a good idea to disable the browser and proxy caching in the web applications, so the clients always get the updated data whenever a web request is sent. The "NocacheFilter" class implements a servlet filter that disables the caching.

Java
package com.song.web.filter;
    
import java.io.IOException;
    
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
    
public class NocacheFilter implements Filter {
    
    public void doFilter(ServletRequest request,
            ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        
        HttpServletResponse httpResponse = (HttpServletResponse)response;
        httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        httpResponse.setHeader("Pragma", "no-cache");
        httpResponse.setDateHeader("Expires", 0);
        
        chain.doFilter(request, response);
    }
    
    public void destroy() {}
    public void init(FilterConfig fConfig) throws ServletException {}
}

This filter will be configured in the "web.xml" file, where we will be set up the url-pattern matching, so it only applies to the matching web requests.

The Data Model Classes

This example application implemented two data model classes. The "Student" class is implemented as the following.

Java
package com.song.data.model;
    
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 org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.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;
    }
}

The "Course" class is implemented as the following.

Java
package com.song.data.model;
    
import java.io.Serializable;
    
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
    
import org.codehaus.jackson.annotate.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;
    }
}

 

  • The two classes are simple Java bean classes;
  • The "@XmlElement" annotation tells the XML serializer to add the field to the XML document as an XML element;
  • The "@JsonIgnore" annotation tells the Json serializer to ignore this field when serializing the object into Json.

The MVC Controller

The MVC Controller class "StudentController" is implemented as the following.

Java
package com.song.web.controller.api;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.song.data.model.Student;
import com.song.data.model.Course;

@Controller
public class StudentController {
    
    @ResponseBody
    @RequestMapping(value = "/getstudent/{id}/{name}",
        method = RequestMethod.GET,
        produces={"application/json", "application/xml"})
    public Student getStudent(HttpServletRequest request, HttpServletResponse response,
            @PathVariable("id") final int id,
            @PathVariable("name") final String name) {
        
        // Create a new student object and return it
        SimpleDateFormat dateFormatter = new SimpleDateFormat("MM/dd/yyyy");
        Student student = new Student(id, name, dateFormatter.format(new Date()));
        
        List<Course> cources = student.getCourses();
        cources.add(new Course("Math", 15));
        cources.add(new Course("Politics", 100));
        
        return student;
    }
    
    @ResponseBody
    @RequestMapping(value = "/echostudent",
        method = RequestMethod.POST,
        produces={"application/json", "application/xml"},
        consumes={"application/json", "application/xml"})
    public Student echoStudent(@RequestBody Student student,
            HttpServletRequest request, HttpServletResponse response) {
        
        // Just echo the same student back
        return student;
    }
}
  • The action method "getStudent" takes an HTTP GET request;
  • The action method "echoStudent" takes an HTTP POST request;
  • The "produces" and "consumes" properties in the "@RequestMapping" annotation tells the Spring framework that the action methods work on both Json and XML serializations.

The "Web.xml"

The central place to configure the Spring MVC application is the "web.xml" file.

XML
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    id="WebApp_ID" version="3.0">
    
    <display-name>Spring Web Example</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
      
    <filter>
        <filter-name>nocachefilter</filter-name>
        <filter-class>
            com.song.web.filter.NocacheFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>nocachefilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
          <servlet-class>
              org.springframework.web.servlet.DispatcherServlet
          </servlet-class>
          <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
    
    <context-param>
        <param-name>BaseUrl</param-name>
          <param-value>
              http://localhost:8080/spring-web-application/
          </param-value>
    </context-param>
</web-app>
  • The entry point of the MVC controllers is the servlet named "mvc-dispatcher" and its url-pattern is set to "/api/*";
  • The "NocacheFilter" is configured to apply to all the web requests. This is because in the development environment, we are constantly making changes to the code. In a production environment, you may need to consider to selectively disable the caching on certain web content. It is a better idea to allow caching on JavaScript files, CSS files, and image files for better performance.

The "mvc-dispatcher" servlet is further configured in the "mvc-dispatcher-servlet.xml" file.

XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
     
     <context:component-scan base-package="com.song.web.controller" />
     <mvc:annotation-driven />
     
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
        <property name="prefix">
            <value>/views/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
     
</beans>
  • The "component-scan" tells Spring what are the Java packages to look for the MVC controllers;
  • The "viewResolver" tells Spring where to look for the MVC views. In this application we only implemented the Rest services, so there is no MVC view for html content in this application.

The Test Client

The "index.jsp" page implemented a simple test client for the Rest services.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    
<%String baseUrl = getServletContext().getInitParameter("BaseUrl");%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Spring Rest Client</title>
    
<link rel="stylesheet" type="text/css"
    href="<%out.print(baseUrl); %>Styles/site.css">
    
<script type="text/javascript"
    src="<%out.print(baseUrl); %>Scripts/jquery-2.1.1.min.js"></script>
<script type="text/javascript"
    src="<%out.print(baseUrl); %>Scripts/vkbeautify.0.99.00.beta.js"></script>
<script type="text/javascript"
    src="<%out.print(baseUrl); %>Scripts/jsonxml.js"></script>
<script type="text/javascript"
    src="<%out.print(baseUrl); %>Scripts/index.js"></script>
        
<script type="text/javascript">
    var urlbase = "<%out.print(baseUrl); %>";
    
    $(document).ready(function() {
        pageobject.initpage(urlbase);
    });
</script>    
</head>
<body>
<div>
<div>
    <textarea id="txtResult" readonly="readonly"></textarea>
</div>
<div>
    <span id="spanMessage" class="msgNormal">
        Click the buttons to test the rest calls...
    </span>
</div>
<div>
    <input type="button" id="btnGetXML" value="GET XML" />
    <input type="button" id="btnGetJson" value="GET Json" />
    <input type="button" id="btnPostJsonAcceptXML" value="POST Json Accept XML" />
    <input type="button" id="btnPOSTXMLAcceptJson" value="POST XML Accept Json" />
</div>
</div>
    
</body>
</html>

The Rest services calls are issued through jQuery in the "index.js" JavaScript file.

JavaScript
var pageobject = function() {
    var getUrl = null;
    var postUrl = null;
    
    var dataToPost = {
            id: 2014,
            name: "Hilary Clinton",
            graduationTime: "09/18/2014",
            courses: [
                {
                    courseName: "Math",
                    score: 15
                },
                {
                    courseName: "Politics",
                    score: 100
                }
            ]
    };
    
    var done = function(data, status) {
        var type = Object.prototype.toString.call(data);
        var formatedText = '';
        
        if (type === "[object XMLDocument]"
                || type === "[object Document]") {
            
            var text = (new XMLSerializer()).serializeToString(data);
            formatedText = vkbeautify.xml(text);
        }
        else {
            var text = JSON.stringify(data);
            formatedText = vkbeautify.json(text);
        }
    
        var date = new Date();
        var msg = 'Rest call successful '
            + date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
        $('#spanMessage').html(msg)
            .removeClass('msgError').addClass('msgNormal');
        $('#txtResult').html(formatedText);
    };
    
    var fail = function(xhr, status, error) {
        $('#txtResult').html('');
        
        var date = new Date();
        var msg = 'Rest call failed (Error code: ' + error + ') - '
            + date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
        $('#spanMessage').html(msg)
            .removeClass('msgNormal').addClass('msgError');;
        
    };
    
    var initpage = function(baseUrl) {
        getUrl = urlbase + 'api/getstudent/2014/Hilary%20Clinton';
        postUrl = urlbase + 'api/echostudent';
        
        $('#btnGetXML').off('click');
        $('#btnGetJson').off('click');
        $('#btnPostJsonAcceptXML').off('click');
        $('#btnPOSTXMLAcceptJson').off('click');
        
        $('#btnGetXML').on('click', function() {
            $.ajax({
                url: getUrl,
                headers: {
                    Accept: 'application/xml'
                }
            }).done(done).fail(fail);
        });
        
        $('#btnGetJson').on('click', function() {
            $.ajax({
                url: getUrl,
                headers: {
                    Accept: 'application/json'
                }
            }).done(done).fail(fail);
        });
    
        $('#btnPostJsonAcceptXML').on('click', function() {
            $.ajax({
                url: postUrl,
                type: 'post',
                data: JSON.stringify(dataToPost),
                headers: {
                    Accept: 'application/xml',
                    'Content-Type': 'application/json'
                }
            }).done(done).fail(fail);
        });
        
        $('#btnPOSTXMLAcceptJson').on('click', function() {
            var xmlData = '<student>'
                + jsonxml.json2xml(dataToPost) + '</student>';
    
            $.ajax({
                url: postUrl,
                type: 'post',
                data: xmlData,
                headers: {
                    Accept: 'application/json',
                    'Content-Type': 'application/xml'
                }
            }).done(done).fail(fail);
        });
    };
    
    return {
        initpage: initpage
    };
    
}();

Import the Maven Project into Eclipse

Since this example is a Maven project, you can simply issue a "mvn clean install" to generate a "war" file and deploy it to a servlet container to test the application. You can also import the project into Eclipse. I have tested the import with Eclipse Java IDE for Web Developers Version: Kepler Service Release 2 and "jdk1.7.0_60". 

  • Download the zip file and unzip it to a folder of your choice;
  • Create an empty folder at a different location and give it a name of your choice. This empty folder will be your Eclipse workspace.

You can then launch Eclipse and open the empty workspace. In the Eclipse menu, you can select "File" -> "Import..." -> "Maven" -> "Existing Maven Projects".

Image 2

Then you can browse to the folder where the "pom.xml" is located and the Eclipse can find the POM file. 

Image 3

If everything goes smoothly, the Maven project should be imported into Eclipse after you clicking on the "Finish" button. The Eclipse and Maven do not always work very well, but the attached project is written in such a way that you should not have much trouble to import it into Eclipse, if you use the correct version of Eclipse.

Run the Application

 If everything goes smoothly, you can then test run the example in your development environment. I have tested the application with Tomcat 7. If you do not know how to run Tomcat in Eclipse, you can take a look at my earlier post.

Image 4

The above picture shows the Json result echoed back from the server when the data is posted to the server in XML format. I have tested this application in Chrome, Firefox, and the most recent version of Safari. Since jQuery version 2.1.1 is used, the test client does not support IE 8 or below. Because I do not have a higher version of IE on my computer, I did not do any testing on IE.

Points of Interest

  • This article presented an example Spring MVC Rest service application that supports both Json and XML serializations;
  • In the Java environment, we have may IDE choices, if you do not want to use Eclipse, you can use other IDEs to import the project into your environment. I have the impression that Netbean has very good support for Maven projects;
  • If you have difficulty to import the project into your IDE, you can always issue the "mvn clean install" command to generate a 'war' file and deploy it to a servlet container to run the application. If you are new to Maven, you can take a look at my earlier post.
  • I hope you like my postings and I hope this article can help you one way or the other.

History

First Revision - 9/19/2014

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)