我的補充:
在struts2的基礎上 配置 struts2還需要的包:
commons-lang-2.4.jar;
json-lib-2.3-jdk13.jar;
jsonplugin-0[1].32.jar;
ezmorph-1.0.2.jar;
commons-beanutils-1.7.0.jar;
另外,可以通過: @JSON(name="mymessage") 來給 action 中的get屬性設 json節點 別名
通過給 屬性設置: @JSON(serialize=false) 來給控制 哪些屬性 不序列化輸出.
我的異常就是: 因為我的 action 中有 service 接口對象屬性,所以導致 序列化成 json 失敗 而異常了
多了去了,
root:只包含xx對象
excludeProperties :除了xx之外的集合
includeProperties : 包含xx的集合
wrapPrefix :json結果開頭添加xx(比如<paramname="wrapPrefix">[</param>)
wrapSuffix :同上,只不過是結尾
ignoreHierarchy :轉化時是否忽略繼承關系true/false
enumAsBean : 是否將枚舉當做一個bean處理
enableGZIP :是否支持gzip壓縮輸出
noCache :是否緩存
excludeNullProperties : 是否轉化輸出 null值
statusCode :設置響應代號比如 304
callbackParameter : JSONP跨域訪問的回調設置
contentType : 輸出類型
大概就這么多了,還有幾個是低版本的,已經廢棄了,就不列出了
以下摘自: http://www.open-open.com/lib/view/open1325518231062.html
早在我剛學Struts2之初的時候,就想寫一篇文章來闡述Struts2如何返回JSON數據的原理和具體應用了,但苦於一直忙於工作難以抽身,漸漸的也淡忘了此事。直到前兩天有同事在工作中遇到這個問題,來找我詢問,我又細細地給他講了一遍之后,才覺得無論如何要抽一個小時的時間來寫這篇文章,從頭到尾將Struts2與JSON的關系說清楚。
其實網絡中,關於這個問題的答案已是海量,我當初也是從這海量的答案中吸收精華,才將“Struts2返回JSON數據”這個問題搞清楚的。但是這些海量的答案,有一個共同的缺陷,就是作者們只關注問題核心,即“如何在具體的Struts2應用中返回JSON數據到客戶端”如何實現,而對於"為何要這樣實現"以及實現的本質卻解釋的不甚了了,在筆者看來這只是“授人以魚”而非筆者所推崇的“授人以魚的同時,授人以漁”。在這篇文章中,筆者將總結前輩們的經驗,並結合自己的理解,來從理論到實踐由淺入深的說明“Struts2返回JSON數據”這一問題。
JSON(JavaScript Object Notation)
首先來看一下JSON官方對於“JSON”的解釋:
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。易於人閱讀和編寫。同時也易於機器解析和生成。它基於JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。 JSON采用完全獨立於語言的文本格式,但是也使用了類似於C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。這些特性使JSON成為理想的數據交換語言。(更多內容請參見JSON官網http://json.org/json-zh.html)
JSON建構於兩種結構:
“名稱/值”對的集合(A collection of name/value pairs)。不同的語言中,它被理解為對象(object),紀錄(record),結構(struct),字典(dictionary),哈希表(hash table),有鍵列表(keyed list),或者關聯數組 (associative array)。
值的有序列表(An ordered list of values)。在大部分語言中,它被理解為數組(array)。
因為JSON中的值(value)可以是雙引號括起來的字符串(string)、數值(number)、true、false、 null、對象(object)或者數組(array),且這些結構可以嵌套,這種特性給予JSON表達數據以無限的可能:它既可以表達一個簡單的 key/value,也可以表達一個復雜的Map或List,而且它是易於閱讀和理解的。
Struts2中JSON的用武之地
因為JSON是脫離語言的理想的數據交換格式,所以它被頻繁的應用在客戶端與服務器的通信過程中,這一點是毋庸置疑的。而在客戶端與服務器的通信過程中,JSON數據的傳遞又被分為服務器向客戶端傳送JSON數據,和客戶端向服務器傳送JSON數據,前者的核心過程中將對象轉換成JSON,而后者的核心是將JSON轉換成對象,這是本質的區別。另外,值得一提的是,JSON數據在傳遞過程中,其實就是傳遞一個普通的符合JSON語法格式的字符串而已,所謂的“JSON對象”是指對這個JSON字符串解析和包裝后的結果,這一點請牢記,因為下面的內容會依賴這一點。
Struts2返回JSON數據到客戶端
這是最常見的需求,在AJAX大行其道的今天,向服務器請求JSON數據已成為每一個WEB應用必備的功能。拋開Struts2暫且不提,在常規WEB應用中由服務器返回JSON數據到客戶端有兩種方式:一是在Servlet中輸出JSON串,二是在JSP頁面中輸出JSON串。上文提到,服務器像客戶端返回JSON數據,其實就是返回一個符合JSON語法規范的字符串,所以在上述兩種 方法中存在一個共同點,就是將需要返回的數據包裝稱符合JSON語法規范的字符串后在頁面中顯示。如下所示
使用Servlet返回JSON數據到客戶端:
01 |
package cn.ysh.studio.struts2.json.demo.servlet; |
02 |
03 |
import java.io.IOException; |
04 |
import java.io.PrintWriter; |
05 |
06 |
import javax.servlet.ServletException; |
07 |
import javax.servlet.http.HttpServlet; |
08 |
import javax.servlet.http.HttpServletRequest; |
09 |
import javax.servlet.http.HttpServletResponse; |
10 |
11 |
import net.sf.json.JSONObject; |
12 |
13 |
import cn.ysh.studio.struts2.json.demo.bean.User; |
14 |
15 |
public class JSON extends HttpServlet { |
16 |
17 |
/** |
18 |
* |
19 |
*/ |
20 |
private static final long serialVersionUID = 1L; |
21 |
22 |
/** |
23 |
* The doGet method of the servlet. <br> |
24 |
* |
25 |
* This method is called when a form has its tag value method equals to get. |
26 |
* |
27 |
* @param request the request send by the client to the server |
28 |
* @param response the response send by the server to the client |
29 |
* @throws ServletException if an error occurred |
30 |
* @throws IOException if an error occurred |
31 |
*/ |
32 |
public void doGet(HttpServletRequest request, HttpServletResponse response) |
33 |
throws ServletException, IOException { |
34 |
35 |
response.setContentType( "text/html" ); |
36 |
PrintWriter out = response.getWriter(); |
37 |
//將要被返回到客戶端的對象 |
38 |
User user= new User(); |
39 |
user.setId( "123" ); |
40 |
user.setName( "JSONServlet" ); |
41 |
user.setPassword( "JSON" ); |
42 |
user.setSay( "Hello , i am a servlet !" ); |
43 |
JSONObject json= new JSONObject(); |
44 |
json.accumulate( "success" , true ); |
45 |
json.accumulate( "user" , user); |
46 |
out.println(json.toString()); |
47 |
// 因為JSON數據在傳遞過程中是以普通字符串形式傳遞的,所以我們也可以手動拼接符合JSON語法規范的字符串輸出到客戶端 |
48 |
// 以下這兩句的作用與38-46行代碼的作用是一樣的,將向客戶端返回一個User對象,和一個success字段 |
49 |
// String jsonString="{\"user\":{\"id\":\"123\",\"name\":\"JSONServlet\",\"say\":\"Hello , i am a servlet !\",\"password\":\"JSON\"},\"success\":true}"; |
50 |
// out.println(jsonString); |
51 |
out.flush(); |
52 |
out.close(); |
53 |
} |
54 |
55 |
/** |
56 |
* The doPost method of the servlet. <br> |
57 |
* |
58 |
* This method is called when a form has its tag value method equals to post. |
59 |
* |
60 |
* @param request the request send by the client to the server |
61 |
* @param response the response send by the server to the client |
62 |
* @throws ServletException if an error occurred |
63 |
* @throws IOException if an error occurred |
64 |
*/ |
65 |
public void doPost(HttpServletRequest request, HttpServletResponse response) |
66 |
throws ServletException, IOException { |
67 |
doGet(request, response); |
68 |
} |
69 |
70 |
} |
結果在意料之中,如下圖所示:
使用JSP(或html等)返回JSON數據到客戶端:
1 |
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> |
2 |
{"user":{"id":"123","name":"JSONJSP","say":"Hello , i am a JSP !","password":"JSON"},"success":true} |
這個就不用去看結果了吧。
再回到Struts,在Struts的MVC模型中,Action替代Servlet擔當了Model的角色,所以對於Struts而言,返回 JSON數據到客戶端,跟傳統的WEB應用一樣,存在兩種方式,即在Action中輸出JSON數據,和在視圖資源中輸出JSON數據。再往下細分的話,在Action中輸出JSON數據又分為兩種方式,一是使用傳統方式輸出自己包裝后的JSON數據,二是使用Struts自帶的JSON數據封裝功能來自動包裝並返回JSON數據。
在視圖資源中輸出JSON數據
Action處理完用戶請求后,將數據存放在某一位置,如request中,並返回視圖,然后Struts將跳轉至該視圖資源,在該視圖中,我們需要做的是將數據從存放位置中取出,然后將其轉換為JSON字符串,輸出在視圖中。這跟傳統WEB應用中在JSP頁面輸出JSON數據的做法如出一轍:
01 |
public String testByJSP() { |
02 |
User user = new User(); |
03 |
user.setId( "123" ); |
04 |
user.setName( "Struts2" ); |
05 |
user.setPassword( "123" ); |
06 |
user.setSay( "Hello world !" ); |
07 |
JSONObject jsonObject= new JSONObject(); |
08 |
jsonObject.accumulate( "user" , user); |
09 |
//這里在request對象中放了一個data,所以struts的result配置中不能有type="redirect" |
10 |
ServletActionContext.getRequest().setAttribute( "data" , jsonObject.toString()); |
11 |
return SUCCESS; |
12 |
}; |
因為是常規的Struts流程配置,所以配置內容就不再展示了。
JSP代碼就非常簡單了,
1 |
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> |
2 |
${data } |
結果如圖所示:
在Action中以傳統方式輸出JSON數據
這一點跟傳統的Servlet的處理方式基本上一模一樣,代碼如下
01 |
public void doAction() throws IOException{ |
02 |
HttpServletResponse response=ServletActionContext.getResponse(); |
03 |
//以下代碼從JSON.java中拷過來的 |
04 |
response.setContentType( "text/html" ); |
05 |
PrintWriter out; |
06 |
out = response.getWriter(); |
07 |
//將要被返回到客戶端的對象 |
08 |
User user= new User(); |
09 |
user.setId( "123" ); |
10 |
user.setName( "JSONActionGeneral" ); |
11 |
user.setPassword( "JSON" ); |
12 |
user.setSay( "Hello , i am a action to print a json!" ); |
13 |
JSONObject json= new JSONObject(); |
14 |
json.accumulate( "success" , true ); |
15 |
json.accumulate( "user" , user); |
16 |
out.println(json.toString()); |
17 |
// 因為JSON數據在傳遞過程中是以普通字符串形式傳遞的,所以我們也可以手動拼接符合JSON語法規范的字符串輸出到客戶端 |
18 |
// 以下這兩句的作用與38-46行代碼的作用是一樣的,將向客戶端返回一個User對象,和一個success字段 |
19 |
// String jsonString="{\"user\":{\"id\":\"123\",\"name\":\"JSONActionGeneral\",\"say\":\"Hello , i am a action to print a json!\",\"password\":\"JSON\"},\"success\":true}"; |
20 |
// out.println(jsonString); |
21 |
out.flush(); |
22 |
out.close(); |
23 |
} |
struts.xml中的配置:
1 |
< package name = "default" extends = "struts-default" namespace = "/" > |
2 |
< action name = "testJSONFromActionByGeneral" class = "cn.ysh.studio.struts2.json.demo.action.UserAction" method = "doAction" > |
3 |
</ action > |
4 |
</ package > |
注意:這個action沒有result,且doAction方法沒有返回值!
就不再貼圖了,因為結果可想而知!
在Action中以Struts2的方式輸出JSON數據
本着“不重復發明輪子”的原則,我們將轉換JSON數據的工作交給Struts2來做,那么相對於在Action中以傳統方式輸出JSON不同的是,Action是需要將注意力放在業務處理上,而無需關心處理結果是如何被轉換成JSON被返回客戶端的——這些 工作通過簡單的配置,Struts2會幫我們做的更好。
01 |
public String testByAction() { |
02 |
// dataMap中的數據將會被Struts2轉換成JSON字符串,所以這里要先清空其中的數據 |
03 |
dataMap.clear(); |
04 |
User user = new User(); |
05 |
user.setId( "123" ); |
06 |
user.setName( "JSONActionStruts2" ); |
07 |
user.setPassword( "123" ); |
08 |
user.setSay( "Hello world !" ); |
09 |
dataMap.put( "user" , user); |
10 |
// 放入一個是否操作成功的標識 |
11 |
dataMap.put( "success" , true ); |
12 |
// 返回結果 |
13 |
return SUCCESS; |
14 |
} |
struts.xml中action的配置:
1 |
< package name = "json" extends = "json-default" namespace = "/test" > |
2 |
< action name = "testByAction" |
3 |
class = "cn.ysh.studio.struts2.json.demo.action.UserAction" method = "testByAction" > |
4 |
< result type = "json" > |
5 |
<!-- 這里指定將被Struts2序列化的屬性,該屬性在action中必須有對應的getter方法 --> |
6 |
< param name = "root" >dataMap</ param > |
7 |
</ result > |
8 |
</ action > |
9 |
</ package > |
凡是使用Struts2序列化對象到JSON的action,所在的package必須繼承自json-default,注意,這里唯一的result,沒有指定name屬性。
結果如下圖所示:
上面很詳細的說明了在WEB應用中如何返回JSON數據到客戶端,講了那么多種方式,涉及的技術核心無非只有兩點:
1、將對象轉換成符合JSON語法格式的字符串;
2、將符合JSON語法格式的字符串返回客戶端;
第二點是整個實現過程的本質,但卻不難做到;第一點其實也不難,他甚至有兩種做法,一是通過字符串拼接方式,而是通過JSONObject以對象方式轉換。看下面的一個例子:
01 |
package cn.ysh.studio.struts2.json.demo.test; |
02 |
03 |
import cn.ysh.studio.struts2.json.demo.bean.User; |
04 |
import net.sf.json.JSONObject; |
05 |
06 |
public class JSONTest { |
07 |
08 |
/** |
09 |
* 將普通的pojo轉換成JSON字符串 |
10 |
* @return |
11 |
*/ |
12 |
public JSONObject bean2json() { |
13 |
User user = new User(); |
14 |
user.setId( "JSONTest" ); |
15 |
user.setName( "JSONTest" ); |
16 |
user.setPassword( "JSON" ); |
17 |
user.setSay( "Hello,i am JSONTest.java" ); |
18 |
JSONObject jsonObject = new JSONObject(); |
19 |
jsonObject.accumulate( "user" , user); |
20 |
System.out.println( "User轉換后的字符串:" +jsonObject.toString()); |
21 |
return jsonObject; |
22 |
} |
23 |
24 |
/** |
25 |
* 從JSONObject對象中反向解析出User對象 |
26 |
* @param jsonObject |
27 |
*/ |
28 |
public void json2bean(JSONObject jsonObject) { |
29 |
User user=(User)JSONObject.toBean((JSONObject)jsonObject.get( "user" ),User. class ); |
30 |
System.out.println( "轉換得到的User對象的Name為:" +user.getName()); |
31 |
} |
32 |
33 |
public static void main(String[] s) { |
34 |
JSONTest tester= new JSONTest(); |
35 |
tester.json2bean(tester.bean2json()); |
36 |
} |
37 |
} |
JSON格式的字符串返回到客戶端后,客戶端會將其解析並封裝成真正的JSON對象,以供JS調用。
總結上述,其實只要明白了服務器返回JSON數據到客戶端的原理,做起來就游刃有余了,他甚至有非常多的可選方案,但既然是基於 Struts2的實現,那么肯定還是要用Struts2的方式來做啦,因為這樣確實可以省很多事。另外,在文章的最后,說明一下返回JSON數據時在 result中配置的參數的含義及其常見常見配置吧:
01 |
< result type = "json" > |
02 |
<!-- 這里指定將被Struts2序列化的屬性,該屬性在action中必須有對應的getter方法 --> |
03 |
<!-- 默認將會序列所有有返回值的getter方法的值,而無論該方法是否有對應屬性 --> |
04 |
< param name = "root" >dataMap</ param > |
05 |
<!-- 指定是否序列化空的屬性 --> |
06 |
< param name = "excludeNullProperties" >true</ param > |
07 |
<!-- 這里指定將序列化dataMap中的那些屬性 --> |
08 |
< param name = "includeProperties" > |
09 |
userList.* |
10 |
</ param > |
11 |
<!-- 這里指定將要從dataMap中排除那些屬性,這些排除的屬性將不被序列化,一半不與上邊的參數配置同時出現 --> |
12 |
< param name = "excludeProperties" > |
13 |
SUCCESS |
14 |
</ param > |
15 |
</ result > |
值得一提的是通過Struts2來返回JSON數據,在IE中會提示下載,這個不用關心,換個瀏覽器就能正常展示JSON數據,而在JS調用中,更是毫無影響。
下面是整個Action的完整代碼:
001 |
package cn.ysh.studio.struts2.json.demo.action; |
002 |
003 |
import java.io.IOException; |
004 |
import java.io.PrintWriter; |
005 |
import java.util.HashMap; |
006 |
import java.util.Map; |
007 |
008 |
import javax.servlet.http.HttpServletResponse; |
009 |
010 |
import org.apache.struts2.ServletActionContext; |
011 |
012 |
import net.sf.json.JSONObject; |
013 |
014 |
import cn.ysh.studio.struts2.json.demo.bean.User; |
015 |
016 |
import com.opensymphony.xwork2.ActionSupport; |
017 |
018 |
public class UserAction extends ActionSupport { |
019 |
020 |
/** |
021 |
* |
022 |
*/ |
023 |
private static final long serialVersionUID = 1L; |
024 |
025 |
//將會被Struts2序列化為JSON字符串的對象 |
026 |
private Map<String, Object> dataMap; |
027 |
028 |
/** |
029 |
* 構造方法 |
030 |
*/ |
031 |
public UserAction() { |
032 |
//初始化Map對象 |
033 |
dataMap = new HashMap<String, Object>(); |
034 |
} |
035 |
036 |
/** |
037 |
* 測試通過action以視圖方式返回JSON數據 |
038 |
* @return |
039 |
*/ |
040 |
public String testByJSP() { |
041 |
User user = new User(); |
042 |
user.setId( "123" ); |
043 |
user.setName( "JSONActionJSP" ); |
044 |
user.setPassword( "123" ); |
045 |
user.setSay( "Hello world !" ); |
046 |
JSONObject jsonObject= new JSONObject(); |
047 |
jsonObject.accumulate( "user" , user); |
048 |
jsonObject.accumulate( "success" , true ); |
049 |
//這里在request對象中放了一個data,所以struts的result配置中不能有type="redirect" |
050 |
ServletActionContext.getRequest().setAttribute( "data" , jsonObject.toString()); |
051 |
return SUCCESS; |
052 |
}; |
053 |
054 |
/** |
055 |
* 測試通過action以Struts2默認方式返回JSON數據 |
056 |
* @return |
057 |
*/ |
058 |
public String testByAction() { |
059 |
// dataMap中的數據將會被Struts2轉換成JSON字符串,所以這里要先清空其中的數據 |
060 |
dataMap.clear(); |
061 |
User user = new User(); |
062 |
user.setId( "123" ); |
063 |
user.setName( "JSONActionStruts2" ); |
064 |
user.setPassword( "123" ); |
065 |
user.setSay( "Hello world !" ); |
066 |
dataMap.put( "user" , user); |
067 |
// 放入一個是否操作成功的標識 |
068 |
dataMap.put( "success" , true ); |
069 |
// 返回結果 |
070 |
return SUCCESS; |
071 |
} |
072 |
073 |
/** |
074 |
* 通過action是以傳統方式返回JSON數據 |
075 |
* @throws IOException |
076 |
*/ |
077 |
public void doAction() throws IOException{ |
078 |
HttpServletResponse response=ServletActionContext.getResponse(); |
079 |
//以下代碼從JSON.java中拷過來的 |
080 |
response.setContentType( "text/html" ); |
081 |
PrintWriter out; |
082 |
out = response.getWriter(); |
083 |
//將要被返回到客戶端的對象 |
084 |
User user= new User(); |
085 |
user.setId( "123" ); |
086 |
user.setName( "JSONActionGeneral" ); |
087 |
user.setPassword( "JSON" ); |
088 |
user.setSay( "Hello , i am a action to print a json!" ); |
089 |
JSONObject json= new JSONObject(); |
090 |
json.accumulate( "success" , true ); |
091 |
json.accumulate( "user" , user); |
092 |
out.println(json.toString()); |
093 |
// 因為JSON數據在傳遞過程中是以普通字符串形式傳遞的,所以我們也可以手動拼接符合JSON語法規范的字符串輸出到客戶端 |
094 |
// 以下這兩句的作用與38-46行代碼的作用是一樣的,將向客戶端返回一個User對象,和一個success字段 |
095 |
// String jsonString="{\"user\":{\"id\":\"123\",\"name\":\"JSONActionGeneral\",\"say\":\"Hello , i am a action to print a json!\",\"password\":\"JSON\"},\"success\":true}"; |
096 |
// out.println(jsonString); |
097 |
out.flush(); |
098 |
out.close(); |
099 |
} |
100 |
|
101 |
/** |
102 |
* Struts2序列化指定屬性時,必須有該屬性的getter方法,實際上,如果沒有屬性,而只有getter方法也是可以的 |
103 |
* @return |
104 |
*/ |
105 |
public Map<String, Object> getDataMap() { |
106 |
return dataMap; |
107 |
} |
108 |
109 |
} |
完整的struts.xml配置文件如下:
01 |
<? xml version = "1.0" encoding = "UTF-8" ?> |
02 |
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" |
03 |
"http://struts.apache.org/dtds/struts-2.0.dtd"> |
04 |
< struts > |
05 |
< package name = "json" extends = "json-default" namespace = "/test" > |
06 |
< action name = "testByAction" |
07 |
class = "cn.ysh.studio.struts2.json.demo.action.UserAction" method = "testByAction" > |
08 |
< result type = "json" > |
09 |
<!-- 這里指定將被Struts2序列化的屬性,該屬性在action中必須有對應的getter方法 --> |
10 |
<!-- 默認將會序列所有有返回值的getter方法的值,而無論該方法是否有對應屬性 --> |
11 |
< param name = "root" >dataMap</ param > |
12 |
<!-- 指定是否序列化空的屬性 --> |
13 |
<!-- |
14 |
<param name="excludeNullProperties">true</param> |
15 |
--> |
16 |
<!-- 這里指定將序列化dataMap中的那些屬性 --> |
17 |
<!-- |
18 |
<param name="includeProperties"> |
19 |
userList.* |
20 |
</param> |
21 |
--> |
22 |
<!-- 這里指定將要從dataMap中排除那些屬性,這些排除的屬性將不被序列化,一半不與上邊的參數配置同時出現 --> |
23 |
<!-- |
24 |
<param name="excludeProperties"> |
25 |
SUCCESS |
26 |
</param> |
27 |
--> |
28 |
</ result > |
29 |
</ action > |
30 |
</ package > |
31 |
< package name = "default" extends = "struts-default" namespace = "/" > |
32 |
< action name = "testJSONFromActionByGeneral" |
33 |
class = "cn.ysh.studio.struts2.json.demo.action.UserAction" method = "doAction" > |
34 |
</ action > |
35 |
< action name = "testByJSP" |
36 |
class = "cn.ysh.studio.struts2.json.demo.action.UserAction" method = "testByJSP" > |
37 |
< result name = "success" >/actionJSP.jsp</ result > |
38 |
</ action > |
39 |
</ package > |
40 |
</ struts > |