如果是作为客户端的HTTP+JSON接口工程,没有JSP等view视图的情况下,使用Jersery框架开发绝对是第一选择。而在基于Spring3 MVC的架构下,对HTTP+JSON的返回类型也有很好的支持。但是,在开发工作中,对功能的升级是基于既定架构是很常见的情况。本人碰到需要用开发基于Struts2的HTTP+JSON返回类型接口就是基于既定框架结构下进行的。

Struts2返回JSON有两种方式:1.使用Servlet的输出流写入JSON字符串;2.使用Struts2对JSON的扩展。

一.使用Servlet的输出流

JSON接口的实质是:JSON数据在传递过程中,其实就是传递一个普通的符合JSON语法格式的字符串而已,所谓的“JSON对象”是指对这个JSON字符串解析和包装后的结果。

所以这里只需要将一个JSON语法格式的字符串写入到Servlet的HttpServletResponse中,这里使用的是PrintWriter的方式,当然也可以采用Stream流的方式。需要注意的是:在调用getWriter之前未设置编码(既调用setContentType或者setCharacterEncoding方法设置编码), HttpServletResponse则会返回一个用默认的编码(既ISO-8859-1)编码的PrintWriter实例。这样就会造成中文乱码。而且设置编码时必须在调用getWriter之前设置,不然是无效的。

1.1编写接口代码:

这里的方法与一般的Struts2方法的区别是这里是void返回类型。

配置action

从以下的配置中可以明显的看到配置与普通的action配置没有任何区别,只是没有返回的视图而已。

返回值

1.2struts2的注解方式:

public class TestAction extends BaseAction {

    private Return ret;
@Action(value = "test", results = { @Result(type="plainText", params = {"userName", "%{userName}", "age", "%{age}", "client", "%{client}"}) })
public void test() { ShardedJedis sjedis = null;
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
try { age_int = Integer.parseInt(age) * 60;
String realName = null;
sjedis = Redis.getInstance().pool.getResource();
for (String string : ShortUrl.ShortText(userName)) {
if (!sjedis.exists(Constants.ALL + Constants.REALNAME + string)) {
realName = string;
sjedis.setex(Constants.REALNAME + string, age_int, userName);
sjedis.setex(Constants.CLIENT + string, age_int, client);
sjedis.setex(Constants.ALL + Constants.REALNAME + string, Constants.ONEYEAR, userName);
sjedis.setex(Constants.ALL + Constants.CLIENT + string, Constants.ONEYEAR, client);
break;
}
} this.ret.setCode(Constants.C_S_OK);
// this.ret.setMessage("userName:" + userName + "age" + age);
HashMap<String, String> map = new HashMap<String, String>();
map.put("sName", Constants.BEGIN + "/" + realName);
map.put("real_name", userName);
map.put("age", age_int/60+"分钟");
this.ret.setUrls(map);
log.debug(this.ret.toString());
response.getWriter().write(JSON.toJSONString(this.ret));
response.getWriter().close();
} catch (Exception e) {
try {
response.getWriter().write(JSON.toJSONString(this.ret)); //fastJson转String
response.getWriter().close();
} catch (IOException e1) {
e1.printStackTrace();
}
} finally {
// close
Redis.getInstance().pool.returnResource(sjedis);
}
}

二.使用Struts2对JSON的扩展

要使用这个扩展功能肯定需要添加支持包。经过本人的调试,这里有两种选择:

1.   xwork-core-2.1.6.jar和struts2-json-plugin-2.1.8.jar。如果你想使用struts2-json-plugin-2.1.8.jar这种支持方式,你的xwork-core-*.jar不能选择2.2.1及以上版本,因为xwork-core-*.jar的2.2.1及以上版本中没有了org.apache.commons.lang等包。启动tomcat的时候会出现:Java.lang.NoClassDefFoundError: org.apache.commons.lang.xwork.StringUtils。

2.   xwork-2.1.2.jar和jsonplugin-0.34.jar。如果想用jsonplugin-0.34.jar这种支持方式,那需要切换你的xwork-core-*.jar为xwork-2.1.2.jar。因为jsonplugin-0.34.jar需要com.opensymphony.xwork2.util.TextUtils

这个类的支持。而xwork-core-*.jar的2.2.1以上版本均为找到该类,且在xwork-core-2.1.6.jar中也没有该类。

最后说一句,还因为用原始构建方式而不停蹚雷,确实不值得,真心累。使用Maven等自动化构件方式,会在很大程度上避免依赖包间的版本差异的bug。第三节的“struts2零配置”中会使用maven的构件方式。

编写接口代码

该类中json()方法就是普通Struts2的方法。在这里没有看到任何JSON格式的字符串,因为我们将要把这项工作交给扩展去完成。在没有任何设定的情况下,改类下的所有getter方法的返回值将被包含在返回给客户端的JSON字符串中。要剔除不需要包含的属性,在类结构结构中需要在getter方法上使用@JSON(serialize=false)进行注解,当然在不影响其他业务的时候也可以直接去掉这个getter方法。所以本例中的返回结果是将dataMap对象转换成的JSON格式的字符串。

  1. package json;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import org.apache.struts2.json.annotations.JSON;
  5. import com.opensymphony.xwork2.ActionSupport;
  6. /**
  7. * JSON测试
  8. *
  9. * @author Watson Xu
  10. * @date 2012-8-4 下午06:21:01
  11. */
  12. public class JsonAction extends ActionSupport{
  13. private static final long serialVersionUID = 1L;
  14. private Map<String,Object> dataMap;
  15. private String key = "Just see see";
  16. public String json() {
  17. // dataMap中的数据将会被Struts2转换成JSON字符串,所以这里要先清空其中的数据
  18. dataMap = new HashMap<String, Object>();
  19. User user = new User();
  20. user.setName("张三");
  21. user.setPassword("123");
  22. dataMap.put("user", user);
  23. // 放入一个是否操作成功的标识
  24. dataMap.put("success", true);
  25. // 返回结果
  26. return SUCCESS;
  27. }
  28. public Map<String, Object> getDataMap() {
  29. return dataMap;
  30. }
  31. //设置key属性不作为json的内容返回
  32. @JSON(serialize=false)
  33. public String getKey() {
  34. return key;
  35. }
  36. }

配置aciton

在配置中,首先需要action所在的package继承了json-default,或者继承的父包继承了json-default。这配置action的返回类型的type为json,并且可以配置其序列化的属性等一些类参数

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE struts PUBLIC
  3. "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
  4. "http://struts.apache.org/dtds/struts-2.0.dtd">
  5. <struts>
  6. <package name="json" extends="struts-default,json-default" >
  7. <action name="json" class="json.JsonAction" method="json">
  8. <result type="json">
  9. <!-- 这里指定将被Struts2序列化的属性,该属性在action中必须有对应的getter方法 -->
  10. <param name="root">dataMap</param>
  11. </result>
  12. </action>
  13. </package>
  14. </struts>

返回值

  1. {"success":true,"user":{"name":"张三","password":"123"}}

三. Struts2零配置使用方法,使用Maven构件:

3.1) 建立一个webapp,这里还是采用Maven构建,构建过程参考limingnihao的blog: 使用Eclipse构建Maven的SpringMVC项目 。

3.2) 添加Struts2的依赖、struts2零配置依赖和struts2的json依赖:

  1. <dependencies>
  2. <!-- struts2核心依赖 -->
  3. <dependency>
  4. <groupId>org.apache.struts</groupId>
  5. <artifactId>struts2-core</artifactId>
  6. <version>2.3.4</version>
  7. <type>jar</type>
  8. <scope>compile</scope>
  9. </dependency>
  10. <!-- struts2零配置依赖 -->
  11. <dependency>
  12. <groupId>org.apache.struts</groupId>
  13. <artifactId>struts2-convention-plugin</artifactId>
  14. <version>2.3.4</version>
  15. <type>jar</type>
  16. <scope>compile</scope>
  17. </dependency>
  18. <!-- struts2的json依赖 -->
  19. <dependency>
  20. <groupId>org.apache.struts</groupId>
  21. <artifactId>struts2-json-plugin</artifactId>
  22. <version>2.3.4</version>
  23. <type>jar</type>
  24. <scope>compile</scope>
  25. </dependency>
  26. </dependencies>

经过测试,上面的依赖包间没有出现版本兼容的bug,不仅仅因为他们是同一个版本,更加得益于Maven的自动构建方式。

3.3) 配置web.xml,启用Struts2:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
  5. http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  6. <filter>
  7. <filter-name>StrutsPrepareAndExecuteFilter </filter-name>
  8. <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class>
  9. <init-param>
  10. <param-name>config</param-name>
  11. <param-value>struts-default.xml,struts-plugin.xml,struts.xml</param-value>
  12. </init-param>
  13. </filter>
  14. <filter-mapping>
  15. <filter-name>StrutsPrepareAndExecuteFilter</filter-name>
  16. <url-pattern>/*</url-pattern>
  17. </filter-mapping>
  18. </web-app>

3.4)配置struts.xml,设置一些基本常量和应用:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE struts PUBLIC
  3. "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
  4. "http://struts.apache.org/dtds/struts-2.0.dtd">
  5. <struts>
  6. <package name="base" extends="json-default,struts-default">
  7. <!-- 这里可以设置一些全局的返回值映射关系等 -->
  8. </package>
  9. <constant name="struts.action.extension" value="" />
  10. <constant name="struts.ui.theme" value="simple" />
  11. <constant name="struts.i18n.encoding" value="utf-8" />
  12. <constant name="struts.multipart.maxSize" value="1073741824"/>
  13. <constant name="struts.devMode" value="false"/>
  14. </struts>

3.5)编写和配置Action。由并未指定Convention进行设置,所以对于Convention插件而言,默认的它会把所有类名以Action结尾的java类当成Action处理:

  1. package watson.action;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import org.apache.struts2.convention.annotation.Action;
  5. import org.apache.struts2.convention.annotation.Namespace;
  6. import org.apache.struts2.convention.annotation.ParentPackage;
  7. import org.apache.struts2.convention.annotation.Result;
  8. import org.apache.struts2.convention.annotation.Results;
  9. @ParentPackage("base")
  10. @Namespace("/watson")
  11. @Results({
  12. @Result(name = "json",type="json", params={"root","msg"})
  13. })
  14. public class JsonAction {
  15. @Action(value="json")
  16. public String json() {
  17. msg = new HashMap<String, Object>();
  18. msg.put("flag", "success");
  19. Map<String, String> user = new HashMap<String, String>();
  20. user.put("name", "张三");
  21. user.put("age", "34");
  22. msg.put("user", user);
  23. return "json";
  24. }
  25. //==================================
  26. private Map<String, Object> msg;
  27. public Map<String, Object> getMsg() {
  28. return msg;
  29. }
  30. }

3.6)部署项目,启动容器,浏览器地址栏中输入:http://localhost:7070/Struts2foo/watson/json。等到结果如下:

  1. {"flag":"success","user":{"age":"34","name":"张三"}}

从上面结果可知在启用了零配置以后,只是少了在xml中的配置,改为在每个action中用annotation进行注解。这里删除上面在xml中的配置,将下面的代码写入到上面的JsonAction的上部:

  1. @ParentPackage("base")
  2. @Namespace("/watson")
  3. @Results({
  4. @Result(name = "json",type="json", params={"root","msg"})
  5. })

root就相当xml配置中的参数配置。

四.附 

action的返回类型为json时的可配置参数详解:

  1. <result type="json">
  2. <!-- 这里指定将被Struts2序列化的属性,该属性在action中必须有对应的getter方法 -->
  3. <!-- 默认将会序列所有有返回值的getter方法的值,而无论该方法是否有对应属性 -->
  4. <param name="root">dataMap</param>
  5. <!-- 指定是否序列化空的属性 -->
  6. <param name="excludeNullProperties">true</param>
  7. <!-- 这里指定将序列化dataMap中的那些属性 -->
  8. <param name="includeProperties">userList.*</param>
  9. <!-- 这里指定将要从dataMap中排除那些属性,这些排除的属性将不被序列化,一般不与上边的参数配置同时出现 -->
  10. <param name="excludeProperties">SUCCESS</param>
  11. </result>

=====================================

去掉返回值中多余的对象方法:

步骤2:配置action
     在配置中,首先需要action所在的package继承了json-default(其实json-default已经继承类 struts-default),或者继承的父包继承了json-default。这配置action的返回result类型的type为json,result设置成json之后,容器会把action的属性自动封装到一个json对象中(json拦截器来做),然后调用js的callback方法. 返回json数据。另外还可以配置其序列化的属性等一些类参数
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
     <package name="json" extends="json-default" >
        <action name="testJson" class="json.JsonAction" method="json">
            <result name="myJson" type="json">
          <!-- 是否去掉返回值为NULL的properties -->
            <param name="excludeNullProperties">true</param>
         <!-- 这里指定将被Struts2序列化的属性,该属性在action中必须有对应的getter方法 -->
                <param name="root">dataMap</param>
      
 <!--<param name="includeProperties">   dataMap.* </param> -->
            </result>
        </action>
    </package>
</struts>
 
   步骤3:查看json返回值
         {"success":true,"user":{"name":"张三","password":"123"}}
       如果上面param配置的是includeProperties,那么返回值就是:
    {"dataMap":{"success":true,
"user":{"name":"张三","password":"123"}}}

总结:即如果使用includeProperties 或者excludeProperties 参数,那么返回的json字符串的key就是这里的属性名,value就是该属性的值(精华呀!!!!!)

################json中param详解#######################
     <param name="root">:是指定将被Struts2序列化的属性转为json字符串,该属性在action中必须有对应的getter方法。
     我们有时候需要根据实际情况返回部分结果,如何对json的结果进行定制输出呢?result提供了一些参数替你解决这个问题,一般情况下用的最多的就是includeProperties 参数和excludeNullProperties参数。当然还有其他的方法,如给pojo的属性加json注解(步骤二中以讲到)。

   includeProperties 参数:输出结果中需要包含的属性值,这里正则表达式和属性名匹配,可以用“,”分割填充多个正则表达式。这个参数直接返回对象的json数据,前台不需要eval转换;<param name="root">result</param>则不同,如果前台请求没有指定返回json数据(即ajax请求未指定返回json),需要前台进行eval转换,
如:
   示例1:
   输出person对象的所有属性与只输出其name
<result type="json">
   <param name="includeProperties">person.*, person\.name</param>
</result>  
  示例2(重要(精华呀!!!!!)):
     输出一个JSON List列表
   <action name="testList" class="testAction" method="list">  
      <result name="success" type="json">
         <param name="includeProperties">
             myList\[\d+\]\.Id,list\[\d+\]\.user\.userName    <!--正则表达式--!>
        </param>
    </result>     
 </action>
其中myList是action中的一个List类型的属性
myList\[\d+\]\.Id表示,list中存储的对象0..end的Id属性(list中存储的对象必须有Id属性)。
myList\[\d+\]\.user\.userName表示list中的对象中的user对象的userName属性
myList.*  表示list中的所有属性

  excludeProperties 参数:输出结果需要剔除的属性值,也支持正则表达式匹配属性名,可以用“,”分割填充多个正则表达式,类同includeProperties,但是excludeProperties有比includeProperties更高的权限.一般不与includeProperties参数配置同时出现 。

  <param name="excludeNullProperties">:指定是否序列化空的属性

为什么要用includeProperties或excludeProperties 参数?
     主要是为了过滤掉接口,pojo的set、list、其它对象等(好像没有Map)不需要的数据防止循环取其它关联对象或找不到接口。如果不配置,默认是处理action中的所有属性,如果action中有接口注入,json拦截器可能找不到接口而返回不了结果,还有如果action中有一个对象,这个对象与好多对象都有关联,json拦截器会将相关联的所有对象的属性全部转换成json格式,如果其它对象有list、set,其返回结果相当庞大,有可能是死循环而无法返回 。如果不用<param name="includeProperties">或其他方式进行json数据过滤,通过debug你会发现前台返回的json字符串,是把action中的所有属性全部转化成json字符串返回给客户端(包括service接口、pojo所有属性及有关联的pojo。有时候根本返回不了结果,也不报错,后台执行了,但前台执行不到callback function,这主要是因为找不到接口或者关联的pojo太多,造成死循环),一般情况下用的最多的就是root、includeProperties 和excludeNullProperties参数。当然还有其他的方法,如给pojo的属性加json注解。

注解的方式中,如何处理呢?

private Return ret;

@Action(value = "genUrl", results = { @Result(type = "json", params = {"root", "ret", "longUrl", "%{longUrl}", "expire", "%{expire}"}) })
public String generateUrl() {

}

参考:

1.《深入了解Struts2返回JSON数据的原理及具体应用范例 》

2.添加支持包时的bug汇总:http://hi.baidu.com/chennning/item/53d8f8b97c1fd5d985dd79de

3.jjiag22222的BLOG《HttpServletResponse中获取PrintWriter需要注意的一个小问题 》

4.struts2 零配置详解: http://topic.csdn.net/u/20101130/11/a47de200-c0e2-4daa-841b-5b2cb659f66b.html?45426

Struts2返回JSON对象的方法总结的更多相关文章

  1. (转)Struts2返回JSON对象的方法总结

    转自:http://kingxss.iteye.com/blog/1622455 如果是作为客户端的HTTP+JSON接口工程,没有JSP等view视图的情况下,使用Jersery框架开发绝对是第一选 ...

  2. ModelAndView返回json对象的方法

    这是在spring4之后. @RequestMapping(value = "/returnjson") public ModelAndView getfsd(){ ModelAn ...

  3. 深入了解Struts2返回JSON数据的原理

    首先来看一下JSON官方对于"JSON"的解释: JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.易于人阅读和编写.同时也易于机器解析 ...

  4. (转)Struts2返回JSON数据的具体应用范例

    转载自 yshjava的个人博客主页 <Struts2返回JSON数据的具体应用范例> 早在我刚学Struts2之初的时候,就想写一篇文章来阐述Struts2如何返回JSON数据的原理和具 ...

  5. Struts2返回JSON数据的具体应用范例

    早在我刚学Struts2之初的时候,就想写一篇文章来阐述Struts2如何返回JSON数据的原理和具体应用了,但苦于一直忙于工作难以抽身,渐渐的也淡忘了此事.直到前两天有同事在工作中遇到这个问题,来找 ...

  6. Struts2返回JSON数据的具体应用范…

    Struts2返回JSON数据的具体应用范例 博客分类: Struts2 Struts2JSON  早在我刚学Struts2之初的时候,就想写一篇文章来阐述Struts2如何返回JSON数据的原理和具 ...

  7. Spring MVC学习笔记——返回JSON对象

    1.想要GET请求返回JSON对象,首先需要导入jackson-all-1.9.4.jar包 2.在控制器中添加不同的show()方法 //show()方法返回JSON对象 @RequestMappi ...

  8. 转: .NET MVC3 几种返回 JSON 对象的方式和注意事项

    .NET MVC3 几种返回 JSON 对象的方式和注意事项 转自:http://blog.csdn.net/xxj_jing/article/details/7382589 引言在用 .NET MV ...

  9. Spring MVC 3.0 返回JSON数据的方法

    Spring MVC 3.0 返回JSON数据的方法1. 直接 PrintWriter 输出2. 使用 JSP 视图3. 使用Spring内置的支持// Spring MVC 配置<bean c ...

随机推荐

  1. QueryRunner使用

    在相继学习了JDBC和数据库操作之后,我们明显感到编写JDBC代码并非一件轻松的事儿.为了帮助我们更高效的学习工作,从JDBC的繁重代码中解脱出来,老佟给我们详尽介绍了一个简化JDBC操作的组件——D ...

  2. lucene 索引流程整理笔记

    索引的原文档(Document). 为了方便说明索引创建过程,这里特意用两个文件为例: 文件一:Students should be allowed to go out with their frie ...

  3. 关于spring 3.0.5的 <mvc:resources mapping="***" location="***">标签的使用

    spring mvc 的<mvc;resources mapping="***" location="***">标签是在spring3.0.4出现的 ...

  4. java生态环境

    Which Java package do I need? Software Developers: JDK (Java SE Development Kit). For Java Developer ...

  5. WebApiThrottle限流框架

    ASP.NET Web API Throttling handler is designed to control the rate of requests that clients can make ...

  6. 用户编辑新建_AngularJS实现

    实现思路: 分步骤完成开发,逐渐添加功能:1.实现输出users对象.2.实现点击“编辑”按钮,在表单中显示firstname和lastname,并不可修改.3.实现“新建用户”和“编辑用户”的切换. ...

  7. 《中日韩联合开发 - Asianux Server 3》(Asianux Server 3.0)[ISO]

    中文名: 中日韩联合开发 - Asianux Server 3英文名: Asianux Server 3.0资源格式: 光盘镜像发行时间: 2007年制作发行: 红旗软件(中国)MiracleLinu ...

  8. 使用匿名委托,Lambda简化多线程代码

    使用匿名委托,Lambda简化多线程代码   .net中的线程也接触不少了.在多线程中最常见的应用莫过于有一个耗时的操作需要放到线程中去操作,而在这个线程中我们需要更新UI,这个时候就要创建一个委托了 ...

  9. (转)【Android测试工具】03. ApkTool在Mac上的安装和使用(2.0版本)

    http://blog.csdn.net/wirelessqa/article/details/8997168 http://code.google.com/p/android-apktool/dow ...

  10. HTML 属性

    HTML 属性 HTML 元素 HTML 标题 属性为 HTML 元素提供附加信息. HTML 属性 HTML 标签可以拥有属性.属性提供了有关 HTML 元素的更多的信息. 属性总是以名称/值对的形 ...