IT忍者神龟之Struts2-Json-Plugin 的使用(翻译自官方文档)

在 Struts2 中要使用 Ajax 获得 Json 数据我认为目前还是 struts2-json-plugin 了。当然你你可以用手工用像 XStreamGoogle
Gson
Jackson 这样的工具手工把 Java
对象转换成 Json 字符串再写往 Response 去,要写的代码自然多不了,还得留心字符集与 content type。而 struts2-json-plugin 毫无疑问是与 Struts2 最亲近了,只需你配置一些属性就能得到你想的结果。

本想分几篇逐步介绍如何使用 struts2-json-plugin 的,然而就在现在发现官方的 struts2-json-plugin 指南已经很详细了,所以干脆翻译一下http://struts.apache.org/2.2.1.1/docs/json-plugin.html,同时自己加深对它的理解。

JSON 插件提供了一个 "json" 结果类型来把 action 序列化成 JSON. 这一序列化的过程是递归的, 意即整个对象图,从 action 类开始 (未包括基类) 将会被序列化 (可以用 "root" 属性来指定自己的根对象). 如果使用了 json 拦截器, action 将可通过请求中的 JSON 内容组装出来, 该拦截器遵循以下几条规则:

  1. "content-type" 必须为 "application/json"
  2. JSON 内容必须是格式良好的, 参考 json.org 中的语法.
  3. Action 里必须有欲获取值的属性的相应 public 的 "setter" 方法.
  4. 所支持的类型有: 原始类型 (int,long...String), Date, List, Map, 原始类型数组, 其他的类 (将会支持更多), 和其他类型的数组.
  5. JSON 中的任何将要被填入到 list 或 map 中的对象会是 Map 类型(属性映射到值), 任何整数都是 Long 类型, 任何小数会是 Double 类型, 任何数组会是 List 类型.

给定下面的 JSON 字符串:


01

02

03

04

05

06

07

08

09

10

{

   "doubleValue":
10.10,

   "nestedBean":
{

      "name":
"Mr Bean"

   },

   "list":
["A", 10, 20.20, {

      "firstName":
"El Zorro"

   }],

   "array":
[10, 20]

}

action 中必须有一个 "setDoubleValue" 方法, 参数为 "float" 或者 "double"(拦截器将会把值转换为相应的类型). 还必须有一个 "setNestedBean" 方法,它的参数类型可以为任何类类型, 其中含有参数为 "String" 的 "setName" 方法. 还必须有一个参数为 "List" 的 "setList" 方法, 这个 List 中将会包含: "A" (String), 10 (Long), 20.20 (Double), Map ("firstName"
-> "El Zorro"). "setArray" 方法可以是 "List", 或任何数字类型数组作参数的.

序列化你的对象成 javascript 的 JSON, 参考 json2

安装

本插件可通过把插件  jar 包到你的应用的 /WEB-INF/lib 目录来完成安装. 没有别的文件需要拷贝或被创建.

使用 maven 的话, 加入下列到你的 pom 中:


1

2

3

4

5

6

7

8

9

<dependencies>

   ...

   <dependency>

       <groupId>org.apache.struts</groupId>

       <artifactId>struts2-json-plugin</artifactId>

       <version>STRUTS_VERSION</version>

   </dependency>

   ...

</dependencies>

定制化序列化和反序列化

使用 JSON 注解来达到定制序列化和反序列化过程. 可用的 JSON 注解如下:

名称 描述 默认值 序列化 反序列化
name 定制字段名 empty yes no
serialize 标识为可被序列化 true yes no
deserialize 标识为可被反序列化 true no yes
format 用于格式化或解析 Date 字段的格式 "yyyy-MM-dd‘T‘HH:mm:ss" yes yes

排除属性

逗号分隔的正则表达式列表可传递给 JSON Result 和 Interceptor(拦截器), 被任何 一个正则表达式匹配的属性将会在序列化过程时忽略掉:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

<!--
Result fragment -->

<result

type
="json">

  <param

name
="excludeProperties">

    login.password,

    studentList.*\.sin

  </param>

</result>

<!--
Interceptor fragment -->

<interceptor-ref

name
="json">

  <param

name
="enableSMD">true</param>

  <param

name
="excludeProperties">

    login.password,

    studentList.*\.sin

  </param>

</interceptor-ref>

包含属性

逗号分隔的正则表达式列表可被传递给 JSON Result, 用于限制哪些属性可用于序列化. 只有当能够匹配任何一个正则表达式的属性才会包含在序列化输出中.

注:

排除属性表达式优先于包含属性的表达式.  那就是说, 如果包含和排除表达式应用于同一个结果, 包含表达式对于被排除表达式匹配到的属性是不起作用的.


1

2

3

4

5

6

7

8

<!--
Result fragment -->

<result

type
="json">

  <param

name
="includeProperties">

    ^entries\[\d+\]\.clientNumber,

    ^entries\[\d+\]\.scheduleNumber,

    ^entries\[\d+\]\.createUserId

  </param>

</result>

根对象

使用 "root" 属性(OGNL 表达式) 指定被用于序列化的根对象.


1

2

3

4

5

<result

type
="json">

  <param

name
="root">

    person.job

  </param>

</result>

"root" 属性(OGNL 表达式) 也可以用于拦截器来指定被组装的对象, 确保这个对象不会是 null.


1

2

3

<interceptor-ref

name
="json">

  <param

name
="root">bean1.bean2</param>

</interceptor-ref>

包装

可能会有某些原因,你想要用些文本对 JSON 输出包装一下, 像用注释包裹, 加上前缀, 或使用文件上载让结果显示在 textarea 之中. 用 wrapPrefix 在开始处加上内容,wrapPostfix 添加内容在尾端.
这两个参数优先使用,而  "wrapWithComments" 和 "prefix" 自从 0.34 后就不推荐使用. 例子:

进行注释:


1

2

3

4

<result

type
="json">

  <param

name
="wrapPrefix">/*</param>

  <param

name
="wrapSuffix">*/</param>

</result>

添加前缀:


1

2

3

<result

type
="json">

  <param

name
="wrapPrefix">{}&&</param>

</result>

包裹上传的文件内容:


1

2

3

4

<result

type
="json">

  <param

name
="wrapPrefix"><![CDATA[<html><body><textarea>]]></param>

  <param

name
="wrapSuffix"><![CDATA[</textarea></body></html>]]></param>

</result>

包裹以注释

wrapWithComments 自 0.34 不推荐使用, 建议用 wrapPrefix 和 wrapSuffix.
wrapWithComments 可使得安全的 JSON 文本变得不安全. 例如,

["*/ alert(‘XSS‘); /*"]

谢谢 Douglas Crockford 的提示! 应考虑用 prefix.

假如被序列化的 JSON 是 {name: ‘El Zorro‘}. 那么此时输出就会是: {}&&
({name: ‘El Zorro‘}

假如 "wrapWithComments" (默认为 false) 属性被设为 true, 生成的被包裹上注释的 JSON 就如下:


01

02

03

04

05

06

07

08

09

10

/*
{

   "doubleVal":
10.10,

   "nestedBean":
{

      "name":
"Mr Bean"

   },

   "list":
["A", 10, 20.20, {

      "firstName":
"El Zorro"

   }],

   "array":
[10, 20]

}
*/

欲取消上面的注释,可用:

var responseObject = eval("("+data.substring(data.indexOf("\/\*")+2, data.lastIndexOf("\*\/"))+")");

前缀

prefix 从 0.34 后不建议用, 请用 wrapPrefix 和 wrapSuffix.

假如参数 prefix 被设置为 true, 生成的 JSON 将被附上前缀 "{}&& ". 这有助于防止被劫持. 详细内容请看 this
Dojo Ticket
:


1

2

3

<result

type
="json">

  <param

name
="prefix">true</param>

</result>

基类

默认时,定义在 "root" 对象的基类中的属性不会被序列化, 要序列化来自于所有基类(直到 Object) 中的属性,需在 JSON result 里设置 "ignoreHierarchy" 为 false:


1

2

3

<result

type
="json">

  <param

name
="ignoreHierarchy">false</param>

</result>

枚举类型

默认的, Enum 被序列化为 name=value 对,这里的 value = name().


1

2

3

4

  public
enum AnEnum {

     ValueA,

     ValueB

  }

JSON: "myEnum":"ValueA"

使用 result 的参数 "enumAsBean" 可使得 Enum 像一个 bean 一样的被序列化,特定的属性为 _name,值为 name().  所有的枚举属性都会被序列化.


01

02

03

04

05

06

07

08

09

10

  public

enum

AnEnum {

     ValueA("A"),

     ValueB("B");

     private

String val;

     public

AnEnum(val) {
this.val
= val; }
public

getVal() {

        return

val;

     }

   }

JSON: myEnum: { "_name": "ValueA", "val": "A" }

在 struts.xml 中启用该参数:


1

2

3

<result

type
="json">

  <param

name
="enumAsBean">true</param>

</result>

压缩输出.

设置 enableGZIP 属性为 true 可用 gzip 压缩响应输出. 在请求后 "Accept-Encoding" 头中必须包含  "gzip" 才能正常工作.


1

2

3

<result

type
="json">

  <param

name
="enableGZIP">true</param>

</result>

防止浏览器缓存响应数据

noCache 设置为 true(默认为 false) 会设置如下响应头:

  • Cache-Control: no-cache
  • Expires: 0
  • Pragma: No-cache

1

2

3

<result

type
="json">

  <param

name
="noCache">true</param>

</result>

排除值为 null 的属性

默认的,为 null 的字段也被序列化,生成像 {property_name: null}. 这能够通过设置 excludeNullProperties 为 true 来防止.


1

2

3

<result

type
="json">

  <param

name
="excludeNullProperties">true</param>

</result>

状态和错误代码

使用 statusCode 来设置响应状态代码:


1

2

3

<result

type
="json">

  <param

name
="statusCode">304</param>

</result>

同时可用 errorCode 来发送一个错误(the server might end up sending something to the client which is not the serialized JSON):


1

2

3

<result

type
="json">

  <param

name
="errorCode">404</param>

</result>

JSONP

To enable JSONP, set the parameter callbackParameter in either the JSON Result or the Interceptor. A parameter with that name will be read
from the request, and it value will be used as the JSONP function. Assuming that a request is made with the parameter "callback"="exec":


1

2

3

<result

type
="json">

  <param

name
="callbackParameter">callback</param>

</result>

And that the serialized JSON is {name: ‘El Zorro‘}. Then the output will be: exec({name:
‘El Zorro‘})

Content Type

Content type will be set to application/json-rpc by default if SMD is being used, or application/json otherwise.
Sometimes it is necessary to set the content type to something else, like when uploading files with Dojo and YUI. Use the contentType parameter
in those cases.


1

2

3

<result

type
="json">

  <param

name
="contentType">text/html</param>

</result>

Example

Setup Action

This simple action has some fields:

Example:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

import

java.util.HashMap;

import

java.util.Map;

import

com.opensymphony.xwork2.Action;

public

class

JSONExample {

    private

String field1 =
"str";

    private

int
[]
ints = {
10,
20};

    private

Map map =
new

HashMap();

    private

String customName =
"custom";

    //‘transient‘
fields are not serialized

    private

transient

String field2;

    //fields
without getter method are not serialized

    private

String field3;

    public

String execute() {

        map.put("John",
"Galt");

        return

Action.SUCCESS;

    }

    public

String getField1() {

        return

field1;

    }

    public

void

setField1(String field1) {

        this.field1
= field1;

    }

    public

int
[]
getInts() {

        return

ints;

    }

    public

void

setInts(
int[]
ints) {

        this.ints
= ints;

    }

    public

Map getMap() {

        return

map;

    }

    public

void

setMap(Map map) {

        this.map
= map;

    }

    @JSON(name="newName")

    public

String getCustomName() {

        return

this
.customName;

    }

}

Write the mapping for the action

  1. Add the map inside a package that extends "json-default"
  2. Add a result of type "json"

Example:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

<?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
="example" 

extends
="json-default">

     <action

name
="JSONExample">

        <result

type
="json"/>

     </action>

  </package>

</struts>

JSON example output


1

2

3

4

5

6

7

8

   "field1"

:
"str",

   "ints":
[10, 20],

   "map":
{

       "John":"Galt"

   },

   "newName":
"custom"

}

JSON RPC

The json plugin can be used to execute action methods from javascript and return the output. This feature was developed with Dojo in mind, so it uses Simple Method Definition to advertise the remote service. Let‘s work it out with an example(useless as most
examples).

First write the action:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

package

smd;

import

com.googlecode.jsonplugin.annotations.SMDMethod;

import

com.opensymphony.xwork2.Action;

public

class

SMDAction {

    public

String smd() {

        return

Action.SUCCESS;

    }

    @SMDMethod

    public

Bean doSomething(Bean bean,
int

quantity) {

        bean.setPrice(quantity
*
10);

        return

bean;

    }

}

Methods that will be called remotely must be annotated with the SMDMethod annotation,
for security reasons. The method will take a bean object, modify its price and return it. The action can be annotated with the SMD annotation
to customize the generated SMD (more on that soon), and parameters can be annotated withSMDMethodParameter. As you can see, we have a "dummy", smd method.
This method will be used to generate the Simple Method Definition (a definition of all the services provided by this class), using the "json" result.

The bean class:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package

smd;

public

class

Bean {

    private

String type;

    private

int

price;

    public

String getType() {

        return

type;

    }

    public

void

setType(String type) {

        this.type
= type;

    }

    public

int

getPrice() {

        return

price;

    }

    public

void

setPrice(
int

price) {

        this.price
= price;

    }

}

The mapping:


01

02

03

04

05

06

07

08

09

10

<package

name
="RPC"

namespace
="/nodecorate"

extends
="json-default">

    <action

name
="SMDAction"

method
="smd">

        <interceptor-ref

name
="json">

            <param

name
="enableSMD">true</param>

        </interceptor-ref>

        <result

type
="json">

             <param

name
="enableSMD">true</param>

        </result>

    </action>

</package>

Nothing special here, except that both the interceptor and the result must be applied to the action, and "enableSMD" must be enabled for both.

Now the javascript code:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

<s:url
id=
"smdUrl"

namespace=
"/nodecorate"

action=
"SMDAction"

/>

<script
type=
"text/javascript">

    //load
dojo RPC

    dojo.require("dojo.rpc.*");

    //create
service object(proxy) using SMD (generated by the json result)

    var

service =
new

dojo.rpc.JsonService(
"${smdUrl}");

    //function
called when remote method returns

    var

callback =
function(bean)
{

        alert("Price
for "

+ bean.type +
"
is "

+ bean.price);

    };

    //parameter

    var

bean = {type:
"Mocca"};

    //execute
remote method

    var

defered = service.doSomething(bean, 5);

    //attach
callback to defered object

    defered.addCallback(callback);

</script>

Dojo‘s JsonService will make a request to the action to load the SMD, which will return a JSON object with the definition of the available remote methods, using that information Dojo creates a "proxy" for those methods. Because of the asynchronous nature of
the request, when the method is executed, a deferred object is returned, to which a callback function can be attached. The callback function will receive as a parameter the object returned from your action. That‘s it.

Proxied objects

As annotations are not inherited in Java, some user might experience problems while trying to serialize objects that are proxied. eg. when you have attached AOP interceptors to your action.

In this situation, the plugin will not detect the annotations on methods in your action.

To overcome this, set the "ignoreInterfaces" result parameter to false (true by default) to request that the plugin inspects all interfaces and superclasses of the action for annotations on the action‘s methods.

NOTE: This parameter should only be set to false if your action could be a proxy as there is a performance cost caused by recursion through the interfaces.


01

02

03

04

05

06

07

08

09

10

11

<action

name
="contact"

method
="smd">

   <interceptor-ref

name
="json">

      <param

name
="enableSMD">true</param>

      <param

name
="ignoreSMDMethodInterfaces">false</param>

   </interceptor-ref>

   <result

type
="json">

      <param

name
="enableSMD">true</param>

      <param

name
="ignoreInterfaces">false</param>

   </result>

   <interceptor-ref

name
="default"/>

</action>

时间: 2024-11-05 20:36:43

IT忍者神龟之Struts2-Json-Plugin 的使用(翻译自官方文档)的相关文章

记录struts2 json plugin 对字符串数组类型的处理

当前项目中,一开发人员在action中定义了一个字符串数组类型的属性,需要以json格式返回到页面(先不论这种设计是否最优), 结果在做页面调试时发现总是无法在ajax的success方法中获取到该属性.后台action调试发现该数组已经正确赋值,get 方法也没有问题,action相关代码如下: private String[] traceList; private String unitName; ... public String queryTrace(){ unitName = "tes

IT忍者神龟之Struts2.xml配置完全正确流程能走通但是有红叉解决

一:Multiple annotations found at this line:Undefined actionName  parameter  Undefined actionnamespace  parameter 这个红色叉叉报错: Multiple annotations found at this line: - Undefined actionName parameter - Undefined actionnamespace parameter 但是不影响功能,但是配置多了,很

Kooboo中怎么写Page Plugin -摘自官方文档

Page plugin development Page plugin is an add-on to Kooboo CMS, and is responsible for making data source available for page access. It is similar to module, but while module contains user interface for both the backend and the frontend site, page pl

【cocos2d-js官方文档】二十、moduleConfig.json

概述 该配置文件相当于v2版本中的jsloader.js.改造的目的是为了使得配置纯粹化,同时也能比较好的支持cocos-console.cocos-utils甚至是用户自定义脚本工具. 字段说明 module 配置各个模块的js列表.key名即为模块名称.这些key名将会在project.json的modules字段中使用.倘若不清楚project.json里面究竟有哪些模块可以配置,就可以直接查看该文件. 每个模块的配置对象是一个数组,数组项分两种,一种是模块名,一种是js路径. 例如: "

【cocos2d-js官方文档】二十二、project.json

概述 项目相关配置,由原来的cocos2d.js中转移到project.json中,该文件需要与index.html同级,一般建议放在根目录下. 由原来的cocos2d.js改为project.json的好处如下: 原来的cocos2d.js中参杂了一些逻辑代码,无法很好体现其就是作为项目的配置文件. 用json格式作为配置文件会更友好.也可以使得cocos-console.cocos-utils,甚至是用户自定义脚本工具能够使用同一个配置文件. 字段说明 debugMode 相当于原来的COC

Android+struts2+JSON方式的手机开发(Login)

在手机的后台服务无论是调用WebService还是Http请求,多数都是采用Android的HttpClient实现相关的调用实现.本文实现Android+Struts2+JSON方式实现为手机前台提供服务. 涉及的知识点: 1.Struts2框架的搭建(包括Struts2的jSON插件) 2.Android前台访问Web采用HttpClient方式. 3.Android采用JSON的解析. 功能:模拟远程登录流程: 手机后台服务:由于采用Struts2的JSON响应格式,响应详细会自动转变为J

Class org.apache.struts2.json.JSONWriter can not access a member of class org.springframework.aop.TruePointcut with modifiers &quot;public&quot;

Spring注入Action使用Json错误:org.apache.struts2.json.JSONException: org.apache.struts2.json.JSONException: org.apache.struts2.json.JSONException: org.apache.struts2.json.JSONException: java.lang.IllegalAccessException: Class org.apache.struts2.json.JSONWri

org.apache.struts2.json.JSONWriter can not access a member of class org.apache.commons.dbcp...

之前在用ssh整合json时一直发现前台获取不到json的返回数据,直接运行action出现以下错误: HTTP Status 500 - org.apache.struts2.json.JSONException: org.apache.struts2.json.JSONException: org.apache.struts2.json.JSONException: org.apache.struts2.json.JSONException: org.apache.struts2.json.

struts2 json 输出日期格式不正确

struts2 输出json中 日期出现:2013-12-17T15:57:47 错误格式的数据 原因:struts2 json插件对日期的格式化有问题 解决方法:在实体类的日期的get方法上加注解:@JSON(format="yy-MM-dd HH:mm:ss") 例如: @JSON(format = "yy-MM-dd HH:mm:ss") public Date getFindtime() { return findtime; }