通过扩展Struts2的ResultType,自定义一个JsonResult使Struts2无缝整合JSON,也使得在Struts2中使用Json的时候变得更加的方便和灵活,更具扩展性。
在Struts2中有个一个StreamResult,这是用于文件的上传和下载的,不知道大家是否用过。我们就先分析一下这个类,然后参照它写出JSONResult。
struts.xml中配置StreamResult的部分如下:
1: <result type="stream"><!-- 使用ognl获取值栈属性值用于配置 -->
2: <param name="contentType">${fileContentType}</param>
3: <param name="inputName">inputStream</param><!-- 默认是inputSteam,可省略,
4: 但是action中需要添加InputStream getInputStream() ,如果这边设置为imageInputStream 则需要添加InputStream getImageInputStream()-->
5: <param name="contentLength">${fileLength}</param>
6: <!-- 设置下载文件名 -->
7: <param name="contentDisposition">filename="${newFileName}"</param>
8: <param name="bufferSize">2048</param>
9: </result>
可以很清楚的看到使用StreamResult的时候,有五个属性参数需要配置,这五个参数都是需要从action中传递过来的!为什么就一定要配置五个参数呢?不能不配置吗?这五个参数是StreamResult中需要使用到的属性,当然也可以不用配置,但是前提是属性值需要符合StreamResult默认的参数。比如这个文件的contentType是image/gif的,而StreamResult中默认的是text/plain,这个能不配置吗?当然不行啦,否则后果是可以设想的,不出错才怪呢?!
接下来我们分析一下StreamResult源文件,要实现一个自定义的ResultType值需要继承StrutsResultSupport,并实现doExecute方法即可
1: import java.io.InputStream;
2: import java.io.OutputStream;
3: import javax.servlet.http.HttpServletResponse;
4: import org.apache.struts2.dispatcher.StrutsResultSupport;
5: import com.opensymphony.xwork2.ActionInvocation;
6:
7: public class StreamResult extends StrutsResultSupport {
8:
9: private static final long serialVersionUID = -1468409635999059850L;
10:
11: public static final String DEFAULT_PARAM = "inputName";
12:
13: protected String contentType = "text/plain";
14: protected String contentLength;
15: protected String contentDisposition = "inline";
16: protected String inputName = "inputStream";
17: protected InputStream inputStream;
18: protected int bufferSize = 1024;
19:
20: public StreamResult() {
21: super();
22: }
23:
24: public StreamResult(InputStream in) {
25: this.inputStream = in;
26: }
27:
28: //省略一大堆的setter和getter
29:
30: protected void doExecute(String finalLocation, ActionInvocation invocation)
31: throws Exception {
32:
33: OutputStream oOutput = null;
34:
35: try {
36: if (inputStream == null) {
37: // 这里是整个StreamResult的关键(个人观点)
38: // 这里是通过struts.xml中inputName配置的参数值,拿到值栈中与参数值同名的真实对象
39: // 这个对象也就是在action中那个InputStream的对象实例。
40: //通俗的讲,就是将那个参数值转化为与之同名的对象
41: inputStream = (InputStream) invocation.getStack().findValue(
42: conditionalParse(inputName, invocation));
43: }
44:
45: if (inputStream == null) {
46: String msg = ("Can not find a java.io.InputStream with the name ["
47: + inputName + "] in the invocation stack. " + "Check the <param name=\"inputName\"> tag specified for this action.");
48: throw new IllegalArgumentException(msg);
49: }
50:
51: // Find the Response in context
52: HttpServletResponse oResponse = (HttpServletResponse) invocation
53: .getInvocationContext().get(HTTP_RESPONSE);
54:
55: // Set the content type
56: oResponse.setContentType(conditionalParse(contentType, invocation));
57:
58: // Set the content length
59: if (contentLength != null) {
60: String _contentLength = conditionalParse(contentLength,
61: invocation);
62: int _contentLengthAsInt = -1;
63: try {
64: _contentLengthAsInt = Integer.parseInt(_contentLength);
65: if (_contentLengthAsInt >= 0) {
66: oResponse.setContentLength(_contentLengthAsInt);
67: }
68: } catch (NumberFormatException e) {
69:
70: }
71: }
72:
73: // Set the content-disposition
74: if (contentDisposition != null) {
75: oResponse.addHeader("Content-disposition", conditionalParse(
76: contentDisposition, invocation));
77: }
78:
79: // Get the outputstream
80: oOutput = oResponse.getOutputStream();
81:
82: // Copy input to output
83: byte[] oBuff = new byte[bufferSize];
84: int iSize;
85: while (-1 != (iSize = inputStream.read(oBuff))) {
86: oOutput.write(oBuff, 0, iSize);
87: }
88:
89: // Flush
90: oOutput.flush();
91: } finally {
92: if (inputStream != null)
93: inputStream.close();
94: if (oOutput != null)
95: oOutput.close();
96: }
97: }
98:
99: }
我们在web页面中访问服务器中文件对象的时候,服务器都是将那个对象以流的形式输出,浏览器在通过不同的contentType做出不同的响应。比如文件是一个图片,就可以在页面中显示出来;如果是一个rar文件,就会提示是否保存文件了。在以上源码中“转换”出来的inputStream就是那个文件对象的“源”了,代码85-87行就是将那个“源”流到客户端了。这样就可以使用StreanResult达到从服务器端下载一个文件了。
StreamResult的实现方式来实现我们自己的JsonResult。
使用过Json的同学都知道,使用Json的时候有几个重要的类是我们经常使用到的。他们分别是:JSONArray,JSONObject和JsonConfig;
JSONArray:是用于将Java中Array以及Collection转换成Json形式的字符串;
JSONObject:是将Java中的普通对象以及Map转换成Json形式的字符串;
JsonConfig:当我们将Java中的对象要转换成Json的时候,需要防止形成环状(比如在一对一等映射关系中),如果你是使用Hibernate的时候,对于代理对象也是不能转化的,还有就是对于日期的转换也是经常会出错的(如果一定要使用日期,建议使用dwr传输数据)。以上三种情景是在使用Json的时候最容易出错的,幸好JsonConfig帮了我们大忙。使用JsonConfig可以将一个类中包含以上三种类型的属性在将对象转换成Json的时候过滤掉(当然是在客户端不需要那些属性的情况下),同时这样也能减少网络通信的数据量,提升性能。
比如有这么一个Person类:
1: public class Person {
2:
3: private String id;
4: private String name;
5: private int age;
6: private Date birthday;
7: /**
8: * 身份证,与person是一对一的关系
9: */
10: private IDCard idcard;
我们就可以用以下代码过滤掉某些不需要转换的属性了:
1: public void TransToJson(){
2: JsonConfig config = new JsonConfig();
3: config.setExcludes(new String[]{"birthday","idcard"});
通过以上分析就可以得出JsonResult需要三个属性了:
1: public class JSONResult extends StrutsResultSupport {
2:
3: private static final long serialVersionUID = 2067467373264270817L;
4:
5: /**
6: * 配置需要过滤掉的属性
7: */
8: public String excludes;
9: /**
10: * 需要转化为真实的对象,在将此对象转化为Json,通过response响应到客户端
11: */
12: public String objectName;
13: /**
14: * 配置对象类型(Object|Array)
15: */
16: public String objectType;
当我们完成了JsonResult的时候还要在Struts.xml中声明自定义的ResultType
1: <!-- 声明自定义的JsonResults -->
2: <result-types>
3: <result-type name="json" class="ssh.jquery.crud.util.JSONResult"/>
4: </result-types>
5:
6: <action name="person" class="ssh.jquery.crud.action.PersonAction">
7: <result>/WEB-INF/page/default.jsp</result>
8: <!-- 使用名字为json的type -->
9: <result name="json" type="json">
10: <!-- 配置对象类型 -->
11: <param name="objectType">array</param>
12: <!-- 配置对象名,在action需要有一个getPersons()方法 -->
13: <param name="objectName">persons</param>
14: <!-- 配置过滤的属性,当不需要过滤的时候值为null;有多个属性时用","隔开 -->
15: <param name="excludes">null</param>
16: </result>
以下是JsonResult的完整代码:
1: public class JSONResult extends StrutsResultSupport {
2:
3: private static final long serialVersionUID = 2067467373264270817L;
4:
5: /**
6: * 配置需要过滤掉的属性
7: */
8: public String excludes;
9: /**
10: * 需要转化为真实的对象,在将此对象转化为Json,通过response响应到客户端
11: */
12: public String objectName;
13: /**
14: * 配置对象类型(Object|Array)
15: */
16: public String objectType;
17:
18: //省略一堆setter和getter
19:
20: @Override
21: protected void doExecute(String finalLocation, ActionInvocation invocation)
22: throws Exception {
23: //获得servlet中的response对象
24: HttpServletResponse response = (HttpServletResponse) invocation
25: .getInvocationContext().get(HTTP_RESPONSE);
26: response.setContentType("text/javascript;charset=utf-8");
27: PrintWriter out = response.getWriter();
28: JsonConfig config = new JsonConfig();
29: //设置过滤的属性
30: config.setExcludes(excludes.split(","));
31: //判断对象类型,再进行转换
32: if ("array".equals(objectType.toLowerCase())) {
33: JSONArray jsonArray = JSONArray.fromObject(invocation.getStack()
34: .findValue(conditionalParse(objectName, invocation)),
35: config);
36: out.print(jsonArray.toString());
37: } else if ("object".equals(objectType.toLowerCase())) {
38: JSONObject jsonObject = JSONObject.fromObject(invocation.getStack()
39: .findValue(conditionalParse(objectName, invocation)),
40: config);
41: out.print(jsonObject.toString());
42: }
43: }