struts2-json-plugin2.2.1使用的几个问题

首先我们看看struts2中插件struts2-json-plugin2.2.1的使用:

1、xml配置:当然package还是得直接或间接继承自json-default

 

<action name="list" class="com.sample.s2web.action.user.UserAction" method="list" >
 <result name="success" type="json">
		<param name="ignoreHierarchy">true</param>
		<param name="excludeNullProperties">true</param>//排除空值属性
		<param name="excludeProperties">message</param>//排除属性
		<param name="includeProperties"></param>//包含属性,默认包含所有属性,如果此处填了属性那么就只会包含该
		<param name="root">pageModule</param>
<result>
</action>

 

2、UserAction.java中的list方法:

    public String list() {
        try {
            pageModule = new PageModule<User>();
            pageModule.setList(null);
            pageModule.setTotalCount(30);
            pageModule.setPage(1);
            pageModule.setMessage("Result");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return SUCCESS;
    }

 

我们来分析下配置和相应的结果:

1、按照上面的配置,这里对root对象pageModule会忽略父类对象:

<param name="ignoreHierarchy">true</param>//default : true

这里主要是内省(Introspector),见JSONWriter中的方法:bean

            info = ((object == this.root) && this.ignoreHierarchy) ? Introspector.getBeanInfo(clazz, clazz
                    .getSuperclass()) : Introspector.getBeanInfo(clazz);

当然这里只会忽略root的父类,而该root的成员变量如果也有继承那就不会被忽略

考虑如下类图:

struts2-json-plugin2.2.1使用的几个问题

我们将Student作为root对象,看看采用上面配置生产的JSON:

        Student s = new Student();
        s.setBirthday(new Date());
        s.setUsername("robin");
        s.setAddress("chengdu");
        s.setSalary(12345);
        s.setTitle("SEE");
        
        SCourse c = new SCourse();
        c.setName("JAVA");
        c.setDesc("xxxx");
        c.setPrice(123);
        c.setPublish("Null");
        s.setCourse(c);
        
        return s;

    JSONUtil.serialize(format(), null, null, true, false, false)
//{"address":"chengdu","course":{"desc":"xxxx","name":"JAVA","price":123,"publish":"Null"},"courses":null,"salary":12345,"title":"SEE"}

 由此可见,在生产的JSON串中只忽略了root对象Student的父类,而没有忽略成员变量course的父类。这也是导致某些JSON转换中循环引用的问题。我们来看看JSONWriter.java中的源码:

//通过内省来对bean属性进行序列化
private void bean(Object object) throws JSONException {
        this.add("{");

        BeanInfo info;

        try {
            Class clazz = object.getClass();
            //关键在这里,是否需要忽略父类,这里有两个条件:当前bean是否是根对象和ignoreHierarchy的设置。在下次递归调用此方法时object就不再是root
            info = ((object == this.root) && this.ignoreHierarchy) ? Introspector.getBeanInfo(clazz, clazz
                    .getSuperclass()) : Introspector.getBeanInfo(clazz);

            PropertyDescriptor[] props = info.getPropertyDescriptors();

            boolean hasData = false;
            for (int i = 0; i < props.length; ++i) {
                PropertyDescriptor prop = props[i];
                String name = prop.getName();
                Method accessor = prop.getReadMethod();
                Method baseAccessor = null;
                if (clazz.getName().indexOf("$$EnhancerByCGLIB$$") > -1) {
                    try {
                        baseAccessor = Class.forName(
                                clazz.getName().substring(0, clazz.getName().indexOf("$$"))).getMethod(
                                accessor.getName(), accessor.getParameterTypes());
                    } catch (Exception ex) {
                        LOG.debug(ex.getMessage(), ex);
                    }
                } else if (clazz.getName().indexOf("$$_javassist") > -1) {
                	try {
                        baseAccessor = Class.forName(
                                clazz.getName().substring(0, clazz.getName().indexOf("_$$")))
                                .getMethod(accessor.getName(), accessor.getParameterTypes());
                    } catch (Exception ex) {
                        LOG.debug(ex.getMessage(), ex);
                    }
                } else
                    baseAccessor = accessor;

                if (baseAccessor != null) {

                    JSON json = baseAccessor.getAnnotation(JSON.class);
                    if (json != null) {
                        if (!json.serialize())
                            continue;
                        else if (json.name().length() > 0)
                            name = json.name();
                    }

                    // ignore "class" and others
                    if (this.shouldExcludeProperty(clazz, prop)) {
                        continue;
                    }
                    String expr = null;
                    if (this.buildExpr) {
                        expr = this.expandExpr(name);
                        if (this.shouldExcludeProperty(expr)) {
                            continue;
                        }
                        expr = this.setExprStack(expr);
                    }

                    Object value = accessor.invoke(object, new Object[0]);
                    boolean propertyPrinted = this.add(name, value, accessor, hasData);
                    hasData = hasData || propertyPrinted;
                    if (this.buildExpr) {
                        this.setExprStack(expr);
                    }
                }
            }

            // special-case handling for an Enumeration - include the name() as
            // a property */
            if (object instanceof Enum) {
                Object value = ((Enum) object).name();
                this.add("_name", value, object.getClass().getMethod("name"), hasData);
            }
        } catch (Exception e) {
            throw new JSONException(e);
        }

        this.add("}");
    }

在工作中也曾遇到一个问题,就是作为当然root对象的一个成员变量的父类,在一个public getXXX(可序列号的方法)会导致NPE,而一直抛出异常java.lang.reflect.InvocationTargetException导致困扰不短时间,就是没搞清楚这个时候的父类不会被忽略。还有一个问题就是同事在做用户权限时,基于RBAC的用户权限,在设计时子类和父类存在多对多的映射,导致在JSON转换时抛出异常,很明显这就是循环引用的问题。

 

 

 

2、接下来的excludeNullProperties指明了在root中(这里pageModule)如果有空值将被忽略,同时接下来的两个属性采用正则匹配排除root中的属性不进行json转化,而includeProperties则指明了包含的属性,默认会全部(当然如果excludeNullProperties=true对于为null或空值的属性不会进行JSON转化)。

如上面的一个配置,对于Action中的方法,那么最终的JSON为:

{"page":1,"totalCount":30}

 

 

3、上面是自己在使用时常用的几个参数,在JSONInterceptor类中还包含其他几个,这里介绍一二:

wrapWithComments:将JSON添加注释:/* {..json..}  */单并不推荐使用,在使用时需要eval(),可能会有XSS攻击

prefix: 生成前缀,如果设置为true,会在json加上前缀"{}&& "这有助于防止被劫持.

enableGZIP:压缩输出

前面说了JSON插件的基本配置,在JSONInterceptor中对root或json进行转换的是JSONUtil,主要是调用了

Object obj = JSONUtil.deserialize(request.getReader());


String json = JSONUtil.serialize(result, excludeProperties, includeProperties,
                        ignoreHierarchy, excludeNullProperties);

在JSONUtil中提供了JSON来对需要序列号的root进行设置:

   注释名                  说明                            默认值               
name                          配置JSONname                        ""
serialize 可序列化serialize true
deserialize 不进行序列号 true
format Date格式化 yyyy-MM-dd'T'HH:mm:ss

对上面的进行设置:

    @JSON(format="yyyy-MM-dd HH:mm:ss")
    public Date getCreateDay() {
        return createDay;
    }
[JSON]{"createDay":"2012-07-14 21:54:27","message":"Result","page":1,"totalCount":30}
 

 


你可能感兴趣的:(json,struts2,JsonUtil)