下载地址: http://code.google.com/p/jsonplugin/downloads/list

              Apache提供的一个插件包,可以把Action中的数据以JSON做个封装然后返回。

它会将整个action中的变量转化为JSON数据(根对象在JSON中数据添加一个”root”标识)。如果要使用它,Action必须遵循以下几点:

1.       返回的页面类型中”content-type”必须是”application/json”.(这个已经Internet Community采用).

2.       JSON内容必须是符合格式要求的.

3.       Action中field必须有public的set方法.(是不是没有set方法就不会将field添加到JSON数据中,有待验证).

4.       它支持的类型有: 基本类型(int,long...String), Date, List, Map, Primitive Arrays, 其它class, 对象数组.

5.       在JSON中任何的Object会被封装在list或map中,数据会被封装程Long,如果是含有的数据则会被封装程Double,数组会被封装程List.

下面给出JSON的数据格式:

{

   "doubleValue": 10.10,

   "nestedBean": {

      "name": "Mr Bean"

   },

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

      "firstName": "El Zorro"

   }],

   "array": [10, 20]

}

说明:

a.       这个插件支持以下几个注释:

注释名

简介

默认值

序列化

反序列化

name

配置JSON中name

empty

yes

no

serialize

在serialization中

true

yes

no

deserialize

在deserialization中

true

no

yes

format

格式化Date字段

"yyyy-MM-dd'T'HH:mm:ss"

yes

yes

可以通过配置来显示指出要放在JSON中field,其中有个自己的验证规则需要研究.

<!-- 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>

b.       根对象

 <result type="json">

 <param name="root">

    person.job

 </param>

</result>

也可以使用拦截器配置操作父对象

<interceptor-ref name="json">

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

</interceptor-ref>

c.       将JSON数据用注释封装

如果wrapWithComments设置为true(默认值为false),则生成的JSON数据会变成这样:

/* {

   "doubleVal": 10.10,

   "nestedBean": {

      "name": "Mr Bean"

   },

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

      "firstName": "El Zorro"

   }],

   "array": [10, 20]

} */

这样做可以避免js中一些潜在的风险,使用时需要:

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

d.       父类

“root”对象中父类的field不会默认存放到JSON数据中,如果不想这样做,需要在配置时指定ignoreHierarchy为false:

<result type="json">

 <param name="ignoreHierarchy">false</param>

</result>

e.       枚举类型

默认处理枚举类型时,会被处理成JSON数据中name等于枚举中value而value等于枚举中name.

public enum AnEnum {

     ValueA,

     ValueB

 }

 JSON: "myEnum":"ValueA"

如果在处理枚举类型时,在xml中配置了enumAsBean,则会被当作一个Bean处理,在JSON数据中会有一个特别的属性”_name”值为name().这个枚举中的所有属性都会被处理.

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

Xml中配置:

<result type="json">

 <param name="enumAsBean">true</param>

</result>

f.        例子

a)         Action

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;

    }

}

b)        Xml配置

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

        <result type="json"/>

     </action>

 </package>

</struts>

这里有两个地方需要注意:

1)      需要继承json-default包

2)      <result>签的定义

c)         JSON数据

 { 

   "field1" : "str",

   "ints": [10, 20],

   "map": {

       "John":"Galt"

   },

   "newName": "custom"

}

d)        JSON RPC

JSON插件可以在js中调用action方法,返回执行结果。这个已经在dojo中有了实现,可以用Simple Method Definition调用远程服务。来一起看看下面的例子:

首先写一个Action:

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;

    }

}

e)         方法必须用SMDMethod加上注解,这样才能被远程调用,为了安全因素。这个方法会产生一个bean对象,实现修改价格的功能。Action被添加上SMD注解会生成一个SMD,同时参数也会被加上SMDMethodParameter注解。像你所看到的,Action中定义了一个空方法:smd。这个方法是作为Simple Method Definition (定义class中提供的服务),在struts.xml配置<result>时使用type属性值为”json”。

下面是bean的定义:

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;

    }

}

Xml文件:

<package name="RPC" namespace="/nodecorate" extends="json-default">

    <action name="SMDAction" class="smd.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>

这里需要注意一点:” enableSMD”这个必须在interceptor和result都要配置.

Js代码:

<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.name + " is " + bean.price);
    };
    //parameter
    var bean = {name: "Mocca"};
    //execute remote method
    var defered = service.doSomething(bean, 5);
    //attach callback to defered object
    defered.addCallback(callback);
</script>

JsonService会发出一个请求到action加载SMD,同时远程方法会返回一个JSON对象,这个过程是Dojo给action中的方法创建了一个Proxy。因为这是异步调用过程,当远程方法执行的时候,它会返回一个对象到callback方法中。

f)         代理的对象

当使用的注解不是继承自Java,可能你使用代理会出现一些问题。比如:当你使用aop拦截你的action的时候。在这种情况下,这个插件不会自动发现注解的方法。为了避免这种情况发生,你需要在xml中配置ignoreInterfaces为false,这样插件会自己查找注解的所有接口和父类。

注意:这个参数只有在Action执行的过程是通过注解来运行的时候才应该设为false。

<action name="contact" class="package.ContactAction" method="smd">

   <interceptor-ref name="json">

      <param name="enableSMD">true</param>

      <param name="ignoreInterfaces">false</param>

   </interceptor-ref>

   <result type="json">

      <param name="enableSMD">true</param>

      <param name="ignoreInterfaces">false</param>

   </result>

   <interceptor-ref name="default"/>

</action>

g)        使用方法

把插件的jar包copy到” /WEB-INF/lib”就可以了。

h)        版本历史

Version

Date

Author

Notes

0.19

Nov 02, 200t

musachy

Return a JSON error instead of execeptions. Some bug fixes and refactoring. Thanks Joe Germuka and the other anonymous guys

0.18

Aug 10, 2007

musachy

Add SMDMethodsHack, fix 16,18,21thanks for the patches guys!

0.17

Aug 10, 2007

Oleg Mikheev

Ignore properties matching 'excludedProperties' when generating SMD

0.16

Jul 27, 2007

musachy

Resolve issue where method is evaluated even if its result is ignored (#14)

0.15

Jul 18, 2007

musachy

Add excludedProperties attribute to interceptor

0.14

Jun 27, 2007

musachy

Add root (OGNL expression) attribute to interceptor

0.13

Jun 14, 2007

musachy

Add 'ignoreHierarchy' property to JSON result to allow serialization of properties on base classes