65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Spring MVC 应用程序中同时支持 Json 和 XML 序列化

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (4投票s)

2014 年 9 月 22 日

CPOL

6分钟阅读

viewsIcon

61777

downloadIcon

750

本文介绍了一个支持 JSON 和 XML 序列化的 Spring MVC REST 服务应用程序示例。

引言

本文介绍了一个支持 JSON 和 XML 序列化的 Spring MVC REST 服务应用程序示例。

背景

有两种流行的序列化方法用于处理 REST 服务:JSON 和 XML。本文介绍了一个在单个 Spring MVC 应用程序中同时支持 JSON 和 XML 序列化的 REST 服务示例。下图是 Eclipse 项目浏览器中的示例 Maven 项目。

  • "com.song.data.model" 包实现了 MVC 应用程序的数据模型;
  • "com.song.web.controller.api" 包实现了 MVC 应用程序的控制器;
  • "com.song.web.filter.NocacheFilter" 类实现了一个 servlet 过滤器,该过滤器禁用了 Web 内容的客户端和代理缓存;
  • "index.jsp" 是一个用作 REST 服务测试客户端的 jsp 页面。

此 Web 应用程序已在 Windows 环境中,使用 Java 1.7、Tomcat 7 和 Spring 4.0.7.RELEASE 进行过测试。本示例使用的开发环境是 Eclipse Java EE IDE for Web Developers,版本 Kepler Service Release 2。

Maven POM

此 Maven 应用程序的 POM 文件实现如下。

<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>
  • "spring-core"、"spring-web"、"spring-webmvc" 依赖是构建 Spring MVC 应用程序的最小依赖集;
  • "jackson-mapper-asl" 是 Jackson 依赖,它提供了用于数据序列化的类;
  • "tomcat-servlet-api" 是用于编译使用任何 servlet 功能的代码的依赖。由于大多数运行时环境(如 Tomcat)都包含此包中的类,因此该依赖的作用域被设置为 "provided"。

NocacheFilter Servlet 过滤器

在许多情况下,禁用 Web 应用程序的浏览器和代理缓存是一个好主意,这样客户端在发送 Web 请求时就能始终获取到更新的数据。"NocacheFilter" 类实现了一个禁用缓存的 servlet 过滤器。

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 {}
}

此过滤器将在 "web.xml" 文件中配置,我们将在其中设置 url-pattern 匹配,以便它仅应用于匹配的 Web 请求。

数据模型类

本示例应用程序实现了两个数据模型类。"Student" 类实现如下。

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;
    }
}

"Course" 类实现如下。

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;
    }
}

 

  • 这两个类是简单的 Java Bean 类;
  • "@XmlElement" 注解告诉 XML 序列化器将该字段添加为 XML 元素添加到 XML 文档中;
  • "@JsonIgnore" 注解告诉 JSON 序列化器在将对象序列化为 JSON 时忽略此字段。

MVC 控制器

MVC 控制器类 "StudentController" 实现如下。

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;
    }
}
  • action 方法 "getStudent" 接收一个 HTTP GET 请求;
  • action 方法 "echoStudent" 接收一个 HTTP POST 请求;
  • "@RequestMapping" 注解中的 "produces" 和 "consumes" 属性告诉 Spring Framework,action 方法支持 JSON 和 XML 序列化。

Web.xml

配置 Spring MVC 应用程序的中心位置是 "web.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>
              https://:8080/spring-web-application/
          </param-value>
    </context-param>
</web-app>
  • MVC 控制器的入口点是名为 "mvc-dispatcher" 的 servlet,其 url-pattern 设置为 "/api/*";
  • "NocacheFilter" 配置为应用于所有 Web 请求。这是因为在开发环境中,我们不断更改代码。在生产环境中,您可能需要考虑选择性地禁用某些 Web 内容的缓存。允许缓存 JavaScript 文件、CSS 文件和图像文件以获得更好的性能是一个更好的主意。

"mvc-dispatcher" servlet 在 "mvc-dispatcher-servlet.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>
  • "component-scan" 告诉 Spring 需要查找 MVC 控制器的 Java 包;
  • "viewResolver" 告诉 Spring 在哪里查找 MVC 视图。在此应用程序中,我们只实现了 REST 服务,因此此应用程序中没有用于 HTML 内容的 MVC 视图。

测试客户端

"index.jsp" 页面实现了一个简单的 REST 服务测试客户端。

<%@ 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>

REST 服务调用通过 "index.js" JavaScript 文件中的 jQuery 进行。

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
    };
    
}();

将 Maven 项目导入 Eclipse

由于这是一个 Maven 项目,您可以简单地执行 "mvn clean install" 命令来生成一个 "war" 文件并将其部署到 servlet 容器中来测试应用程序。您也可以将项目导入 Eclipse。我已经使用 Eclipse Java IDE for Web Developers Version: Kepler Service Release 2 和 "jdk1.7.0_60" 测试了导入。

  • 下载 zip 文件并解压缩到您选择的文件夹;
  • 在另一个位置创建一个空文件夹,并为其命名。这个空文件夹将作为您的 Eclipse 工作区。

然后您可以启动 Eclipse 并打开空工作区。在 Eclipse 菜单中,您可以选择 "File" -> "Import..." -> "Maven" -> "Existing Maven Projects"。

然后您可以浏览到 "pom.xml" 所在文件夹,Eclipse 就可以找到 POM 文件。

如果一切顺利,您应该可以在点击 "Finish" 按钮后将 Maven 项目导入 Eclipse。Eclipse 和 Maven 并非总是能很好地协同工作,但附件的项目编写方式应能确保您在使用正确的 Eclipse 版本时不会遇到太多导入问题。

运行应用程序

如果一切顺利,您就可以在开发环境中测试运行此示例了。我已经使用 Tomcat 7 测试了该应用程序。如果您不知道如何在 Eclipse 中运行 Tomcat,可以参考我 之前的帖子

上图显示了当数据以 XML 格式发送到服务器时,服务器回显的 JSON 结果。我已在 Chrome、Firefox 和最新版本的 Safari 中测试了此应用程序。由于使用了 jQuery 2.1.1 版本,测试客户端不支持 IE 8 或更低版本。由于我的电脑上没有更高版本的 IE,所以我没有在 IE 上进行任何测试。

关注点

  • 本文介绍了一个支持 JSON 和 XML 序列化的 Spring MVC REST 服务应用程序示例;
  • 在 Java 环境中,我们有很多 IDE 选择,如果您不想使用 Eclipse,可以在其他 IDE 中将项目导入到您的环境中。我认为 Netbeans 对 Maven 项目有很好的支持;
  • 如果您在将项目导入 IDE 时遇到困难,您可以随时执行 "mvn clean install" 命令来生成一个 'war' 文件并将其部署到 servlet 容器来运行应用程序。如果您是 Maven 新手,可以参考我 之前的帖子
  • 我希望您喜欢我的文章,希望本文能以某种方式帮助您。

历史

第一次修订 - 2014/9/19

© . All rights reserved.