Struts2插件 —— struts2-json-plugin-2.x.x

本文介绍了如何在Struts2中使用JSON插件进行AJAX数据交换,包括需要引入的JAR包struts2-json-plugin-2.x.x.jar,修改Struts2配置文件,设置package的extends属性为'json-default',以及配置result元素的type属性为'json'。同时提到了通过<interceptor-ref name="json" />引用JSON插件的拦截器处理JSON文本提交的请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了方便AJAX与服务器进行数据交换,struts2中加入的json插件可用于对象的序列化和反序列化。


相关JAR

struts2-json-plugin.2.x.x.jar


修改Struts2配置文件

  • 更改package元素的extends属性
    <package name="default" extends="json-default">
  • 配置result元素的type属性为json,将把Action中属性序列化返回
    <result type="json" /> 
  • 如果需要处理以JSON文本提交的请求,可在action元素中添加子元素interceptor引用JSON插件包中声明的拦截器:json。
    <interceptor-ref name="json" />
    

result type参数

result type对应的类为:org.apache.struts2.json.JSONInterceptor。下面是一些常用的result param

root

<paramname="root">person</param>

excludeNullProperties 

是否去掉值为null的属性, 默认值为false

<paramname="excludeNullProperties">true</param>

ignoreHierarchy 

是否忽略继承关系,ignoreHierarchy值默认为true。设置为false后,父类的属性也将被构建在JSON字符串中。

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

includeProperties

响应结果需要包含的属性值,支持正则表达式,可用","分隔以使用多个正则表达式匹配。

<paramname="includeProperties">person.*, person\.name</param>

excludeProperties 

响应结果需要排除的属性值,支持正则表达式,可用“,”分隔以使用多个正则表达式匹配。

JSONInterceptor源码:

/**
 * Populates an action from a JSON string
 */
public class JSONInterceptor extends AbstractInterceptor {
    private static final long serialVersionUID = 4950170304212158803L;
    private static final Logger LOG = LoggerFactory.getLogger(JSONInterceptor.class);
    private boolean enableSMD = false;
    private boolean enableGZIP = false;
    private boolean wrapWithComments;
    private boolean prefix;
    private String defaultEncoding = "ISO-8859-1";
    private boolean ignoreHierarchy = true;
    private String root;
    private List<Pattern> excludeProperties;
    private List<Pattern> includeProperties;
    private boolean ignoreSMDMethodInterfaces = true;
    private JSONPopulator populator = new JSONPopulator();
    private JSONCleaner dataCleaner = null;
    private boolean debug = false;
    private boolean noCache = false;
    private boolean excludeNullProperties;
    private String callbackParameter;
    private String contentType;

    @SuppressWarnings("unchecked")
    public String intercept(ActionInvocation invocation) throws Exception {
        HttpServletRequest request = ServletActionContext.getRequest();
        HttpServletResponse response = ServletActionContext.getResponse();
        String contentType = request.getHeader("content-type");
        if (contentType != null) {
            int iSemicolonIdx;
            if ((iSemicolonIdx = contentType.indexOf(";")) != -1)
                contentType = contentType.substring(0, iSemicolonIdx);
        }

        Object rootObject = null;
        if (this.root != null) {
            ValueStack stack = invocation.getStack();
            rootObject = stack.findValue(this.root);

            if (rootObject == null) {
                throw new RuntimeException("Invalid root expression: '" + this.root + "'.");
            }
        }

        if ((contentType != null) && contentType.equalsIgnoreCase("application/json")) {
            // load JSON object
            Object obj = JSONUtil.deserialize(request.getReader());

            if (obj instanceof Map) {
                Map json = (Map) obj;

                // clean up the values
                if (dataCleaner != null)
                    dataCleaner.clean("", json);

                if (rootObject == null) // model overrides action
                    rootObject = invocation.getStack().peek();

                // populate fields
                populator.populateObject(rootObject, json);
            } else {
                LOG.error("Unable to deserialize JSON object from request");
                throw new JSONException("Unable to deserialize JSON object from request");
            }
        } else if ((contentType != null) && contentType.equalsIgnoreCase("application/json-rpc")) {
            Object result;
            if (this.enableSMD) {
                // load JSON object
                Object obj = JSONUtil.deserialize(request.getReader());

                if (obj instanceof Map) {
                    Map smd = (Map) obj;

                    if (rootObject == null) // model makes no sense when using RPC
                        rootObject = invocation.getAction();

                    // invoke method
                    try {
                        result = this.invoke(rootObject, smd);
                    } catch (Exception e) {
                        RPCResponse rpcResponse = new RPCResponse();
                        rpcResponse.setId(smd.get("id").toString());
                        rpcResponse.setError(new RPCError(e, RPCErrorCode.EXCEPTION, getDebug()));

                        result = rpcResponse;
                    }
                } else {
                    String message = "SMD request was not in the right format. See http://json-rpc.org";

                    RPCResponse rpcResponse = new RPCResponse();
                    rpcResponse.setError(new RPCError(message, RPCErrorCode.INVALID_PROCEDURE_CALL));
                    result = rpcResponse;
                }
            } else {
                String message = "Request with content type of 'application/json-rpc' was received but SMD is "
                        + "not enabled for this interceptor. Set 'enableSMD' to true to enable it";

                RPCResponse rpcResponse = new RPCResponse();
                rpcResponse.setError(new RPCError(message, RPCErrorCode.SMD_DISABLED));
                result = rpcResponse;
            }

            String json = JSONUtil.serialize(result, excludeProperties, getIncludeProperties(),
                    ignoreHierarchy, excludeNullProperties);
            json = addCallbackIfApplicable(request, json);
            boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);
            JSONUtil.writeJSONToResponse(new SerializationParams(response, this.defaultEncoding,
                    this.wrapWithComments, json, true, writeGzip, noCache, -1, -1, prefix, "application/json"));

            return Action.NONE;
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Content type must be 'application/json' or 'application/json-rpc'. " +
                          "Ignoring request with content type " + contentType);
            }
        }

        return invocation.invoke();
    }

    @SuppressWarnings("unchecked")
    public RPCResponse invoke(Object object, Map data) throws IllegalArgumentException,
            IllegalAccessException, InvocationTargetException, JSONException, InstantiationException,
            NoSuchMethodException, IntrospectionException {

        RPCResponse response = new RPCResponse();

        // validate id
        Object id = data.get("id");
        if (id == null) {
            String message = "'id' is required for JSON RPC";
            response.setError(new RPCError(message, RPCErrorCode.METHOD_NOT_FOUND));
            return response;
        }
        // could be a numeric value
        response.setId(id.toString());

        // the map is going to have: 'params', 'method' and 'id' (for the
        // client to identify the response)
        Class clazz = object.getClass();

        // parameters
        List parameters = (List) data.get("params");
        int parameterCount = parameters != null ? parameters.size() : 0;

        // method
        String methodName = (String) data.get("method");
        if (methodName == null) {
            String message = "'method' is required for JSON RPC";
            response.setError(new RPCError(message, RPCErrorCode.MISSING_METHOD));
            return response;
        }

        Method method = this.getMethod(clazz, methodName, parameterCount);
        if (method == null) {
            String message = "Method " + methodName + " could not be found in action class.";
            response.setError(new RPCError(message, RPCErrorCode.METHOD_NOT_FOUND));
            return response;
        }

        // parameters
        if (parameterCount > 0) {
            Class[] parameterTypes = method.getParameterTypes();
            Type[] genericTypes = method.getGenericParameterTypes();
            List invocationParameters = new ArrayList();

            // validate size
            if (parameterTypes.length != parameterCount) {
                // size mismatch
                String message = "Parameter count in request, " + parameterCount
                        + " do not match expected parameter count for " + methodName + ", "
                        + parameterTypes.length;

                response.setError(new RPCError(message, RPCErrorCode.PARAMETERS_MISMATCH));
                return response;
            }

            // convert parameters
            for (int i = 0; i < parameters.size(); i++) {
                Object parameter = parameters.get(i);
                Class paramType = parameterTypes[i];
                Type genericType = genericTypes[i];

                // clean up the values
                if (dataCleaner != null)
                    parameter = dataCleaner.clean("[" + i + "]", parameter);

                Object converted = populator.convert(paramType, genericType, parameter, method);
                invocationParameters.add(converted);
            }

            response.setResult(method.invoke(object, invocationParameters.toArray()));
        } else {
            response.setResult(method.invoke(object, new Object[0]));
        }

        return response;
    }

    @SuppressWarnings("unchecked")
    private Method getMethod(Class clazz, String name, int parameterCount) {
        Method[] smdMethods = JSONUtil.listSMDMethods(clazz, ignoreSMDMethodInterfaces);

        for (Method method : smdMethods) {
            if (checkSMDMethodSignature(method, name, parameterCount)) {
                return method;
            }
        }
        return null;
    }

    /**
     * Look for a method in clazz carrying the SMDMethod annotation with
     * matching name and parametersCount
     * 
     * @return true if matches name and parameterCount
     */
    private boolean checkSMDMethodSignature(Method method, String name, int parameterCount) {

        SMDMethod smdMethodAnntotation = method.getAnnotation(SMDMethod.class);
        if (smdMethodAnntotation != null) {
            String alias = smdMethodAnntotation.name();
            boolean paramsMatch = method.getParameterTypes().length == parameterCount;
            if (((alias.length() == 0) && method.getName().equals(name) && paramsMatch)
                    || (alias.equals(name) && paramsMatch)) {
                return true;
            }
        }

        return false;
    }

    protected String addCallbackIfApplicable(HttpServletRequest request, String json) {
        if ((callbackParameter != null) && (callbackParameter.length() > 0)) {
            String callbackName = request.getParameter(callbackParameter);
            if ((callbackName != null) && (callbackName.length() > 0))
                json = callbackName + "(" + json + ")";
        }
        return json;
    }

    public boolean isEnableSMD() {
        return this.enableSMD;
    }

    public void setEnableSMD(boolean enableSMD) {
        this.enableSMD = enableSMD;
    }

    /**
     * Ignore annotations on methods in interfaces You may need to set to this
     * true if your action is a proxy/enhanced as annotations are not inherited
     */
    public void setIgnoreSMDMethodInterfaces(boolean ignoreSMDMethodInterfaces) {
        this.ignoreSMDMethodInterfaces = ignoreSMDMethodInterfaces;
    }

    /**
     * Wrap generated JSON with comments. Only used if SMD is enabled.
     * 
     * @param wrapWithComments
     */
    public void setWrapWithComments(boolean wrapWithComments) {
        this.wrapWithComments = wrapWithComments;
    }

    @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
    public void setDefaultEncoding(String val) {
        this.defaultEncoding = val;
    }

    /**
     * Ignore properties defined on base classes of the root object.
     * 
     * @param ignoreHierarchy
     */
    public void setIgnoreHierarchy(boolean ignoreHierarchy) {
        this.ignoreHierarchy = ignoreHierarchy;
    }

    /**
     * Sets the root object to be deserialized, defaults to the Action
     * 
     * @param root
     *            OGNL expression of root object to be serialized
     */
    public void setRoot(String root) {
        this.root = root;
    }

    /**
     * Sets the JSONPopulator to be used
     * 
     * @param populator
     *            JSONPopulator
     */
    public void setJSONPopulator(JSONPopulator populator) {
        this.populator = populator;
    }

    /**
     * Sets the JSONCleaner to be used
     * 
     * @param dataCleaner
     *            JSONCleaner
     */
    public void setJSONCleaner(JSONCleaner dataCleaner) {
        this.dataCleaner = dataCleaner;
    }

    /**
     * @return true if debugging is turned on
     */
    public boolean getDebug() {
        Boolean devModeOverride = FilterDispatcher.getDevModeOverride();
        return devModeOverride != null ? devModeOverride.booleanValue() : this.debug;
    }

    /**
     * Turns debugging on or off
     * 
     * @param debug
     *            true or false
     */
    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    @Inject(StrutsConstants.STRUTS_DEVMODE)
    public void setDevMode(
        String mode)
    {
        setDebug("true".equalsIgnoreCase(mode));
    }

    /**
     * Sets a comma-delimited list of regular expressions to match properties
     * that should be excluded from the JSON output.
     * 
     * @param commaDelim
     *            A comma-delimited list of regular expressions
     */
    public void setExcludeProperties(String commaDelim) {
        Set<String> excludePatterns = JSONUtil.asSet(commaDelim);
        if (excludePatterns != null) {
            this.excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
            for (String pattern : excludePatterns) {
                this.excludeProperties.add(Pattern.compile(pattern));
            }
        }
    }

    /**
     * Sets a comma-delimited list of wildcard expressions to match
     * properties that should be excluded from the JSON output.
     * 
     * @param commaDelim
     *            A comma-delimited list of wildcard expressions
     */
    public void setExcludeWildcards(String commaDelim) {
        Set<String> excludePatterns = JSONUtil.asSet(commaDelim);
        if (excludePatterns != null) {
            this.excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
            for (String pattern : excludePatterns) {
                this.excludeProperties.add(WildcardUtil.compileWildcardPattern(pattern));
            }
        }
    }

    /**
     * Sets a comma-delimited list of regular expressions to match properties
     * that should be included from the JSON output.
     * 
     * @param commaDelim
     *            A comma-delimited list of regular expressions
     */
    public void setIncludeProperties(String commaDelim) {
        includeProperties = JSONUtil.processIncludePatterns(JSONUtil.asSet(commaDelim), JSONUtil.REGEXP_PATTERN);
    }

    /**
     * Sets a comma-delimited list of wildcard expressions to match
     * properties that should be included from the JSON output.  The
     * standard boilerplate (id, error, debug) are automatically included,
     * as appropriate, so you only need to provide patterns for the
     * contents of "result".
     * 
     * @param commaDelim
     *            A comma-delimited list of wildcard expressions
     */
    public void setIncludeWildcards(String commaDelim) {
        includeProperties = JSONUtil.processIncludePatterns(JSONUtil.asSet(commaDelim), JSONUtil.WILDCARD_PATTERN);
        if (includeProperties != null) {
            includeProperties.add(Pattern.compile("id"));
            includeProperties.add(Pattern.compile("result"));
            includeProperties.add(Pattern.compile("error"));
            includeProperties.add(WildcardUtil.compileWildcardPattern("error.code"));
        }
    }

    /**
     * Returns the appropriate set of includes, based on debug setting.
     * Derived classes can override if there are additional, custom
     * debug-only parameters.
     */
    protected List getIncludeProperties() {
        if (includeProperties != null && getDebug()) {
            List<Pattern> list = new ArrayList<Pattern>(includeProperties);
            list.add(Pattern.compile("debug"));
            list.add(WildcardUtil.compileWildcardPattern("error.*"));
            return list;
        } else {
            return includeProperties;
        }
    }

    public boolean isEnableGZIP() {
        return enableGZIP;
    }

    /**
     * Setting this property to "true" will compress the output.
     * 
     * @param enableGZIP
     *            Enable compressed output
     */
    public void setEnableGZIP(boolean enableGZIP) {
        this.enableGZIP = enableGZIP;
    }

    public boolean isNoCache() {
        return noCache;
    }

    /**
     * Add headers to response to prevent the browser from caching the response
     * 
     * @param noCache
     */
    public void setNoCache(boolean noCache) {
        this.noCache = noCache;
    }

    public boolean isExcludeNullProperties() {
        return excludeNullProperties;
    }

    /**
     * Do not serialize properties with a null value
     * 
     * @param excludeNullProperties
     */
    public void setExcludeNullProperties(boolean excludeNullProperties) {
        this.excludeNullProperties = excludeNullProperties;
    }

    public void setCallbackParameter(String callbackParameter) {
        this.callbackParameter = callbackParameter;
    }

    public String getCallbackParameter() {
        return callbackParameter;
    }

    /**
     * Add "{} && " to generated JSON
     * 
     * @param prefix
     */
    public void setPrefix(boolean prefix) {
        this.prefix = prefix;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值