mirror of
https://github.com/apache/struts.git
synced 2026-08-07 15:46:57 +00:00
WW-3217 Bring JSON plugin into trunk
git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@802878 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>2.1.8-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-json-plugin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Struts 2 JSON Plugin</name>
|
||||
|
||||
<scm>
|
||||
<connection>scm:svn:http://svn.apache.org/repos/asf/struts/struts2/trunk/struts2-json-plugin</connection>
|
||||
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/struts/sandbox/struts2/struts2-json-plugin</developerConnection>
|
||||
<url>http://svn.apache.org/viewcvs.cgi/struts/sandbox/struts2/struts2-json-plugin</url>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<inherited>true</inherited>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Bundle-Activator>org.apache.struts2.osgi.StrutsActivator</Bundle-Activator>
|
||||
<manifestLocation>META-INF</manifestLocation>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-core</artifactId>
|
||||
<version>${pom.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-junit-plugin</artifactId>
|
||||
<version>${pom.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>jsp-api</artifactId>
|
||||
<version>2.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.easymock</groupId>
|
||||
<artifactId>easymock</artifactId>
|
||||
<version>2.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>${struts2.springPlatformVersion}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Isolate the process of cleaning JSON data from the Interceptor class itself.
|
||||
*/
|
||||
public abstract class JSONCleaner {
|
||||
|
||||
public Object clean(String ognlPrefix, Object data) throws JSONException {
|
||||
if (data == null)
|
||||
return null;
|
||||
else if (data instanceof List)
|
||||
return cleanList(ognlPrefix, data);
|
||||
else if (data instanceof Map)
|
||||
return cleanMap(ognlPrefix, data);
|
||||
else
|
||||
return cleanValue(ognlPrefix, data);
|
||||
}
|
||||
|
||||
protected Object cleanList(String ognlPrefix, Object data) throws JSONException {
|
||||
List list = (List) data;
|
||||
int count = list.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
list.set(i, clean(ognlPrefix + "[" + i + "]", list.get(i)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
protected Object cleanMap(String ognlPrefix, Object data) throws JSONException {
|
||||
Map map = (Map) data;
|
||||
Iterator iter = map.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry e = (Map.Entry) iter.next();
|
||||
e.setValue(clean((ognlPrefix.length() > 0 ? ognlPrefix + "." : "") + e.getKey(), e.getValue()));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
protected abstract Object cleanValue(String ognlName, Object data) throws JSONException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
/**
|
||||
* Wrap exceptions throwed by the JSON serializer
|
||||
*/
|
||||
public class JSONException extends Exception {
|
||||
public JSONException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public JSONException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.json.annotations.SMDMethod;
|
||||
import org.apache.struts2.json.rpc.RPCError;
|
||||
import org.apache.struts2.json.rpc.RPCErrorCode;
|
||||
import org.apache.struts2.json.rpc.RPCResponse;
|
||||
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.Interceptor;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Populates an action from a JSON string
|
||||
*/
|
||||
public class JSONInterceptor implements Interceptor {
|
||||
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;
|
||||
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@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;
|
||||
if (this.root != null) {
|
||||
ValueStack stack = invocation.getStack();
|
||||
rootObject = stack.findValue(this.root);
|
||||
|
||||
if (rootObject == null) {
|
||||
throw new RuntimeException("Invalid root expression: '" + this.root + "'.");
|
||||
}
|
||||
} else {
|
||||
rootObject = invocation.getAction();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
|
||||
// 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, debug));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
String json = JSONUtil.serialize(result, excludeProperties, includeProperties,
|
||||
ignoreHierarchy, excludeNullProperties);
|
||||
json = addCallbackIfApplicable(request, json);
|
||||
JSONUtil.writeJSONToResponse(new SerializationParams(response, this.defaultEncoding,
|
||||
this.wrapWithComments, json, true, false, noCache, -1, -1, prefix, contentType));
|
||||
|
||||
return Action.NONE;
|
||||
} 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, includeProperties, 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, contentType));
|
||||
|
||||
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' (what is the id
|
||||
// for?)
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns debugging on or off
|
||||
*
|
||||
* @param debug
|
||||
* true or false
|
||||
*/
|
||||
public boolean getDebug() {
|
||||
return this.debug;
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
List<String> excludePatterns = JSONUtil.asList(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 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) {
|
||||
List<String> includePatterns = JSONUtil.asList(commaDelim);
|
||||
if (includePatterns != null) {
|
||||
this.includeProperties = new ArrayList<Pattern>(includePatterns.size());
|
||||
for (String pattern : includePatterns) {
|
||||
this.includeProperties.add(Pattern.compile(pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.struts2.json.annotations.JSON;
|
||||
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Isolate the process of populating JSON objects from the Interceptor class
|
||||
* itself.
|
||||
*/
|
||||
public class JSONPopulator {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JSONPopulator.class);
|
||||
|
||||
private String dateFormat = JSONUtil.RFC3339_FORMAT;
|
||||
|
||||
public JSONPopulator() {
|
||||
}
|
||||
|
||||
public JSONPopulator(String dateFormat) {
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
public String getDateFormat() {
|
||||
return dateFormat;
|
||||
}
|
||||
|
||||
public void setDateFormat(String dateFormat) {
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void populateObject(Object object, final Map elements) throws IllegalAccessException,
|
||||
InvocationTargetException, NoSuchMethodException, IntrospectionException,
|
||||
IllegalArgumentException, JSONException, InstantiationException {
|
||||
Class clazz = object.getClass();
|
||||
|
||||
BeanInfo info = Introspector.getBeanInfo(clazz);
|
||||
PropertyDescriptor[] props = info.getPropertyDescriptors();
|
||||
|
||||
// iterate over class fields
|
||||
for (int i = 0; i < props.length; ++i) {
|
||||
PropertyDescriptor prop = props[i];
|
||||
String name = prop.getName();
|
||||
|
||||
if (elements.containsKey(name)) {
|
||||
Object value = elements.get(name);
|
||||
Method method = prop.getWriteMethod();
|
||||
|
||||
if (method != null) {
|
||||
JSON json = method.getAnnotation(JSON.class);
|
||||
if ((json != null) && !json.deserialize()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// use only public setters
|
||||
if (Modifier.isPublic(method.getModifiers())) {
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
Type[] genericTypes = method.getGenericParameterTypes();
|
||||
|
||||
if (paramTypes.length == 1) {
|
||||
Object convertedValue = this.convert(paramTypes[0], genericTypes[0], value,
|
||||
method);
|
||||
method.invoke(object, new Object[] { convertedValue });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object convert(Class clazz, Type type, Object value, Method method)
|
||||
throws IllegalArgumentException, JSONException, IllegalAccessException,
|
||||
InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException {
|
||||
|
||||
if (value == null) {
|
||||
// if it is a java primitive then get a default value, otherwise
|
||||
// leave it as null
|
||||
return clazz.isPrimitive() ? convertPrimitive(clazz, value, method) : null;
|
||||
} else if (isJSONPrimitive(clazz))
|
||||
return convertPrimitive(clazz, value, method);
|
||||
else if (Collection.class.isAssignableFrom(clazz))
|
||||
return convertToCollection(clazz, type, value, method);
|
||||
else if (Map.class.isAssignableFrom(clazz))
|
||||
return convertToMap(clazz, type, value, method);
|
||||
else if (clazz.isArray())
|
||||
return convertToArray(clazz, type, value, method);
|
||||
else if (value instanceof Map) {
|
||||
// nested bean
|
||||
Object convertedValue = clazz.newInstance();
|
||||
this.populateObject(convertedValue, (Map) value);
|
||||
return convertedValue;
|
||||
} else if (BigDecimal.class.equals(clazz)) {
|
||||
return new BigDecimal(value != null ? value.toString() : "0");
|
||||
} else if (BigInteger.class.equals(clazz)) {
|
||||
return new BigInteger(value != null ? value.toString() : "0");
|
||||
} else
|
||||
throw new JSONException("Incompatible types for property " + method.getName());
|
||||
}
|
||||
|
||||
private static boolean isJSONPrimitive(Class clazz) {
|
||||
return clazz.isPrimitive() || clazz.equals(String.class) || clazz.equals(Date.class)
|
||||
|| clazz.equals(Boolean.class) || clazz.equals(Byte.class) || clazz.equals(Character.class)
|
||||
|| clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Integer.class)
|
||||
|| clazz.equals(Long.class) || clazz.equals(Short.class) || clazz.equals(Locale.class)
|
||||
|| clazz.isEnum();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object convertToArray(Class clazz, Type type, Object value, Method accessor)
|
||||
throws JSONException, IllegalArgumentException, IllegalAccessException,
|
||||
InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException {
|
||||
if (value == null)
|
||||
return null;
|
||||
else if (value instanceof List) {
|
||||
Class arrayType = clazz.getComponentType();
|
||||
List values = (List) value;
|
||||
Object newArray = Array.newInstance(arrayType, values.size());
|
||||
|
||||
// create an object for each element
|
||||
for (int j = 0; j < values.size(); j++) {
|
||||
Object listValue = values.get(j);
|
||||
|
||||
if (arrayType.equals(Object.class)) {
|
||||
// Object[]
|
||||
Array.set(newArray, j, listValue);
|
||||
} else if (isJSONPrimitive(arrayType)) {
|
||||
// primitive array
|
||||
Array.set(newArray, j, this.convertPrimitive(arrayType, listValue, accessor));
|
||||
} else if (listValue instanceof Map) {
|
||||
// array of other class
|
||||
Object newObject = null;
|
||||
if (Map.class.isAssignableFrom(arrayType)) {
|
||||
newObject = convertToMap(arrayType, type, listValue, accessor);
|
||||
} else if (List.class.isAssignableFrom(arrayType)) {
|
||||
newObject = convertToCollection(arrayType, type, listValue, accessor);
|
||||
} else {
|
||||
newObject = arrayType.newInstance();
|
||||
this.populateObject(newObject, (Map) listValue);
|
||||
}
|
||||
|
||||
Array.set(newArray, j, newObject);
|
||||
} else
|
||||
throw new JSONException("Incompatible types for property " + accessor.getName());
|
||||
}
|
||||
|
||||
return newArray;
|
||||
} else
|
||||
throw new JSONException("Incompatible types for property " + accessor.getName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object convertToCollection(Class clazz, Type type, Object value, Method accessor)
|
||||
throws JSONException, IllegalArgumentException, IllegalAccessException,
|
||||
InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException {
|
||||
if (value == null)
|
||||
return null;
|
||||
else if (value instanceof List) {
|
||||
Class itemClass = Object.class;
|
||||
Type itemType = null;
|
||||
if ((type != null) && (type instanceof ParameterizedType)) {
|
||||
ParameterizedType ptype = (ParameterizedType) type;
|
||||
itemType = ptype.getActualTypeArguments()[0];
|
||||
if (itemType.getClass().equals(Class.class)) {
|
||||
itemClass = (Class) itemType;
|
||||
} else {
|
||||
itemClass = (Class) ((ParameterizedType) itemType).getRawType();
|
||||
}
|
||||
}
|
||||
List values = (List) value;
|
||||
|
||||
Collection newCollection = null;
|
||||
try {
|
||||
newCollection = (Collection) clazz.newInstance();
|
||||
} catch (InstantiationException ex) {
|
||||
// fallback if clazz represents an interface or abstract class
|
||||
if (Set.class.isAssignableFrom(clazz)) {
|
||||
newCollection = new HashSet();
|
||||
} else {
|
||||
newCollection = new ArrayList();
|
||||
}
|
||||
}
|
||||
|
||||
// create an object for each element
|
||||
for (int j = 0; j < values.size(); j++) {
|
||||
Object listValue = values.get(j);
|
||||
|
||||
if (itemClass.equals(Object.class)) {
|
||||
// Object[]
|
||||
newCollection.add(listValue);
|
||||
} else if (isJSONPrimitive(itemClass)) {
|
||||
// primitive array
|
||||
newCollection.add(this.convertPrimitive(itemClass, listValue, accessor));
|
||||
} else if (Map.class.isAssignableFrom(itemClass)) {
|
||||
Object newObject = convertToMap(itemClass, itemType, listValue, accessor);
|
||||
newCollection.add(newObject);
|
||||
} else if (List.class.isAssignableFrom(itemClass)) {
|
||||
Object newObject = convertToCollection(itemClass, itemType, listValue, accessor);
|
||||
newCollection.add(newObject);
|
||||
} else if (listValue instanceof Map) {
|
||||
// array of beans
|
||||
Object newObject = itemClass.newInstance();
|
||||
this.populateObject(newObject, (Map) listValue);
|
||||
newCollection.add(newObject);
|
||||
} else
|
||||
throw new JSONException("Incompatible types for property " + accessor.getName());
|
||||
}
|
||||
|
||||
return newCollection;
|
||||
} else
|
||||
throw new JSONException("Incompatible types for property " + accessor.getName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object convertToMap(Class clazz, Type type, Object value, Method accessor) throws JSONException,
|
||||
IllegalArgumentException, IllegalAccessException, InvocationTargetException,
|
||||
InstantiationException, NoSuchMethodException, IntrospectionException {
|
||||
if (value == null)
|
||||
return null;
|
||||
else if (value instanceof Map) {
|
||||
Class itemClass = Object.class;
|
||||
Type itemType = null;
|
||||
if ((type != null) && (type instanceof ParameterizedType)) {
|
||||
ParameterizedType ptype = (ParameterizedType) type;
|
||||
itemType = ptype.getActualTypeArguments()[1];
|
||||
if (itemType.getClass().equals(Class.class)) {
|
||||
itemClass = (Class) itemType;
|
||||
} else {
|
||||
itemClass = (Class) ((ParameterizedType) itemType).getRawType();
|
||||
}
|
||||
}
|
||||
Map values = (Map) value;
|
||||
|
||||
Map newMap = null;
|
||||
try {
|
||||
newMap = (Map) clazz.newInstance();
|
||||
} catch (InstantiationException ex) {
|
||||
// fallback if clazz represents an interface or abstract class
|
||||
newMap = new HashMap();
|
||||
}
|
||||
|
||||
// create an object for each element
|
||||
Iterator iter = values.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) iter.next();
|
||||
String key = (String) entry.getKey();
|
||||
Object v = entry.getValue();
|
||||
|
||||
if (itemClass.equals(Object.class)) {
|
||||
// String, Object
|
||||
newMap.put(key, v);
|
||||
} else if (isJSONPrimitive(itemClass)) {
|
||||
// primitive map
|
||||
newMap.put(key, this.convertPrimitive(itemClass, v, accessor));
|
||||
} else if (Map.class.isAssignableFrom(itemClass)) {
|
||||
Object newObject = convertToMap(itemClass, itemType, v, accessor);
|
||||
newMap.put(key, newObject);
|
||||
} else if (List.class.isAssignableFrom(itemClass)) {
|
||||
Object newObject = convertToCollection(itemClass, itemType, v, accessor);
|
||||
newMap.put(key, newObject);
|
||||
} else if (v instanceof Map) {
|
||||
// map of beans
|
||||
Object newObject = itemClass.newInstance();
|
||||
this.populateObject(newObject, (Map) v);
|
||||
newMap.put(key, newObject);
|
||||
} else
|
||||
throw new JSONException("Incompatible types for property " + accessor.getName());
|
||||
}
|
||||
|
||||
return newMap;
|
||||
} else
|
||||
throw new JSONException("Incompatible types for property " + accessor.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts numbers to the desired class, if possible
|
||||
*
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object convertPrimitive(Class clazz, Object value, Method method) throws JSONException {
|
||||
if (value == null) {
|
||||
if (Short.TYPE.equals(clazz) || Short.class.equals(clazz))
|
||||
return (short) 0;
|
||||
else if (Byte.TYPE.equals(clazz) || Byte.class.equals(clazz))
|
||||
return (byte) 0;
|
||||
else if (Integer.TYPE.equals(clazz) || Integer.class.equals(clazz))
|
||||
return 0;
|
||||
else if (Long.TYPE.equals(clazz) || Long.class.equals(clazz))
|
||||
return 0L;
|
||||
else if (Float.TYPE.equals(clazz) || Float.class.equals(clazz))
|
||||
return 0f;
|
||||
else if (Double.TYPE.equals(clazz) || Double.class.equals(clazz))
|
||||
return 0d;
|
||||
else if (Boolean.TYPE.equals(clazz) || Boolean.class.equals(clazz))
|
||||
return Boolean.FALSE;
|
||||
else
|
||||
return null;
|
||||
} else if (value instanceof Number) {
|
||||
Number number = (Number) value;
|
||||
|
||||
if (Short.TYPE.equals(clazz))
|
||||
return number.shortValue();
|
||||
else if (Short.class.equals(clazz))
|
||||
return new Short(number.shortValue());
|
||||
else if (Byte.TYPE.equals(clazz))
|
||||
return number.byteValue();
|
||||
else if (Byte.class.equals(clazz))
|
||||
return new Byte(number.byteValue());
|
||||
else if (Integer.TYPE.equals(clazz))
|
||||
return number.intValue();
|
||||
else if (Integer.class.equals(clazz))
|
||||
return new Integer(number.intValue());
|
||||
else if (Long.TYPE.equals(clazz))
|
||||
return number.longValue();
|
||||
else if (Long.class.equals(clazz))
|
||||
return new Long(number.longValue());
|
||||
else if (Float.TYPE.equals(clazz))
|
||||
return number.floatValue();
|
||||
else if (Float.class.equals(clazz))
|
||||
return new Float(number.floatValue());
|
||||
else if (Double.TYPE.equals(clazz))
|
||||
return number.doubleValue();
|
||||
else if (Double.class.equals(clazz))
|
||||
return new Double(number.doubleValue());
|
||||
else if (String.class.equals(clazz))
|
||||
return value.toString();
|
||||
} else if (clazz.equals(Date.class)) {
|
||||
try {
|
||||
JSON json = method.getAnnotation(JSON.class);
|
||||
|
||||
DateFormat formatter = new SimpleDateFormat(
|
||||
(json != null) && (json.format().length() > 0) ? json.format() : this.dateFormat);
|
||||
return formatter.parse((String) value);
|
||||
} catch (ParseException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw new JSONException("Unable to parse date from: " + value);
|
||||
}
|
||||
} else if (clazz.isEnum()) {
|
||||
String sValue = (String) value;
|
||||
return Enum.valueOf(clazz, sValue);
|
||||
} else if (value instanceof String) {
|
||||
String sValue = (String) value;
|
||||
if (Boolean.TYPE.equals(clazz))
|
||||
return Boolean.parseBoolean(sValue);
|
||||
else if (Boolean.class.equals(clazz))
|
||||
return Boolean.valueOf(sValue);
|
||||
else if (Short.TYPE.equals(clazz))
|
||||
return Short.parseShort(sValue);
|
||||
else if (Short.class.equals(clazz))
|
||||
return Short.valueOf(sValue);
|
||||
else if (Byte.TYPE.equals(clazz))
|
||||
return Byte.parseByte(sValue);
|
||||
else if (Byte.class.equals(clazz))
|
||||
return Byte.valueOf(sValue);
|
||||
else if (Integer.TYPE.equals(clazz))
|
||||
return Integer.parseInt(sValue);
|
||||
else if (Integer.class.equals(clazz))
|
||||
return Integer.valueOf(sValue);
|
||||
else if (Long.TYPE.equals(clazz))
|
||||
return Long.parseLong(sValue);
|
||||
else if (Long.class.equals(clazz))
|
||||
return Long.valueOf(sValue);
|
||||
else if (Float.TYPE.equals(clazz))
|
||||
return Float.parseFloat(sValue);
|
||||
else if (Float.class.equals(clazz))
|
||||
return Float.valueOf(sValue);
|
||||
else if (Double.TYPE.equals(clazz))
|
||||
return Double.parseDouble(sValue);
|
||||
else if (Double.class.equals(clazz))
|
||||
return Double.valueOf(sValue);
|
||||
else if (Character.TYPE.equals(clazz) || Character.class.equals(clazz)) {
|
||||
char charValue = 0;
|
||||
if (sValue.length() > 0) {
|
||||
charValue = sValue.charAt(0);
|
||||
}
|
||||
if (Character.TYPE.equals(clazz))
|
||||
return charValue;
|
||||
else
|
||||
return new Character(charValue);
|
||||
} else if (clazz.equals(Locale.class)) {
|
||||
String[] components = sValue.split("_", 2);
|
||||
if (components.length == 2) {
|
||||
return new Locale(components[0], components[1]);
|
||||
} else {
|
||||
return new Locale(sValue);
|
||||
}
|
||||
} else if (Enum.class.isAssignableFrom(clazz)) {
|
||||
return Enum.valueOf(clazz, sValue);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.StringCharacterIterator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Deserializes and object from a JSON string
|
||||
* </p>
|
||||
*/
|
||||
class JSONReader {
|
||||
private static final Object OBJECT_END = new Object();
|
||||
private static final Object ARRAY_END = new Object();
|
||||
private static final Object COLON = new Object();
|
||||
private static final Object COMMA = new Object();
|
||||
private static Map<Character, Character> escapes = new HashMap<Character, Character>();
|
||||
|
||||
static {
|
||||
escapes.put(new Character('"'), new Character('"'));
|
||||
escapes.put(new Character('\\'), new Character('\\'));
|
||||
escapes.put(new Character('/'), new Character('/'));
|
||||
escapes.put(new Character('b'), new Character('\b'));
|
||||
escapes.put(new Character('f'), new Character('\f'));
|
||||
escapes.put(new Character('n'), new Character('\n'));
|
||||
escapes.put(new Character('r'), new Character('\r'));
|
||||
escapes.put(new Character('t'), new Character('\t'));
|
||||
}
|
||||
|
||||
private CharacterIterator it;
|
||||
private char c;
|
||||
private Object token;
|
||||
private StringBuilder buf = new StringBuilder();
|
||||
|
||||
private char next() {
|
||||
this.c = this.it.next();
|
||||
|
||||
return this.c;
|
||||
}
|
||||
|
||||
private void skipWhiteSpace() {
|
||||
while (Character.isWhitespace(this.c)) {
|
||||
this.next();
|
||||
}
|
||||
}
|
||||
|
||||
public Object read(String string) throws JSONException {
|
||||
this.it = new StringCharacterIterator(string);
|
||||
this.c = this.it.first();
|
||||
|
||||
return this.read();
|
||||
}
|
||||
|
||||
private Object read() throws JSONException {
|
||||
Object ret = null;
|
||||
|
||||
this.skipWhiteSpace();
|
||||
|
||||
if (this.c == '"') {
|
||||
this.next();
|
||||
ret = this.string('"');
|
||||
} else if (this.c == '\'') {
|
||||
this.next();
|
||||
ret = this.string('\'');
|
||||
} else if (this.c == '[') {
|
||||
this.next();
|
||||
ret = this.array();
|
||||
} else if (this.c == ']') {
|
||||
ret = ARRAY_END;
|
||||
this.next();
|
||||
} else if (this.c == ',') {
|
||||
ret = COMMA;
|
||||
this.next();
|
||||
} else if (this.c == '{') {
|
||||
this.next();
|
||||
ret = this.object();
|
||||
} else if (this.c == '}') {
|
||||
ret = OBJECT_END;
|
||||
this.next();
|
||||
} else if (this.c == ':') {
|
||||
ret = COLON;
|
||||
this.next();
|
||||
} else if ((this.c == 't') && (this.next() == 'r') && (this.next() == 'u') && (this.next() == 'e')) {
|
||||
ret = Boolean.TRUE;
|
||||
this.next();
|
||||
} else if ((this.c == 'f') && (this.next() == 'a') && (this.next() == 'l') && (this.next() == 's')
|
||||
&& (this.next() == 'e')) {
|
||||
ret = Boolean.FALSE;
|
||||
this.next();
|
||||
} else if ((this.c == 'n') && (this.next() == 'u') && (this.next() == 'l') && (this.next() == 'l')) {
|
||||
ret = null;
|
||||
this.next();
|
||||
} else if (Character.isDigit(this.c) || (this.c == '-')) {
|
||||
ret = this.number();
|
||||
} else {
|
||||
throw buildInvalidInputException();
|
||||
}
|
||||
|
||||
this.token = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map object() throws JSONException {
|
||||
Map ret = new HashMap();
|
||||
Object next = this.read();
|
||||
if (next != OBJECT_END) {
|
||||
String key = (String) next;
|
||||
while (this.token != OBJECT_END) {
|
||||
this.read(); // should be a colon
|
||||
|
||||
if (this.token != OBJECT_END) {
|
||||
ret.put(key, this.read());
|
||||
|
||||
if (this.read() == COMMA) {
|
||||
Object name = this.read();
|
||||
|
||||
if (name instanceof String) {
|
||||
key = (String) name;
|
||||
} else
|
||||
throw buildInvalidInputException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private JSONException buildInvalidInputException() {
|
||||
return new JSONException("Input string is not well formed JSON (invalid char " + this.c + ")");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List array() throws JSONException {
|
||||
List ret = new ArrayList();
|
||||
Object value = this.read();
|
||||
|
||||
while (this.token != ARRAY_END) {
|
||||
ret.add(value);
|
||||
|
||||
Object read = this.read();
|
||||
if (read == COMMA) {
|
||||
value = this.read();
|
||||
} else if (read != ARRAY_END) {
|
||||
throw buildInvalidInputException();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Object number() {
|
||||
this.buf.setLength(0);
|
||||
|
||||
if (this.c == '-') {
|
||||
this.add();
|
||||
}
|
||||
|
||||
this.addDigits();
|
||||
|
||||
if (this.c == '.') {
|
||||
this.add();
|
||||
this.addDigits();
|
||||
}
|
||||
|
||||
if ((this.c == 'e') || (this.c == 'E')) {
|
||||
this.add();
|
||||
|
||||
if ((this.c == '+') || (this.c == '-')) {
|
||||
this.add();
|
||||
}
|
||||
|
||||
this.addDigits();
|
||||
}
|
||||
|
||||
return (this.buf.indexOf(".") >= 0) ? (Object) Double.parseDouble(this.buf.toString())
|
||||
: (Object) Long.parseLong(this.buf.toString());
|
||||
}
|
||||
|
||||
private Object string(char quote) {
|
||||
this.buf.setLength(0);
|
||||
|
||||
while ((this.c != quote) && (this.c != CharacterIterator.DONE)) {
|
||||
if (this.c == '\\') {
|
||||
this.next();
|
||||
|
||||
if (this.c == 'u') {
|
||||
this.add(this.unicode());
|
||||
} else {
|
||||
Object value = escapes.get(new Character(this.c));
|
||||
|
||||
if (value != null) {
|
||||
this.add(((Character) value).charValue());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.add();
|
||||
}
|
||||
}
|
||||
|
||||
this.next();
|
||||
|
||||
return this.buf.toString();
|
||||
}
|
||||
|
||||
private void add(char cc) {
|
||||
this.buf.append(cc);
|
||||
this.next();
|
||||
}
|
||||
|
||||
private void add() {
|
||||
this.add(this.c);
|
||||
}
|
||||
|
||||
private void addDigits() {
|
||||
while (Character.isDigit(this.c)) {
|
||||
this.add();
|
||||
}
|
||||
}
|
||||
|
||||
private char unicode() {
|
||||
int value = 0;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
switch (this.next()) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
value = (value << 4) + (this.c - '0');
|
||||
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
value = (value << 4) + (this.c - 'W');
|
||||
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
value = (value << 4) + (this.c - '7');
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (char) value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.StrutsStatics;
|
||||
import org.apache.struts2.json.annotations.SMD;
|
||||
import org.apache.struts2.json.annotations.SMDMethod;
|
||||
import org.apache.struts2.json.annotations.SMDMethodParameter;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.Result;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: description --> <p/> This result serializes an action
|
||||
* into JSON. <p/> <!-- END SNIPPET: description --> <p/> <p/> <u>Result
|
||||
* parameters:</u> <p/> <!-- START SNIPPET: parameters --> <p/>
|
||||
* <ul>
|
||||
* <p/>
|
||||
* <li>excludeProperties - list of regular expressions matching the properties
|
||||
* to be excluded. The regular expressions are evaluated against the OGNL
|
||||
* expression representation of the properties. </li>
|
||||
* <p/>
|
||||
* </ul>
|
||||
* <p/> <!-- END SNIPPET: parameters --> <p/> <b>Example:</b> <p/>
|
||||
*
|
||||
* <pre>
|
||||
* <!-- START SNIPPET: example -->
|
||||
* <result name="success" type="json" />
|
||||
* <!-- END SNIPPET: example -->
|
||||
* </pre>
|
||||
*/
|
||||
public class JSONResult implements Result {
|
||||
private static final long serialVersionUID = 8624350183189931165L;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JSONResult.class);
|
||||
|
||||
private String defaultEncoding = "ISO-8859-1";
|
||||
private List<Pattern> includeProperties;
|
||||
private List<Pattern> excludeProperties;
|
||||
private String root;
|
||||
private boolean wrapWithComments;
|
||||
private boolean prefix;
|
||||
private boolean enableSMD = false;
|
||||
private boolean enableGZIP = false;
|
||||
private boolean ignoreHierarchy = true;
|
||||
private boolean ignoreInterfaces = true;
|
||||
private boolean enumAsBean = JSONWriter.ENUM_AS_BEAN_DEFAULT;
|
||||
private boolean noCache = false;
|
||||
private boolean excludeNullProperties = false;
|
||||
private int statusCode;
|
||||
private int errorCode;
|
||||
private String callbackParameter;
|
||||
private String contentType;
|
||||
private String wrapPrefix;
|
||||
private String wrapSuffix;
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
|
||||
public void setDefaultEncoding(String val) {
|
||||
this.defaultEncoding = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of regular expressions of properties to exclude from the JSON
|
||||
* output.
|
||||
*
|
||||
* @return A list of compiled regular expression patterns
|
||||
*/
|
||||
public List<Pattern> getExcludePropertiesList() {
|
||||
return this.excludeProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
List<String> excludePatterns = JSONUtil.asList(commaDelim);
|
||||
if (excludePatterns != null) {
|
||||
this.excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
|
||||
for (String pattern : excludePatterns) {
|
||||
this.excludeProperties.add(Pattern.compile(pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the includeProperties
|
||||
*/
|
||||
public List<Pattern> getIncludePropertiesList() {
|
||||
return includeProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param includedProperties
|
||||
* the includeProperties to set
|
||||
*/
|
||||
public void setIncludeProperties(String commaDelim) {
|
||||
List<String> includePatterns = JSONUtil.asList(commaDelim);
|
||||
if (includePatterns != null) {
|
||||
this.includeProperties = new ArrayList<Pattern>(includePatterns.size());
|
||||
|
||||
HashMap existingPatterns = new HashMap();
|
||||
|
||||
for (String pattern : includePatterns) {
|
||||
// Compile a pattern for each *unique* "level" of the object
|
||||
// hierarchy specified in the regex.
|
||||
String[] patternPieces = pattern.split("\\\\\\.");
|
||||
|
||||
String patternExpr = "";
|
||||
for (String patternPiece : patternPieces) {
|
||||
if (patternExpr.length() > 0) {
|
||||
patternExpr += "\\.";
|
||||
}
|
||||
patternExpr += patternPiece;
|
||||
|
||||
// Check for duplicate patterns so that there is no overlap.
|
||||
if (!existingPatterns.containsKey(patternExpr)) {
|
||||
existingPatterns.put(patternExpr, patternExpr);
|
||||
|
||||
// Add a pattern that does not have the indexed property
|
||||
// matching (ie. list\[\d+\] becomes list).
|
||||
if (patternPiece.endsWith("\\]")) {
|
||||
this.includeProperties.add(Pattern.compile(patternExpr.substring(0, patternPiece
|
||||
.lastIndexOf("\\["))));
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Adding include property expression: "
|
||||
+ patternExpr.substring(0, patternPiece.lastIndexOf("\\[")));
|
||||
}
|
||||
|
||||
this.includeProperties.add(Pattern.compile(patternExpr));
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Adding include property expression: " + patternExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void execute(ActionInvocation invocation) throws Exception {
|
||||
ActionContext actionContext = invocation.getInvocationContext();
|
||||
HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
|
||||
HttpServletResponse response = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);
|
||||
|
||||
try {
|
||||
String json;
|
||||
Object rootObject;
|
||||
if (this.enableSMD) {
|
||||
// generate SMD
|
||||
rootObject = this.writeSMD(invocation);
|
||||
} else {
|
||||
// generate JSON
|
||||
if (this.root != null) {
|
||||
ValueStack stack = invocation.getStack();
|
||||
rootObject = stack.findValue(this.root);
|
||||
} else {
|
||||
rootObject = invocation.getAction();
|
||||
}
|
||||
}
|
||||
json = JSONUtil.serialize(rootObject, excludeProperties, includeProperties, ignoreHierarchy,
|
||||
enumAsBean, excludeNullProperties);
|
||||
json = addCallbackIfApplicable(request, json);
|
||||
|
||||
boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);
|
||||
|
||||
writeToResponse(response, json, writeGzip);
|
||||
|
||||
} catch (IOException exception) {
|
||||
LOG.error(exception.getMessage(), exception);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeToResponse(HttpServletResponse response, String json, boolean gzip)
|
||||
throws IOException {
|
||||
JSONUtil.writeJSONToResponse(new SerializationParams(response, getEncoding(), isWrapWithComments(),
|
||||
json, false, gzip, noCache, statusCode, errorCode, prefix, contentType, wrapPrefix,
|
||||
wrapSuffix));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected org.apache.struts2.json.smd.SMD writeSMD(ActionInvocation invocation) {
|
||||
ActionContext actionContext = invocation.getInvocationContext();
|
||||
HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
|
||||
|
||||
// root is based on OGNL expression (action by default)
|
||||
Object rootObject = null;
|
||||
if (this.root != null) {
|
||||
ValueStack stack = invocation.getStack();
|
||||
rootObject = stack.findValue(this.root);
|
||||
} else {
|
||||
rootObject = invocation.getAction();
|
||||
}
|
||||
|
||||
Class clazz = rootObject.getClass();
|
||||
org.apache.struts2.json.smd.SMD smd = new org.apache.struts2.json.smd.SMD();
|
||||
// URL
|
||||
smd.setServiceUrl(request.getRequestURI());
|
||||
|
||||
// customize SMD
|
||||
SMD smdAnnotation = (SMD) clazz.getAnnotation(SMD.class);
|
||||
if (smdAnnotation != null) {
|
||||
smd.setObjectName(smdAnnotation.objectName());
|
||||
smd.setServiceType(smdAnnotation.serviceType());
|
||||
smd.setVersion(smdAnnotation.version());
|
||||
}
|
||||
|
||||
// get public methods
|
||||
Method[] methods = JSONUtil.listSMDMethods(clazz, ignoreInterfaces);
|
||||
|
||||
for (Method method : methods) {
|
||||
SMDMethod smdMethodAnnotation = method.getAnnotation(SMDMethod.class);
|
||||
|
||||
// SMDMethod annotation is required
|
||||
if (((smdMethodAnnotation != null) && !this.shouldExcludeProperty(method.getName()))) {
|
||||
String methodName = smdMethodAnnotation.name().length() == 0 ? method.getName()
|
||||
: smdMethodAnnotation.name();
|
||||
|
||||
org.apache.struts2.json.smd.SMDMethod smdMethod = new org.apache.struts2.json.smd.SMDMethod(
|
||||
methodName);
|
||||
smd.addSMDMethod(smdMethod);
|
||||
|
||||
// find params for this method
|
||||
int parametersCount = method.getParameterTypes().length;
|
||||
if (parametersCount > 0) {
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
|
||||
for (int i = 0; i < parametersCount; i++) {
|
||||
// are you ever going to pick shorter names? nope
|
||||
SMDMethodParameter smdMethodParameterAnnotation = this
|
||||
.getSMDMethodParameterAnnotation(parameterAnnotations[i]);
|
||||
|
||||
String paramName = smdMethodParameterAnnotation != null ? smdMethodParameterAnnotation
|
||||
.name()
|
||||
: "p" + i;
|
||||
|
||||
// goog thing this is the end of the hierarchy,
|
||||
// oitherwise I would need that 21'' LCD ;)
|
||||
smdMethod.addSMDMethodParameter(new org.apache.struts2.json.smd.SMDMethodParameter(
|
||||
paramName));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Ignoring property " + method.getName());
|
||||
}
|
||||
}
|
||||
return smd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an SMDethodParameter annotation on this array
|
||||
*/
|
||||
private org.apache.struts2.json.annotations.SMDMethodParameter getSMDMethodParameterAnnotation(
|
||||
Annotation[] annotations) {
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation instanceof org.apache.struts2.json.annotations.SMDMethodParameter)
|
||||
return (org.apache.struts2.json.annotations.SMDMethodParameter) annotation;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean shouldExcludeProperty(String expr) {
|
||||
if (this.excludeProperties != null) {
|
||||
for (Pattern pattern : this.excludeProperties) {
|
||||
if (pattern.matcher(expr).matches())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the encoding <p/>
|
||||
*
|
||||
* @return The encoding associated with this template (defaults to the value
|
||||
* of 'struts.i18n.encoding' property)
|
||||
*/
|
||||
protected String getEncoding() {
|
||||
String encoding = this.defaultEncoding;
|
||||
|
||||
if (encoding == null) {
|
||||
encoding = System.getProperty("file.encoding");
|
||||
}
|
||||
|
||||
if (encoding == null) {
|
||||
encoding = "UTF-8";
|
||||
}
|
||||
|
||||
return encoding;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OGNL expression of root object to be serialized
|
||||
*/
|
||||
public String getRoot() {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the root object to be serialized, defaults to the Action
|
||||
*
|
||||
* @param root
|
||||
* OGNL expression of root object to be serialized
|
||||
*/
|
||||
public void setRoot(String root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generated JSON must be enclosed in comments
|
||||
*/
|
||||
public boolean isWrapWithComments() {
|
||||
return this.wrapWithComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap generated JSON with comments
|
||||
*
|
||||
* @param wrapWithComments
|
||||
*/
|
||||
public void setWrapWithComments(boolean wrapWithComments) {
|
||||
this.wrapWithComments = wrapWithComments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Result has SMD generation enabled
|
||||
*/
|
||||
public boolean isEnableSMD() {
|
||||
return this.enableSMD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable SMD generation for action, which can be used for JSON-RPC
|
||||
*
|
||||
* @param enableSMD
|
||||
*/
|
||||
public void setEnableSMD(boolean enableSMD) {
|
||||
this.enableSMD = enableSMD;
|
||||
}
|
||||
|
||||
public void setIgnoreHierarchy(boolean ignoreHierarchy) {
|
||||
this.ignoreHierarchy = ignoreHierarchy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls whether interfaces should be inspected for method annotations
|
||||
* You may need to set to this true if your action is a proxy as annotations
|
||||
* on methods are not inherited
|
||||
*/
|
||||
public void setIgnoreInterfaces(boolean ignoreInterfaces) {
|
||||
this.ignoreInterfaces = ignoreInterfaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls how Enum's are serialized : If true, an Enum is serialized as a
|
||||
* name=value pair (name=name()) (default) If false, an Enum is serialized
|
||||
* as a bean with a special property _name=name()
|
||||
*
|
||||
* @param enumAsBean
|
||||
*/
|
||||
public void setEnumAsBean(boolean enumAsBean) {
|
||||
this.enumAsBean = enumAsBean;
|
||||
}
|
||||
|
||||
public boolean isEnumAsBean() {
|
||||
return enumAsBean;
|
||||
}
|
||||
|
||||
public boolean isEnableGZIP() {
|
||||
return enableGZIP;
|
||||
}
|
||||
|
||||
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 isIgnoreHierarchy() {
|
||||
return ignoreHierarchy;
|
||||
}
|
||||
|
||||
public boolean isExcludeNullProperties() {
|
||||
return excludeNullProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not serialize properties with a null value
|
||||
*
|
||||
* @param excludeNullProperties
|
||||
*/
|
||||
public void setExcludeNullProperties(boolean excludeNullProperties) {
|
||||
this.excludeNullProperties = excludeNullProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status code to be set in the response
|
||||
*
|
||||
* @param statusCode
|
||||
*/
|
||||
public void setStatusCode(int statusCode) {
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error code to be set in the response
|
||||
*
|
||||
* @param errorCode
|
||||
*/
|
||||
public void setErrorCode(int errorCode) {
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public void setCallbackParameter(String callbackParameter) {
|
||||
this.callbackParameter = callbackParameter;
|
||||
}
|
||||
|
||||
public String getCallbackParameter() {
|
||||
return callbackParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix JSON with "{} &&"
|
||||
*
|
||||
* @param prefix
|
||||
*/
|
||||
public void setPrefix(boolean prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content type to be set in the response
|
||||
*
|
||||
* @param contentType
|
||||
*/
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public String getWrapPrefix() {
|
||||
return wrapPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Text to be inserted at the begining of the response
|
||||
*/
|
||||
public void setWrapPrefix(String wrapPrefix) {
|
||||
this.wrapPrefix = wrapPrefix;
|
||||
}
|
||||
|
||||
public String getWrapSuffix() {
|
||||
return wrapSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Text to be inserted at the end of the response
|
||||
*/
|
||||
public void setWrapSuffix(String wrapSuffix) {
|
||||
this.wrapSuffix = wrapSuffix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.xwork.StringUtils;
|
||||
import org.apache.struts2.json.annotations.SMDMethod;
|
||||
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Wrapper for JSONWriter with some utility methods.
|
||||
*/
|
||||
public class JSONUtil {
|
||||
final static String RFC3339_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JSONUtil.class);
|
||||
|
||||
/**
|
||||
* Serializes an object into JSON.
|
||||
*
|
||||
* @param object
|
||||
* to be serialized
|
||||
* @return JSON string
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String serialize(Object object) throws JSONException {
|
||||
JSONWriter writer = new JSONWriter();
|
||||
|
||||
return writer.write(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes an object into JSON, excluding any properties matching any of
|
||||
* the regular expressions in the given collection.
|
||||
*
|
||||
* @param object
|
||||
* to be serialized
|
||||
* @param excludeProperties
|
||||
* Patterns matching properties to exclude
|
||||
* @param ignoreHierarchy
|
||||
* whether to ignore properties defined on base classes of the
|
||||
* root object
|
||||
* @return JSON string
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String serialize(Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean excludeNullProperties)
|
||||
throws JSONException {
|
||||
JSONWriter writer = new JSONWriter();
|
||||
writer.setIgnoreHierarchy(ignoreHierarchy);
|
||||
return writer.write(object, excludeProperties, includeProperties, excludeNullProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes an object into JSON, excluding any properties matching any of
|
||||
* the regular expressions in the given collection.
|
||||
*
|
||||
* @param object
|
||||
* to be serialized
|
||||
* @param excludeProperties
|
||||
* Patterns matching properties to exclude
|
||||
* @param ignoreHierarchy
|
||||
* whether to ignore properties defined on base classes of the
|
||||
* root object
|
||||
* @param enumAsBean
|
||||
* whether to serialized enums a Bean or name=value pair
|
||||
* @return JSON string
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String serialize(Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean enumAsBean,
|
||||
boolean excludeNullProperties) throws JSONException {
|
||||
JSONWriter writer = new JSONWriter();
|
||||
writer.setIgnoreHierarchy(ignoreHierarchy);
|
||||
writer.setEnumAsBean(enumAsBean);
|
||||
return writer.write(object, excludeProperties, includeProperties, excludeNullProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes an object into JSON to the given writer.
|
||||
*
|
||||
* @param writer
|
||||
* Writer to serialize the object to
|
||||
* @param object
|
||||
* object to be serialized
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static void serialize(Writer writer, Object object) throws IOException, JSONException {
|
||||
writer.write(serialize(object));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes an object into JSON to the given writer, excluding any
|
||||
* properties matching any of the regular expressions in the given
|
||||
* collection.
|
||||
*
|
||||
* @param writer
|
||||
* Writer to serialize the object to
|
||||
* @param object
|
||||
* object to be serialized
|
||||
* @param excludeProperties
|
||||
* Patterns matching properties to ignore
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static void serialize(Writer writer, Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean excludeNullProperties) throws IOException,
|
||||
JSONException {
|
||||
writer.write(serialize(object, excludeProperties, includeProperties, true, excludeNullProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a object from JSON
|
||||
*
|
||||
* @param json
|
||||
* string in JSON
|
||||
* @return desrialized object
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static Object deserialize(String json) throws JSONException {
|
||||
JSONReader reader = new JSONReader();
|
||||
return reader.read(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a object from JSON
|
||||
*
|
||||
* @param reader
|
||||
* Reader to read a JSON string from
|
||||
* @return deserialized object
|
||||
* @throws JSONException
|
||||
* when IOException happens
|
||||
*/
|
||||
public static Object deserialize(Reader reader) throws JSONException {
|
||||
// read content
|
||||
BufferedReader bufferReader = new BufferedReader(reader);
|
||||
String line = null;
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
try {
|
||||
while ((line = bufferReader.readLine()) != null) {
|
||||
buffer.append(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
|
||||
return deserialize(buffer.toString());
|
||||
}
|
||||
|
||||
public static void writeJSONToResponse(SerializationParams serializationParams) throws IOException {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (StringUtils.isNotBlank(serializationParams.getSerializedJSON()))
|
||||
stringBuilder.append(serializationParams.getSerializedJSON());
|
||||
|
||||
if (StringUtils.isNotBlank(serializationParams.getWrapPrefix()))
|
||||
stringBuilder.insert(0, serializationParams.getWrapPrefix());
|
||||
else if (serializationParams.isWrapWithComments()) {
|
||||
stringBuilder.insert(0, "/* ");
|
||||
stringBuilder.append(" */");
|
||||
} else if (serializationParams.isPrefix())
|
||||
stringBuilder.insert(0, "{}&& ");
|
||||
|
||||
if (StringUtils.isNotBlank(serializationParams.getWrapSuffix()))
|
||||
stringBuilder.append(serializationParams.getWrapSuffix());
|
||||
|
||||
String json = stringBuilder.toString();
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("[JSON]" + json);
|
||||
}
|
||||
|
||||
HttpServletResponse response = serializationParams.getResponse();
|
||||
|
||||
// status or error code
|
||||
if (serializationParams.getStatusCode() > 0)
|
||||
response.setStatus(serializationParams.getStatusCode());
|
||||
else if (serializationParams.getErrorCode() > 0)
|
||||
response.sendError(serializationParams.getErrorCode());
|
||||
|
||||
// content type
|
||||
if (serializationParams.isSmd())
|
||||
response.setContentType("application/json-rpc;charset=" + serializationParams.getEncoding());
|
||||
else
|
||||
response.setContentType(serializationParams.getContentType() + ";charset="
|
||||
+ serializationParams.getEncoding());
|
||||
|
||||
if (serializationParams.isNoCache()) {
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
response.setHeader("Pragma", "No-cache");
|
||||
}
|
||||
|
||||
if (serializationParams.isGzip()) {
|
||||
response.addHeader("Content-Encoding", "gzip");
|
||||
GZIPOutputStream out = null;
|
||||
InputStream in = null;
|
||||
try {
|
||||
out = new GZIPOutputStream(response.getOutputStream());
|
||||
in = new ByteArrayInputStream(json.getBytes());
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
} finally {
|
||||
if (in != null)
|
||||
in.close();
|
||||
if (out != null) {
|
||||
out.finish();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
response.setContentLength(json.getBytes(serializationParams.getEncoding()).length);
|
||||
PrintWriter out = response.getWriter();
|
||||
out.print(json);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> asList(String commaDelim) {
|
||||
if ((commaDelim == null) || (commaDelim.trim().length() == 0))
|
||||
return null;
|
||||
List<String> list = new ArrayList<String>();
|
||||
String[] split = commaDelim.split(",");
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
String trimmed = split[i].trim();
|
||||
if (trimmed.length() > 0) {
|
||||
list.add(trimmed);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* List visible methods carrying the
|
||||
*
|
||||
* @SMDMethod annotation
|
||||
*
|
||||
* @param ignoreInterfaces
|
||||
* if true, only the methods of the class are examined. If false,
|
||||
* annotations on every interfaces' methods are examined.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Method[] listSMDMethods(Class clazz, boolean ignoreInterfaces) {
|
||||
final List<Method> methods = new LinkedList<Method>();
|
||||
if (ignoreInterfaces) {
|
||||
for (Method method : clazz.getMethods()) {
|
||||
SMDMethod smdMethodAnnotation = method.getAnnotation(SMDMethod.class);
|
||||
if (smdMethodAnnotation != null) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// recurse the entire superclass/interface hierarchy and add in
|
||||
// order encountered
|
||||
JSONUtil.visitInterfaces(clazz, new JSONUtil.ClassVisitor() {
|
||||
public boolean visit(Class aClass) {
|
||||
for (Method method : aClass.getMethods()) {
|
||||
SMDMethod smdMethodAnnotation = method.getAnnotation(SMDMethod.class);
|
||||
if ((smdMethodAnnotation != null) && !methods.contains(method)) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Method[] methodResult = new Method[methods.size()];
|
||||
return methods.toArray(methodResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Realizes the visit(Class) method called by vistInterfaces for all
|
||||
* encountered classes/interfaces
|
||||
*/
|
||||
public static interface ClassVisitor {
|
||||
|
||||
/**
|
||||
* Called when a new interface/class is encountered
|
||||
*
|
||||
* @param aClass
|
||||
* the encountered class/interface
|
||||
* @return true if the recursion should continue, false to stop
|
||||
* recursion immediately
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
boolean visit(Class aClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit all the interfaces realized by the specified object, its
|
||||
* superclasses and its interfaces <p/> Visitation is performed in the
|
||||
* following order: aClass aClass' interfaces the interface's superclasses
|
||||
* (interfaces) aClass' superclass superclass' interfaces superclass'
|
||||
* interface's superclasses (interfaces) super-superclass and so on <p/> The
|
||||
* Object base class is base excluded. Classes/interfaces are only visited
|
||||
* once each
|
||||
*
|
||||
* @param aClass
|
||||
* the class to start recursing upwards from
|
||||
* @param visitor
|
||||
* this vistor is called for each class/interface encountered
|
||||
* @return true if all classes/interfaces were visited, false if it was
|
||||
* exited early as specified by a ClassVisitor result
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean visitInterfaces(Class aClass, ClassVisitor visitor) {
|
||||
List<Class> classesVisited = new LinkedList<Class>();
|
||||
return visitUniqueInterfaces(aClass, visitor, classesVisited);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive method to visit all the interfaces of a class (and its
|
||||
* superclasses and super-interfaces) if they haven't already been visited.
|
||||
* <p/> Always visits itself if it hasn't already been visited
|
||||
*
|
||||
* @param thisClass
|
||||
* the current class to visit (if not already done so)
|
||||
* @param classesVisited
|
||||
* classes already visited
|
||||
* @param visitor
|
||||
* this vistor is called for each class/interface encountered
|
||||
* @return true if recursion can continue, false if it should be aborted
|
||||
*/
|
||||
private static boolean visitUniqueInterfaces(Class thisClass, ClassVisitor visitor,
|
||||
List<Class> classesVisited) {
|
||||
boolean okayToContinue = true;
|
||||
|
||||
if (!classesVisited.contains(thisClass)) {
|
||||
classesVisited.add(thisClass);
|
||||
okayToContinue = visitor.visit(thisClass);
|
||||
|
||||
if (okayToContinue) {
|
||||
Class[] interfaces = thisClass.getInterfaces();
|
||||
int index = 0;
|
||||
while ((index < interfaces.length) && (okayToContinue)) {
|
||||
okayToContinue = visitUniqueInterfaces(interfaces[index++], visitor, classesVisited);
|
||||
}
|
||||
|
||||
if (okayToContinue) {
|
||||
Class superClass = thisClass.getSuperclass();
|
||||
if ((superClass != null) && (!Object.class.equals(superClass))) {
|
||||
okayToContinue = visitUniqueInterfaces(superClass, visitor, classesVisited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return okayToContinue;
|
||||
}
|
||||
|
||||
public static boolean isGzipInRequest(HttpServletRequest request) {
|
||||
String header = request.getHeader("Accept-Encoding");
|
||||
return (header != null) && (header.indexOf("gzip") >= 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.text.StringCharacterIterator;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.struts2.json.annotations.JSON;
|
||||
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Serializes an object into JavaScript Object Notation (JSON). If cyclic
|
||||
* references are detected they will be nulled out.
|
||||
* </p>
|
||||
*/
|
||||
class JSONWriter {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JSONWriter.class);
|
||||
|
||||
/**
|
||||
* By default, enums are serialzied as name=value pairs
|
||||
*/
|
||||
public static final boolean ENUM_AS_BEAN_DEFAULT = false;
|
||||
|
||||
static char[] hex = "0123456789ABCDEF".toCharArray();
|
||||
private StringBuilder buf = new StringBuilder();
|
||||
private Stack stack = new Stack();
|
||||
private boolean ignoreHierarchy = true;
|
||||
private Object root;
|
||||
private boolean buildExpr = true;
|
||||
private String exprStack = "";
|
||||
private Collection<Pattern> excludeProperties;
|
||||
private Collection<Pattern> includeProperties;
|
||||
private DateFormat formatter;
|
||||
private boolean enumAsBean = ENUM_AS_BEAN_DEFAULT;
|
||||
private boolean excludeNullProperties;
|
||||
|
||||
/**
|
||||
* @param object
|
||||
* Object to be serialized into JSON
|
||||
* @return JSON string for object
|
||||
* @throws JSONException
|
||||
*/
|
||||
public String write(Object object) throws JSONException {
|
||||
return this.write(object, null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object
|
||||
* Object to be serialized into JSON
|
||||
* @return JSON string for object
|
||||
* @throws JSONException
|
||||
*/
|
||||
public String write(Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean excludeNullProperties) throws JSONException {
|
||||
this.excludeNullProperties = excludeNullProperties;
|
||||
this.buf.setLength(0);
|
||||
this.root = object;
|
||||
this.exprStack = "";
|
||||
this.buildExpr = ((excludeProperties != null) && !excludeProperties.isEmpty())
|
||||
|| ((includeProperties != null) && !includeProperties.isEmpty());
|
||||
this.excludeProperties = excludeProperties;
|
||||
this.includeProperties = includeProperties;
|
||||
this.value(object, null);
|
||||
|
||||
return this.buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect cyclic references
|
||||
*/
|
||||
private void value(Object object, Method method) throws JSONException {
|
||||
if (object == null) {
|
||||
this.add("null");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.stack.contains(object)) {
|
||||
Class clazz = object.getClass();
|
||||
|
||||
// cyclic reference
|
||||
if (clazz.isPrimitive() || clazz.equals(String.class)) {
|
||||
this.process(object, method);
|
||||
} else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Cyclic reference detected on " + object);
|
||||
}
|
||||
|
||||
this.add("null");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.process(object, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize object into json
|
||||
*/
|
||||
private void process(Object object, Method method) throws JSONException {
|
||||
this.stack.push(object);
|
||||
|
||||
if (object instanceof Class) {
|
||||
this.string(object);
|
||||
} else if (object instanceof Boolean) {
|
||||
this.bool(((Boolean) object).booleanValue());
|
||||
} else if (object instanceof Number) {
|
||||
this.add(object);
|
||||
} else if (object instanceof String) {
|
||||
this.string(object);
|
||||
} else if (object instanceof Character) {
|
||||
this.string(object);
|
||||
} else if (object instanceof Map) {
|
||||
this.map((Map) object, method);
|
||||
} else if (object.getClass().isArray()) {
|
||||
this.array(object, method);
|
||||
} else if (object instanceof Iterable) {
|
||||
this.array(((Iterable) object).iterator(), method);
|
||||
} else if (object instanceof Date) {
|
||||
this.date((Date) object, method);
|
||||
} else if (object instanceof Calendar) {
|
||||
this.date(((Calendar) object).getTime(), method);
|
||||
} else if (object instanceof Locale) {
|
||||
this.string(object);
|
||||
} else if (object instanceof Enum) {
|
||||
this.enumeration((Enum) object);
|
||||
} else {
|
||||
this.bean(object);
|
||||
}
|
||||
|
||||
this.stack.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instrospect bean and serialize its properties
|
||||
*/
|
||||
private void bean(Object object) throws JSONException {
|
||||
this.add("{");
|
||||
|
||||
BeanInfo info;
|
||||
|
||||
try {
|
||||
Class clazz = object.getClass();
|
||||
|
||||
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
|
||||
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("}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Instrospect an Enum and serialize it as a name/value pair or as a bean
|
||||
* including all its own properties
|
||||
*/
|
||||
private void enumeration(Enum enumeration) throws JSONException {
|
||||
if (enumAsBean) {
|
||||
this.bean(enumeration);
|
||||
} else {
|
||||
this.string(enumeration.name());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore "class" field
|
||||
*/
|
||||
private boolean shouldExcludeProperty(Class clazz, PropertyDescriptor prop) throws SecurityException,
|
||||
NoSuchFieldException {
|
||||
String name = prop.getName();
|
||||
|
||||
if (name.equals("class") || name.equals("declaringClass") || name.equals("cachedSuperClass")
|
||||
|| name.equals("metaClass")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String expandExpr(int i) {
|
||||
return this.exprStack + "[" + i + "]";
|
||||
}
|
||||
|
||||
private String expandExpr(String property) {
|
||||
if (this.exprStack.length() == 0)
|
||||
return property;
|
||||
return this.exprStack + "." + property;
|
||||
}
|
||||
|
||||
private String setExprStack(String expr) {
|
||||
String s = this.exprStack;
|
||||
this.exprStack = expr;
|
||||
return s;
|
||||
}
|
||||
|
||||
private boolean shouldExcludeProperty(String expr) {
|
||||
if (this.excludeProperties != null) {
|
||||
for (Pattern pattern : this.excludeProperties) {
|
||||
if (pattern.matcher(expr).matches()) {
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Ignoring property because of exclude rule: " + expr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.includeProperties != null) {
|
||||
for (Pattern pattern : this.includeProperties) {
|
||||
if (pattern.matcher(expr).matches()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Ignoring property because of include rule: " + expr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add name/value pair to buffer
|
||||
*/
|
||||
private boolean add(String name, Object value, Method method, boolean hasData) throws JSONException {
|
||||
if (!excludeNullProperties || (value != null)) {
|
||||
if (hasData) {
|
||||
this.add(',');
|
||||
}
|
||||
this.add('"');
|
||||
this.add(name);
|
||||
this.add("\":");
|
||||
this.value(value, method);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add map to buffer
|
||||
*/
|
||||
private void map(Map map, Method method) throws JSONException {
|
||||
this.add("{");
|
||||
|
||||
Iterator it = map.entrySet().iterator();
|
||||
|
||||
boolean warnedNonString = false; // one report per map
|
||||
boolean hasData = false;
|
||||
while (it.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
Object key = entry.getKey();
|
||||
String expr = null;
|
||||
if (this.buildExpr) {
|
||||
if (key == null) {
|
||||
LOG.error("Cannot build expression for null key in " + this.exprStack);
|
||||
continue;
|
||||
} else {
|
||||
expr = this.expandExpr(key.toString());
|
||||
if (this.shouldExcludeProperty(expr)) {
|
||||
continue;
|
||||
}
|
||||
expr = this.setExprStack(expr);
|
||||
}
|
||||
}
|
||||
if (hasData) {
|
||||
this.add(',');
|
||||
}
|
||||
hasData = true;
|
||||
if (!warnedNonString && !(key instanceof String)) {
|
||||
LOG.warn("JavaScript doesn't support non-String keys, using toString() on "
|
||||
+ key.getClass().getName());
|
||||
warnedNonString = true;
|
||||
}
|
||||
this.value(key.toString(), method);
|
||||
this.add(":");
|
||||
this.value(entry.getValue(), method);
|
||||
if (this.buildExpr) {
|
||||
this.setExprStack(expr);
|
||||
}
|
||||
}
|
||||
|
||||
this.add("}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add date to buffer
|
||||
*/
|
||||
private void date(Date date, Method method) {
|
||||
JSON json = null;
|
||||
if (method != null)
|
||||
json = method.getAnnotation(JSON.class);
|
||||
if (this.formatter == null)
|
||||
this.formatter = new SimpleDateFormat(JSONUtil.RFC3339_FORMAT);
|
||||
|
||||
DateFormat formatter = (json != null) && (json.format().length() > 0) ? new SimpleDateFormat(json
|
||||
.format()) : this.formatter;
|
||||
this.string(formatter.format(date));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add array to buffer
|
||||
*/
|
||||
private void array(Iterator it, Method method) throws JSONException {
|
||||
this.add("[");
|
||||
|
||||
boolean hasData = false;
|
||||
for (int i = 0; it.hasNext(); i++) {
|
||||
String expr = null;
|
||||
if (this.buildExpr) {
|
||||
expr = this.expandExpr(i);
|
||||
if (this.shouldExcludeProperty(expr)) {
|
||||
it.next();
|
||||
continue;
|
||||
}
|
||||
expr = this.setExprStack(expr);
|
||||
}
|
||||
if (hasData) {
|
||||
this.add(',');
|
||||
}
|
||||
hasData = true;
|
||||
this.value(it.next(), method);
|
||||
if (this.buildExpr) {
|
||||
this.setExprStack(expr);
|
||||
}
|
||||
}
|
||||
|
||||
this.add("]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add array to buffer
|
||||
*/
|
||||
private void array(Object object, Method method) throws JSONException {
|
||||
this.add("[");
|
||||
|
||||
int length = Array.getLength(object);
|
||||
|
||||
boolean hasData = false;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
String expr = null;
|
||||
if (this.buildExpr) {
|
||||
expr = this.expandExpr(i);
|
||||
if (this.shouldExcludeProperty(expr)) {
|
||||
continue;
|
||||
}
|
||||
expr = this.setExprStack(expr);
|
||||
}
|
||||
if (hasData) {
|
||||
this.add(',');
|
||||
}
|
||||
hasData = true;
|
||||
this.value(Array.get(object, i), method);
|
||||
if (this.buildExpr) {
|
||||
this.setExprStack(expr);
|
||||
}
|
||||
}
|
||||
|
||||
this.add("]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add boolean to buffer
|
||||
*/
|
||||
private void bool(boolean b) {
|
||||
this.add(b ? "true" : "false");
|
||||
}
|
||||
|
||||
/**
|
||||
* escape characters
|
||||
*/
|
||||
private void string(Object obj) {
|
||||
this.add('"');
|
||||
|
||||
CharacterIterator it = new StringCharacterIterator(obj.toString());
|
||||
|
||||
for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
|
||||
if (c == '"') {
|
||||
this.add("\\\"");
|
||||
} else if (c == '\\') {
|
||||
this.add("\\\\");
|
||||
} else if (c == '/') {
|
||||
this.add("\\/");
|
||||
} else if (c == '\b') {
|
||||
this.add("\\b");
|
||||
} else if (c == '\f') {
|
||||
this.add("\\f");
|
||||
} else if (c == '\n') {
|
||||
this.add("\\n");
|
||||
} else if (c == '\r') {
|
||||
this.add("\\r");
|
||||
} else if (c == '\t') {
|
||||
this.add("\\t");
|
||||
} else if (Character.isISOControl(c)) {
|
||||
this.unicode(c);
|
||||
} else {
|
||||
this.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
this.add('"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add object to buffer
|
||||
*/
|
||||
private void add(Object obj) {
|
||||
this.buf.append(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add char to buffer
|
||||
*/
|
||||
private void add(char c) {
|
||||
this.buf.append(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent as unicode
|
||||
*
|
||||
* @param c
|
||||
* character to be encoded
|
||||
*/
|
||||
private void unicode(char c) {
|
||||
this.add("\\u");
|
||||
|
||||
int n = c;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int digit = (n & 0xf000) >> 12;
|
||||
|
||||
this.add(hex[digit]);
|
||||
n <<= 4;
|
||||
}
|
||||
}
|
||||
|
||||
public void setIgnoreHierarchy(boolean ignoreHierarchy) {
|
||||
this.ignoreHierarchy = ignoreHierarchy;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, an Enum is serialized as a bean with a special property
|
||||
* _name=name() as all as all other properties defined within the enum.<br/>
|
||||
* If false, an Enum is serialized as a name=value pair (name=name())
|
||||
*
|
||||
* @param enumAsBean
|
||||
* true to serialize an enum as a bean instead of as a name=value
|
||||
* pair (default=false)
|
||||
*/
|
||||
public void setEnumAsBean(boolean enumAsBean) {
|
||||
this.enumAsBean = enumAsBean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.xwork.StringUtils;
|
||||
|
||||
public class SerializationParams {
|
||||
private static final String DEFAULT_CONTENT_TYPE = "application/json";
|
||||
|
||||
private final HttpServletResponse response;
|
||||
private final String encoding;
|
||||
private final boolean wrapWithComments;
|
||||
private final String serializedJSON;
|
||||
private final boolean smd;
|
||||
private final boolean gzip;
|
||||
private final boolean noCache;
|
||||
private final int statusCode;
|
||||
private final int errorCode;
|
||||
private final boolean prefix;
|
||||
private String contentType = DEFAULT_CONTENT_TYPE;
|
||||
private String wrapPrefix;
|
||||
private String wrapSuffix;
|
||||
|
||||
public SerializationParams(HttpServletResponse response, String encoding, boolean wrapWithComments,
|
||||
String serializedJSON, boolean smd, boolean gzip, boolean noCache, int statusCode, int errorCode,
|
||||
boolean prefix, String contentType, String wrapPrefix, String wrapSuffix) {
|
||||
this.response = response;
|
||||
this.encoding = encoding;
|
||||
this.wrapWithComments = wrapWithComments;
|
||||
this.serializedJSON = serializedJSON;
|
||||
this.smd = smd;
|
||||
this.gzip = gzip;
|
||||
this.noCache = noCache;
|
||||
this.statusCode = statusCode;
|
||||
this.errorCode = errorCode;
|
||||
this.prefix = prefix;
|
||||
this.contentType = StringUtils.defaultString(contentType, DEFAULT_CONTENT_TYPE);
|
||||
this.wrapPrefix = wrapPrefix;
|
||||
this.wrapSuffix = wrapSuffix;
|
||||
}
|
||||
|
||||
public SerializationParams(HttpServletResponse response, String defaultEncoding,
|
||||
boolean wrapWithComments, String json, boolean b, boolean b1, boolean noCache, int i, int i1,
|
||||
boolean prefix, String contentType) {
|
||||
this(response, defaultEncoding, wrapWithComments, json, b, b1, noCache, i, i1, prefix, contentType,
|
||||
null, null);
|
||||
}
|
||||
|
||||
public String getWrapSuffix() {
|
||||
return wrapSuffix;
|
||||
}
|
||||
|
||||
public String getWrapPrefix() {
|
||||
return wrapPrefix;
|
||||
}
|
||||
|
||||
public HttpServletResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public boolean isWrapWithComments() {
|
||||
return wrapWithComments;
|
||||
}
|
||||
|
||||
public String getSerializedJSON() {
|
||||
return serializedJSON;
|
||||
}
|
||||
|
||||
public boolean isSmd() {
|
||||
return smd;
|
||||
}
|
||||
|
||||
public boolean isGzip() {
|
||||
return gzip;
|
||||
}
|
||||
|
||||
public boolean isNoCache() {
|
||||
return noCache;
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public boolean isPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/*
|
||||
* Annotation used to customize serialization
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface JSON {
|
||||
String name() default "";
|
||||
|
||||
boolean serialize() default true;
|
||||
|
||||
boolean deserialize() default true;
|
||||
|
||||
String format() default "";
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SMD {
|
||||
String version() default org.apache.struts2.json.smd.SMD.DEFAULT_VERSION;
|
||||
|
||||
String objectName();
|
||||
|
||||
String serviceType() default org.apache.struts2.json.smd.SMD.DEFAULT_SERVICE_TYPE;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SMDMethod {
|
||||
String name() default "";
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SMDMethodParameter {
|
||||
String name();
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json.rpc;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
/*
|
||||
* Used to serialize RPC Errors
|
||||
*/
|
||||
public class RPCError {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RPCError.class);
|
||||
|
||||
private int code;
|
||||
private String name;
|
||||
private String message;
|
||||
private String stack;
|
||||
|
||||
public RPCError() {
|
||||
}
|
||||
|
||||
public RPCError(String message, int code) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
|
||||
LOG.error(message);
|
||||
}
|
||||
|
||||
public RPCError(String message, RPCErrorCode code) {
|
||||
this(message, code.code());
|
||||
}
|
||||
|
||||
public RPCError(Throwable t, int code, boolean debug) {
|
||||
while (t.getCause() != null) {
|
||||
t = t.getCause();
|
||||
}
|
||||
|
||||
this.code = code;
|
||||
this.message = t.getMessage();
|
||||
this.name = t.getClass().getName();
|
||||
|
||||
if (debug) {
|
||||
StringWriter s = new StringWriter();
|
||||
PrintWriter w = new PrintWriter(s);
|
||||
t.printStackTrace(w);
|
||||
w.flush();
|
||||
this.stack = s.toString();
|
||||
}
|
||||
|
||||
LOG.error(t.getMessage(), t);
|
||||
}
|
||||
|
||||
public RPCError(Throwable t, RPCErrorCode code, boolean debug) {
|
||||
this(t, code.code(), debug);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
public void setStack(String stack) {
|
||||
this.stack = stack;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json.rpc;
|
||||
|
||||
public enum RPCErrorCode {
|
||||
MISSING_METHOD(100, "'method' parameter is missing in request"),
|
||||
MISSING_ID(100, "'id' parameter is missing in request"),
|
||||
INVALID_PROCEDURE_CALL(0, "Invalid procedure call"),
|
||||
METHOD_NOT_FOUND(101, "Procedure not found"),
|
||||
PARAMETERS_MISMATCH(102, "Parameters count in request does not patch parameters count on method"),
|
||||
EXCEPTION(103, "An exception was thrown"),
|
||||
SMD_DISABLED(104, "SMD is disabled");
|
||||
|
||||
private int code;
|
||||
private String message;
|
||||
|
||||
RPCErrorCode(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String message() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json.rpc;
|
||||
|
||||
/**
|
||||
* Class that will be serialized as a response to an RPC call
|
||||
*/
|
||||
public class RPCResponse {
|
||||
private String id;
|
||||
private Object result;
|
||||
private RPCError error;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(Object result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public RPCError getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(RPCError error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json.smd;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/*
|
||||
* HOlds SMD declarations for a class
|
||||
*/
|
||||
public class SMD {
|
||||
public static final String DEFAULT_VERSION = ".1";
|
||||
public static final String DEFAULT_SERVICE_TYPE = "JSON-RPC";
|
||||
|
||||
private String version = DEFAULT_VERSION;
|
||||
private String objectName;
|
||||
private String serviceType = DEFAULT_SERVICE_TYPE;
|
||||
private String serviceUrl;
|
||||
private Set<SMDMethod> methods = new TreeSet<SMDMethod>();
|
||||
|
||||
public void addSMDMethod(SMDMethod method) {
|
||||
this.methods.add(method);
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getObjectName() {
|
||||
return this.objectName;
|
||||
}
|
||||
|
||||
public void setObjectName(String objectName) {
|
||||
this.objectName = objectName;
|
||||
}
|
||||
|
||||
public String getServiceType() {
|
||||
return this.serviceType;
|
||||
}
|
||||
|
||||
public void setServiceType(String serviceType) {
|
||||
this.serviceType = serviceType;
|
||||
}
|
||||
|
||||
public String getServiceUrl() {
|
||||
return this.serviceUrl;
|
||||
}
|
||||
|
||||
public void setServiceUrl(String serviceUrl) {
|
||||
this.serviceUrl = serviceUrl;
|
||||
}
|
||||
|
||||
public Set<SMDMethod> getMethods() {
|
||||
return this.methods;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json.smd;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class SMDMethod implements Comparable {
|
||||
private String name;
|
||||
private Set<SMDMethodParameter> parameters = new TreeSet<SMDMethodParameter>();
|
||||
|
||||
public SMDMethod(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void addSMDMethodParameter(SMDMethodParameter parameter) {
|
||||
this.parameters.add(parameter);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<SMDMethodParameter> getParameters() {
|
||||
return this.parameters;
|
||||
}
|
||||
|
||||
public int compareTo(Object o) {
|
||||
if (!(o instanceof SMDMethod))
|
||||
return 1;
|
||||
if (o == null)
|
||||
return 1;
|
||||
SMDMethod other = (SMDMethod) o;
|
||||
if ((name == null) && (other.name == null))
|
||||
return 0;
|
||||
if (name == null)
|
||||
return -1;
|
||||
if (name.equals(other.name))
|
||||
return parameters.size() - other.parameters.size();
|
||||
|
||||
return name.compareTo(other.name);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof SMDMethod))
|
||||
return false;
|
||||
SMDMethod toCompare = (SMDMethod) obj;
|
||||
if ((name == null) && (toCompare.name == null))
|
||||
return true;
|
||||
return (name != null) && name.equals(toCompare.name)
|
||||
&& (parameters.size() == toCompare.parameters.size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json.smd;
|
||||
|
||||
public class SMDMethodParameter implements Comparable {
|
||||
private String name;
|
||||
|
||||
public SMDMethodParameter(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int compareTo(Object o) {
|
||||
if (!(o instanceof SMDMethodParameter))
|
||||
return 1;
|
||||
if (o == null)
|
||||
return 1;
|
||||
if ((name == null) && (((SMDMethodParameter) o).name == null))
|
||||
return 0;
|
||||
if (name == null)
|
||||
return -1;
|
||||
return name.compareTo(((SMDMethodParameter) o).name);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof SMDMethodParameter))
|
||||
return false;
|
||||
if ((name == null) && (((SMDMethodParameter) o).name == null))
|
||||
return true;
|
||||
return (name != null) && name.equals(((SMDMethodParameter) o).name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
|
||||
version="2.0">
|
||||
|
||||
<description>
|
||||
This exposes JSON util functions from the Struts JSON plugin
|
||||
</description>
|
||||
|
||||
<tlib-version>1.0</tlib-version>
|
||||
|
||||
<short-name>json</short-name>
|
||||
|
||||
<uri>/struts-json-tags</uri>
|
||||
|
||||
<function>
|
||||
<name>serialize</name>
|
||||
<function-class>org.apache.struts2.json.JSONUtil</function-class>
|
||||
<function-signature>
|
||||
java.lang.String serialize(java.lang.Object)
|
||||
</function-signature>
|
||||
</function>
|
||||
|
||||
</taglib>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?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-default" extends="struts-default">
|
||||
<result-types>
|
||||
<result-type name="json" class="org.apache.struts2.json.JSONResult"/>
|
||||
</result-types>
|
||||
<interceptors>
|
||||
<interceptor name="json" class="org.apache.struts2.json.JSONInterceptor"/>
|
||||
</interceptors>
|
||||
</package>
|
||||
</struts>
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
/**
|
||||
* An enumeration for JSON serialization testing
|
||||
*/
|
||||
public enum AnEnum {
|
||||
ValueA, ValueB, ValueC
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
/**
|
||||
* A more complicated Enum with additional properties.
|
||||
*/
|
||||
public enum AnEnumBean {
|
||||
|
||||
One("A", "B"), Two("C", "D"), Three("E", "F");
|
||||
|
||||
private String propA;
|
||||
private String propB;
|
||||
|
||||
AnEnumBean(String propA, String propB) {
|
||||
this.propA = propA;
|
||||
this.propB = propB;
|
||||
}
|
||||
|
||||
public String getPropA() {
|
||||
return propA;
|
||||
}
|
||||
|
||||
public String getPropB() {
|
||||
return propB;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class Bean {
|
||||
private String stringField;
|
||||
private int intField;
|
||||
private boolean booleanField;
|
||||
private char charField;
|
||||
private long longField;
|
||||
private float floatField;
|
||||
private double doubleField;
|
||||
private Object objectField;
|
||||
private byte byteField;
|
||||
private AnEnum enumField;
|
||||
private AnEnumBean enumBean;
|
||||
private BigDecimal bigDecimal;
|
||||
private BigInteger bigInteger;
|
||||
|
||||
/**
|
||||
* @return the byteField
|
||||
*/
|
||||
public byte getByteField() {
|
||||
return this.byteField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param byteField
|
||||
* the byteField to set
|
||||
*/
|
||||
public void setByteField(byte byteField) {
|
||||
this.byteField = byteField;
|
||||
}
|
||||
|
||||
public boolean isBooleanField() {
|
||||
return this.booleanField;
|
||||
}
|
||||
|
||||
public void setBooleanField(boolean booleanField) {
|
||||
this.booleanField = booleanField;
|
||||
}
|
||||
|
||||
public char getCharField() {
|
||||
return this.charField;
|
||||
}
|
||||
|
||||
public void setCharField(char charField) {
|
||||
this.charField = charField;
|
||||
}
|
||||
|
||||
public double getDoubleField() {
|
||||
return this.doubleField;
|
||||
}
|
||||
|
||||
public void setDoubleField(double doubleField) {
|
||||
this.doubleField = doubleField;
|
||||
}
|
||||
|
||||
public float getFloatField() {
|
||||
return this.floatField;
|
||||
}
|
||||
|
||||
public void setFloatField(float floatField) {
|
||||
this.floatField = floatField;
|
||||
}
|
||||
|
||||
public int getIntField() {
|
||||
return this.intField;
|
||||
}
|
||||
|
||||
public void setIntField(int intField) {
|
||||
this.intField = intField;
|
||||
}
|
||||
|
||||
public long getLongField() {
|
||||
return this.longField;
|
||||
}
|
||||
|
||||
public void setLongField(long longField) {
|
||||
this.longField = longField;
|
||||
}
|
||||
|
||||
public Object getObjectField() {
|
||||
return this.objectField;
|
||||
}
|
||||
|
||||
public void setObjectField(Object objectField) {
|
||||
this.objectField = objectField;
|
||||
}
|
||||
|
||||
public String getStringField() {
|
||||
return this.stringField;
|
||||
}
|
||||
|
||||
public void setStringField(String stringField) {
|
||||
this.stringField = stringField;
|
||||
}
|
||||
|
||||
public AnEnum getEnumField() {
|
||||
return enumField;
|
||||
}
|
||||
|
||||
public void setEnumField(AnEnum enumField) {
|
||||
this.enumField = enumField;
|
||||
}
|
||||
|
||||
public AnEnumBean getEnumBean() {
|
||||
return enumBean;
|
||||
}
|
||||
|
||||
public void setEnumBean(AnEnumBean enumBean) {
|
||||
this.enumBean = enumBean;
|
||||
}
|
||||
|
||||
public BigInteger getBigInteger() {
|
||||
return bigInteger;
|
||||
}
|
||||
|
||||
public void setBigInteger(BigInteger bigInteger) {
|
||||
this.bigInteger = bigInteger;
|
||||
}
|
||||
|
||||
public BigDecimal getBigDecimal() {
|
||||
return bigDecimal;
|
||||
}
|
||||
|
||||
public void setBigDecimal(BigDecimal bigDecimal) {
|
||||
this.bigDecimal = bigDecimal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Test serialization of an Enum in the two supported modes: - Enum as a
|
||||
* name=value pair; - Enum as a bean
|
||||
*/
|
||||
public class JSONEnumTest extends TestCase {
|
||||
|
||||
/**
|
||||
* Asserts that a bean can be serialized to JSON and restored as a map
|
||||
*/
|
||||
public void testEnumAsNameValue() throws Exception {
|
||||
Bean bean1 = new Bean();
|
||||
|
||||
bean1.setStringField("str");
|
||||
bean1.setBooleanField(true);
|
||||
bean1.setCharField('s');
|
||||
bean1.setDoubleField(10.1);
|
||||
bean1.setFloatField(1.5f);
|
||||
bean1.setIntField(10);
|
||||
bean1.setLongField(100);
|
||||
bean1.setEnumField(AnEnum.ValueA);
|
||||
bean1.setEnumBean(AnEnumBean.Two);
|
||||
|
||||
JSONWriter jsonWriter = new JSONWriter();
|
||||
jsonWriter.setEnumAsBean(false);
|
||||
String json = jsonWriter.write(bean1);
|
||||
|
||||
Map result = (Map) JSONUtil.deserialize(json);
|
||||
assertEquals("str", result.get("stringField"));
|
||||
assertEquals(true, result.get("booleanField"));
|
||||
assertEquals("s", result.get("charField")); // note: this is a
|
||||
// String
|
||||
assertEquals(10.1, result.get("doubleField"));
|
||||
assertEquals(1.5, result.get("floatField")); // note: this is a
|
||||
// Double
|
||||
assertEquals(10L, result.get("intField")); // note: this is a
|
||||
// Long
|
||||
assertEquals(AnEnum.ValueA, AnEnum.valueOf((String) result.get("enumField"))); // note:
|
||||
// this
|
||||
// is a
|
||||
// String
|
||||
assertEquals(AnEnumBean.Two, AnEnumBean.valueOf((String) result.get("enumBean"))); // note:
|
||||
// this
|
||||
// is a
|
||||
// String
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a bean can be serialized to JSON and restored as a map <p/>
|
||||
* In this case, the name of the enum is in _name and the two properties of
|
||||
* AnEnumBean are also serialized
|
||||
*/
|
||||
public void testEnumAsBean() throws Exception {
|
||||
Bean bean1 = new Bean();
|
||||
|
||||
bean1.setStringField("str");
|
||||
bean1.setBooleanField(true);
|
||||
bean1.setCharField('s');
|
||||
bean1.setDoubleField(10.1);
|
||||
bean1.setFloatField(1.5f);
|
||||
bean1.setIntField(10);
|
||||
bean1.setLongField(100);
|
||||
bean1.setEnumField(AnEnum.ValueA);
|
||||
bean1.setEnumBean(AnEnumBean.Two);
|
||||
|
||||
JSONWriter jsonWriter = new JSONWriter();
|
||||
jsonWriter.setEnumAsBean(true);
|
||||
String json = jsonWriter.write(bean1);
|
||||
|
||||
Map result = (Map) JSONUtil.deserialize(json);
|
||||
assertEquals("str", result.get("stringField"));
|
||||
assertEquals(true, result.get("booleanField"));
|
||||
assertEquals("s", result.get("charField")); // note: this is a
|
||||
// String
|
||||
assertEquals(10.1, result.get("doubleField"));
|
||||
assertEquals(1.5, result.get("floatField")); // note: this is a
|
||||
// Double
|
||||
assertEquals(10L, result.get("intField")); // note: this is a
|
||||
// Long
|
||||
Map enumBean1 = (Map) result.get("enumField");
|
||||
assertNotNull(enumBean1);
|
||||
assertEquals(AnEnum.ValueA, AnEnum.valueOf((String) enumBean1.get("_name"))); // get
|
||||
// the
|
||||
// special
|
||||
// name
|
||||
// property
|
||||
Map enumBean2 = (Map) result.get("enumBean");
|
||||
assertEquals(AnEnumBean.Two, AnEnumBean.valueOf((String) enumBean2.get("_name"))); // get
|
||||
// the
|
||||
// special
|
||||
// name
|
||||
// property
|
||||
assertEquals(AnEnumBean.Two.getPropA(), (String) enumBean2.get("propA")); // get
|
||||
// the
|
||||
// propA
|
||||
// property
|
||||
assertEquals(AnEnumBean.Two.getPropB(), (String) enumBean2.get("propB")); // get
|
||||
// the
|
||||
// propA
|
||||
// property
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,502 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.struts2.StrutsStatics;
|
||||
import org.apache.struts2.StrutsTestCase;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.mock.MockActionInvocation;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
public class JSONInterceptorTest extends StrutsTestCase {
|
||||
private MockActionInvocationEx invocation;
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
private void setRequestContent(String fileName) throws Exception {
|
||||
String content = TestUtils.readContent(JSONInterceptorTest.class.getResource(fileName));
|
||||
this.request.setContent(content.getBytes());
|
||||
}
|
||||
|
||||
public void testBadJSON1() throws Exception {
|
||||
tryBadJSON("bad-1.txt");
|
||||
}
|
||||
|
||||
public void testBadJSON2() throws Exception {
|
||||
tryBadJSON("bad-2.txt");
|
||||
}
|
||||
|
||||
public void testBadJSON3() throws Exception {
|
||||
tryBadJSON("bad-3.txt");
|
||||
}
|
||||
|
||||
public void testBadJSON4() throws Exception {
|
||||
tryBadJSON("bad-4.txt");
|
||||
}
|
||||
|
||||
public void testBadJSON5() throws Exception {
|
||||
tryBadJSON("bad-5.txt");
|
||||
}
|
||||
|
||||
public void testBadToTheBoneJSON4() throws Exception {
|
||||
tryBadJSON("bad-to-the-bone.txt");
|
||||
}
|
||||
|
||||
private void tryBadJSON(String fileName) throws Exception {
|
||||
// request
|
||||
setRequestContent(fileName);
|
||||
this.request.addHeader("content-type", "application/json-rpc");
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
// JSON is not well formed, throw exception
|
||||
try {
|
||||
interceptor.intercept(this.invocation);
|
||||
fail("Should have thrown an exception");
|
||||
} catch (JSONException e) {
|
||||
// I can't get JUnit to ignore the exception
|
||||
// @Test(expected = JSONException.class)
|
||||
}
|
||||
}
|
||||
|
||||
public void testSMDDisabledSMD() throws Exception {
|
||||
// request
|
||||
setRequestContent("smd-3.txt");
|
||||
this.request.addHeader("content-type", "application/json-rpc");
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
// SMD was not enabled so invocation must happen
|
||||
try {
|
||||
interceptor.intercept(this.invocation);
|
||||
} catch (JSONException e) {
|
||||
fail("Should have not thrown an exception");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void testSMDAliasedMethodCall1() throws Exception {
|
||||
// request
|
||||
setRequestContent("smd-14.txt");
|
||||
this.request.addHeader("content-type", "application/json-rpc");
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
SMDActionTest2 action = new SMDActionTest2();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
interceptor.intercept(this.invocation);
|
||||
// method was aliased, but was invoked with the regular name
|
||||
// so method must not be invoked
|
||||
assertFalse(this.invocation.isInvoked());
|
||||
assertFalse(action.isDoSomethingInvoked());
|
||||
}
|
||||
|
||||
public void testSMDAliasedMethodCall2() throws Exception {
|
||||
// request
|
||||
setRequestContent("smd-15.txt");
|
||||
this.request.addHeader("content-type", "application/json-rpc");
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
SMDActionTest2 action = new SMDActionTest2();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
interceptor.intercept(this.invocation);
|
||||
// method was aliased, but was invoked with the aliased name
|
||||
// so method must be invoked
|
||||
assertFalse(this.invocation.isInvoked());
|
||||
assertTrue(action.isDoSomethingInvoked());
|
||||
}
|
||||
|
||||
public void testSMDNoMethod() throws Exception {
|
||||
// request
|
||||
setRequestContent("smd-4.txt");
|
||||
this.request.addHeader("content-type", "application/json-rpc");
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
// SMD was enabled so invocation must happen
|
||||
|
||||
interceptor.intercept(this.invocation);
|
||||
|
||||
String json = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-13.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
|
||||
assertFalse(this.invocation.isInvoked());
|
||||
}
|
||||
|
||||
public void testSMDMethodWithoutAnnotations() throws Exception {
|
||||
// request
|
||||
setRequestContent("smd-9.txt");
|
||||
this.request.addHeader("content-type", "application/json-rpc");
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
// SMD was enabled so invocation must happen
|
||||
try {
|
||||
interceptor.intercept(this.invocation);
|
||||
assertTrue("Exception was expected here!", true);
|
||||
} catch (Exception e) {
|
||||
// ok
|
||||
}
|
||||
assertFalse(this.invocation.isInvoked());
|
||||
}
|
||||
|
||||
public void testSMDPrimitivesNoResult() throws Exception {
|
||||
// request
|
||||
setRequestContent("smd-6.txt");
|
||||
this.request.addHeader("content-type", "application/json-rpc");
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
// can't be invoked
|
||||
interceptor.intercept(this.invocation);
|
||||
assertFalse(this.invocation.isInvoked());
|
||||
|
||||
// asert values were passed properly
|
||||
assertEquals("string", action.getStringParam());
|
||||
assertEquals(1, action.getIntParam());
|
||||
assertEquals(true, action.isBooleanParam());
|
||||
assertEquals('c', action.getCharParam());
|
||||
assertEquals(2, action.getLongParam());
|
||||
assertEquals(new Float(3.3), action.getFloatParam());
|
||||
assertEquals(4.4, action.getDoubleParam());
|
||||
assertEquals(5, action.getShortParam());
|
||||
assertEquals(6, action.getByteParam());
|
||||
|
||||
String json = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-11.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
|
||||
assertEquals("application/json-rpc;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
public void testSMDReturnObject() throws Exception {
|
||||
// request
|
||||
setRequestContent("smd-10.txt");
|
||||
this.request.addHeader("content-type", "application/json-rpc");
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
SMDActionTest2 action = new SMDActionTest2();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
// can't be invoked
|
||||
interceptor.intercept(this.invocation);
|
||||
assertFalse(this.invocation.isInvoked());
|
||||
|
||||
String json = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-12.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
|
||||
assertEquals("application/json-rpc;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testSMDObjectsNoResult() throws Exception {
|
||||
// request
|
||||
setRequestContent("smd-7.txt");
|
||||
this.request.addHeader("content-type", "application/json-rpc");
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
// can't be invoked
|
||||
interceptor.intercept(this.invocation);
|
||||
assertFalse(this.invocation.isInvoked());
|
||||
|
||||
// asert values were passed properly
|
||||
Bean bean = action.getBeanParam();
|
||||
assertNotNull(bean);
|
||||
assertTrue(bean.isBooleanField());
|
||||
assertEquals(bean.getStringField(), "test");
|
||||
assertEquals(bean.getIntField(), 10);
|
||||
assertEquals(bean.getCharField(), 's');
|
||||
assertEquals(bean.getDoubleField(), 10.1);
|
||||
assertEquals(bean.getByteField(), 3);
|
||||
|
||||
List list = action.getListParam();
|
||||
assertNotNull(list);
|
||||
assertEquals("str0", list.get(0));
|
||||
assertEquals("str1", list.get(1));
|
||||
|
||||
Map map = action.getMapParam();
|
||||
assertNotNull(map);
|
||||
assertNotNull(map.get("a"));
|
||||
assertEquals(new Long(1), map.get("a"));
|
||||
assertNotNull(map.get("c"));
|
||||
List insideList = (List) map.get("c");
|
||||
assertEquals(1.0d, insideList.get(0));
|
||||
assertEquals(2.0d, insideList.get(1));
|
||||
|
||||
String json = response.getContentAsString();
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-11.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
|
||||
assertEquals("application/json-rpc;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
@SuppressWarnings( { "unchecked", "unchecked" })
|
||||
public void testReadEmpty() throws Exception {
|
||||
// request
|
||||
setRequestContent("json-6.txt");
|
||||
this.request.addHeader("content-type", "application/json");
|
||||
|
||||
// interceptor
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
TestAction action = new TestAction();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
interceptor.intercept(this.invocation);
|
||||
}
|
||||
|
||||
@SuppressWarnings( { "unchecked", "unchecked" })
|
||||
public void test() throws Exception {
|
||||
// request
|
||||
setRequestContent("json-1.txt");
|
||||
this.request.addHeader("content-type", "application/json");
|
||||
|
||||
// interceptor
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
TestAction action = new TestAction();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
interceptor.intercept(this.invocation);
|
||||
|
||||
// serialize and compare
|
||||
List list = action.getList();
|
||||
|
||||
assertNotNull(list);
|
||||
assertEquals(list.size(), 10);
|
||||
|
||||
list = action.getCollection();
|
||||
assertNotNull(list);
|
||||
assertEquals(list.size(), 3);
|
||||
assertEquals(list.get(0), "b");
|
||||
assertEquals(list.get(1), 1L);
|
||||
list = (List) list.get(2);
|
||||
assertNotNull(list);
|
||||
assertEquals(list.size(), 2);
|
||||
assertEquals(list.get(0), 10L);
|
||||
assertEquals(list.get(1), 12L);
|
||||
|
||||
list = action.getCollection2();
|
||||
assertNotNull(list);
|
||||
assertEquals(list.size(), 1);
|
||||
|
||||
// inside a map any primitive is either: String, Long, Boolean or Double
|
||||
Map bean = (Map) list.get(0);
|
||||
|
||||
assertNotNull(bean);
|
||||
assertTrue((Boolean) bean.get("booleanField"));
|
||||
assertEquals(bean.get("charField"), "s");
|
||||
assertEquals(bean.get("doubleField"), 10.1);
|
||||
assertEquals(bean.get("floatField"), 1.5);
|
||||
assertEquals(bean.get("intField"), 10L);
|
||||
assertEquals(bean.get("longField"), 100L);
|
||||
assertEquals(bean.get("stringField"), "str");
|
||||
|
||||
bean = (Map) bean.get("objectField");
|
||||
assertNotNull(bean);
|
||||
assertFalse((Boolean) bean.get("booleanField"));
|
||||
assertEquals(bean.get("charField"), "\u0000");
|
||||
assertEquals(bean.get("doubleField"), 2.2);
|
||||
assertEquals(bean.get("floatField"), 1.1);
|
||||
assertEquals(bean.get("intField"), 0L);
|
||||
assertEquals(bean.get("longField"), 0L);
|
||||
assertEquals(bean.get("stringField"), " ");
|
||||
|
||||
assertEquals(action.getFoo(), "foo");
|
||||
|
||||
Map map = action.getMap();
|
||||
|
||||
assertNotNull(map);
|
||||
assertEquals(map.size(), 2);
|
||||
assertEquals(map.get("a"), 1L);
|
||||
list = (List) map.get("c");
|
||||
assertNotNull(list);
|
||||
assertEquals(list.size(), 2);
|
||||
assertEquals(list.get(0), 1.0);
|
||||
assertEquals(list.get(1), 2.0);
|
||||
|
||||
assertEquals(action.getResult(), null);
|
||||
|
||||
Bean bean2 = action.getBean();
|
||||
|
||||
assertNotNull(bean2);
|
||||
assertTrue(bean2.isBooleanField());
|
||||
assertEquals(bean2.getStringField(), "test");
|
||||
assertEquals(bean2.getIntField(), 10);
|
||||
assertEquals(bean2.getCharField(), 's');
|
||||
assertEquals(bean2.getDoubleField(), 10.1);
|
||||
assertEquals(bean2.getByteField(), 3);
|
||||
|
||||
String[] strArray = action.getArray();
|
||||
|
||||
assertNotNull(strArray);
|
||||
assertEquals(strArray.length, 2);
|
||||
assertEquals(strArray[0], "str0");
|
||||
assertEquals(strArray[1], "str1");
|
||||
|
||||
int[] intArray = action.getIntArray();
|
||||
|
||||
assertNotNull(intArray);
|
||||
assertEquals(intArray.length, 2);
|
||||
assertEquals(intArray[0], 1);
|
||||
assertEquals(intArray[1], 2);
|
||||
|
||||
Bean[] beanArray = action.getBeanArray();
|
||||
|
||||
assertNotNull(beanArray);
|
||||
assertNotNull(beanArray[0]);
|
||||
assertEquals(beanArray[0].getStringField(), "bean1");
|
||||
assertNotNull(beanArray[1]);
|
||||
assertEquals(beanArray[1].getStringField(), "bean2");
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(action.getDate());
|
||||
|
||||
assertEquals(calendar.get(Calendar.YEAR), 1999);
|
||||
assertEquals(calendar.get(Calendar.MONTH), Calendar.DECEMBER);
|
||||
assertEquals(calendar.get(Calendar.DAY_OF_MONTH), 31);
|
||||
assertEquals(calendar.get(Calendar.HOUR), 11);
|
||||
assertEquals(calendar.get(Calendar.MINUTE), 59);
|
||||
assertEquals(calendar.get(Calendar.SECOND), 59);
|
||||
|
||||
calendar.setTime(action.getDate2());
|
||||
assertEquals(calendar.get(Calendar.YEAR), 1999);
|
||||
assertEquals(calendar.get(Calendar.MONTH), Calendar.DECEMBER);
|
||||
assertEquals(calendar.get(Calendar.DAY_OF_MONTH), 31);
|
||||
|
||||
// test desrialize=false
|
||||
assertNull(action.getFoo2());
|
||||
}
|
||||
|
||||
public void testRoot() throws Exception {
|
||||
setRequestContent("json-5.txt");
|
||||
this.request.addHeader("content-type", "application/json");
|
||||
|
||||
// interceptor
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setRoot("bean");
|
||||
TestAction4 action = new TestAction4();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
this.invocation.getStack().push(action);
|
||||
|
||||
interceptor.intercept(this.invocation);
|
||||
|
||||
Bean bean2 = action.getBean();
|
||||
|
||||
assertNotNull(bean2);
|
||||
assertTrue(bean2.isBooleanField());
|
||||
assertEquals(bean2.getStringField(), "test");
|
||||
assertEquals(bean2.getIntField(), 10);
|
||||
assertEquals(bean2.getCharField(), 's');
|
||||
assertEquals(bean2.getDoubleField(), 10.1);
|
||||
assertEquals(bean2.getByteField(), 3);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.response = new MockHttpServletResponse();
|
||||
|
||||
ActionContext context = ActionContext.getContext();
|
||||
ValueStack stack = context.getValueStack();
|
||||
|
||||
ActionContext.setContext(context);
|
||||
context.put(StrutsStatics.HTTP_REQUEST, this.request);
|
||||
context.put(StrutsStatics.HTTP_RESPONSE, this.response);
|
||||
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
|
||||
context.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
|
||||
this.invocation = new MockActionInvocationEx();
|
||||
this.invocation.setInvocationContext(context);
|
||||
this.invocation.setStack(stack);
|
||||
}
|
||||
}
|
||||
|
||||
class MockActionInvocationEx extends MockActionInvocation {
|
||||
private boolean invoked;
|
||||
|
||||
@Override
|
||||
public String invoke() throws Exception {
|
||||
this.invoked = true;
|
||||
return super.invoke();
|
||||
}
|
||||
|
||||
public boolean isInvoked() {
|
||||
return this.invoked;
|
||||
}
|
||||
|
||||
public void setInvoked(boolean invoked) {
|
||||
this.invoked = invoked;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class JSONPopulatorTest extends TestCase {
|
||||
|
||||
public void testNulls() throws IntrospectionException, InvocationTargetException, NoSuchMethodException,
|
||||
JSONException, InstantiationException, IllegalAccessException {
|
||||
JSONPopulator populator = new JSONPopulator();
|
||||
OtherBean bean = new OtherBean();
|
||||
Map jsonMap = new HashMap();
|
||||
|
||||
jsonMap.put("intField", null);
|
||||
jsonMap.put("booleanField", null);
|
||||
jsonMap.put("charField", null);
|
||||
jsonMap.put("longField", null);
|
||||
jsonMap.put("floatField", null);
|
||||
jsonMap.put("doubleField", null);
|
||||
jsonMap.put("byteField", null);
|
||||
|
||||
populator.populateObject(bean, jsonMap);
|
||||
assertNull(bean.getIntField());
|
||||
assertNull(bean.isBooleanField());
|
||||
assertNull(bean.getCharField());
|
||||
assertNull(bean.getLongField());
|
||||
assertNull(bean.getDoubleField());
|
||||
assertNull(bean.getByteField());
|
||||
}
|
||||
|
||||
public void testPrimitiveBean() throws Exception {
|
||||
StringReader stringReader = new StringReader(TestUtils.readContent(JSONInterceptorTest.class
|
||||
.getResource("json-7.txt")));
|
||||
Object json = JSONUtil.deserialize(stringReader);
|
||||
assertNotNull(json);
|
||||
assertTrue(json instanceof Map);
|
||||
Map jsonMap = (Map) json;
|
||||
JSONPopulator populator = new JSONPopulator();
|
||||
Bean bean = new Bean();
|
||||
populator.populateObject(bean, jsonMap);
|
||||
assertTrue(bean.isBooleanField());
|
||||
assertEquals("test\u000E\u000f", bean.getStringField());
|
||||
assertEquals(10, bean.getIntField());
|
||||
assertEquals('s', bean.getCharField());
|
||||
assertEquals(10.1d, bean.getDoubleField(), 0d);
|
||||
assertEquals(3, bean.getByteField());
|
||||
assertEquals(new BigDecimal(111111.5d), bean.getBigDecimal());
|
||||
assertEquals(new BigInteger("111111"), bean.getBigInteger());
|
||||
}
|
||||
|
||||
public void testObjectBean() throws Exception {
|
||||
String text = TestUtils.readContent(JSONInterceptorTest.class.getResource("json-7.txt"));
|
||||
Object json = JSONUtil.deserialize(text);
|
||||
assertNotNull(json);
|
||||
assertTrue(json instanceof Map);
|
||||
Map jsonMap = (Map) json;
|
||||
JSONPopulator populator = new JSONPopulator();
|
||||
WrapperClassBean bean = new WrapperClassBean();
|
||||
populator.populateObject(bean, jsonMap);
|
||||
assertEquals(Boolean.TRUE, bean.getBooleanField());
|
||||
assertEquals(true, bean.isPrimitiveBooleanField1());
|
||||
assertEquals(false, bean.isPrimitiveBooleanField2());
|
||||
assertEquals(false, bean.isPrimitiveBooleanField3());
|
||||
assertEquals("test\u000E\u000f", bean.getStringField());
|
||||
assertEquals(new Integer(10), bean.getIntField());
|
||||
assertEquals(0, bean.getNullIntField());
|
||||
assertEquals(new Character('s'), bean.getCharField());
|
||||
assertEquals(10.1d, bean.getDoubleField());
|
||||
assertEquals(new Byte((byte) 3), bean.getByteField());
|
||||
|
||||
assertEquals(2, bean.getListField().size());
|
||||
assertEquals("1", bean.getListField().get(0).getValue());
|
||||
assertEquals("2", bean.getListField().get(1).getValue());
|
||||
|
||||
assertEquals(1, bean.getListMapField().size());
|
||||
assertEquals(2, bean.getListMapField().get(0).size());
|
||||
assertEquals(new Long(2073501), bean.getListMapField().get(0).get("id1"));
|
||||
assertEquals(new Long(3), bean.getListMapField().get(0).get("id2"));
|
||||
|
||||
assertEquals(2, bean.getMapListField().size());
|
||||
assertEquals(3, bean.getMapListField().get("id1").size());
|
||||
assertEquals(new Long(2), bean.getMapListField().get("id1").get(1));
|
||||
assertEquals(4, bean.getMapListField().get("id2").size());
|
||||
assertEquals(new Long(3), bean.getMapListField().get("id2").get(1));
|
||||
|
||||
assertEquals(1, bean.getArrayMapField().length);
|
||||
assertEquals(2, bean.getArrayMapField()[0].size());
|
||||
assertEquals(new Long(2073501), bean.getArrayMapField()[0].get("id1"));
|
||||
assertEquals(new Long(3), bean.getArrayMapField()[0].get("id2"));
|
||||
}
|
||||
|
||||
public void testObjectBeanWithStrings() throws Exception {
|
||||
StringReader stringReader = new StringReader(TestUtils.readContent(JSONInterceptorTest.class
|
||||
.getResource("json-8.txt")));
|
||||
Object json = JSONUtil.deserialize(stringReader);
|
||||
assertNotNull(json);
|
||||
assertTrue(json instanceof Map);
|
||||
Map jsonMap = (Map) json;
|
||||
JSONPopulator populator = new JSONPopulator();
|
||||
WrapperClassBean bean = new WrapperClassBean();
|
||||
populator.populateObject(bean, jsonMap);
|
||||
assertEquals(Boolean.TRUE, bean.getBooleanField());
|
||||
assertEquals("test", bean.getStringField());
|
||||
assertEquals(new Integer(10), bean.getIntField());
|
||||
assertEquals(new Character('s'), bean.getCharField());
|
||||
assertEquals(10.1d, bean.getDoubleField());
|
||||
assertEquals(new Byte((byte) 3), bean.getByteField());
|
||||
|
||||
assertEquals(null, bean.getListField());
|
||||
assertEquals(null, bean.getListMapField());
|
||||
assertEquals(null, bean.getMapListField());
|
||||
assertEquals(null, bean.getArrayMapField());
|
||||
}
|
||||
|
||||
public void testInfiniteLoop() throws JSONException {
|
||||
try {
|
||||
JSONReader reader = new JSONReader();
|
||||
reader.read("[1,\"a]");
|
||||
fail("Should have thrown an exception");
|
||||
} catch (JSONException e) {
|
||||
// I can't get JUnit to ignore the exception
|
||||
// @Test(expected = JSONException.class)
|
||||
}
|
||||
}
|
||||
|
||||
public void testParseBadInput() throws JSONException {
|
||||
try {
|
||||
JSONReader reader = new JSONReader();
|
||||
reader.read("[1,\"a\"1]");
|
||||
fail("Should have thrown an exception");
|
||||
} catch (JSONException e) {
|
||||
// I can't get JUnit to ignore the exception
|
||||
// @Test(expected = JSONException.class)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
+ * $Id$
|
||||
+ *
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.struts2.StrutsStatics;
|
||||
import org.apache.struts2.StrutsTestCase;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.mock.MockActionInvocation;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
/**
|
||||
* JSONResultTest
|
||||
*/
|
||||
public class JSONResultTest extends StrutsTestCase {
|
||||
MockActionInvocation invocation;
|
||||
MockHttpServletResponse response;
|
||||
MockServletContext servletContext;
|
||||
ActionContext context;
|
||||
ValueStack stack;
|
||||
MockHttpServletRequest request;
|
||||
|
||||
public void testJSONUtilNPEOnNullMehtod() {
|
||||
Map map = new HashMap();
|
||||
map.put("createtime", new Date());
|
||||
try {
|
||||
JSONUtil.serialize(map);
|
||||
} catch (JSONException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testJSONWriterEndlessLoopOnExludedProperties() throws JSONException {
|
||||
Pattern all = Pattern.compile(".*");
|
||||
|
||||
JSONWriter writer = new JSONWriter();
|
||||
writer.write(Arrays.asList("a", "b"), Arrays.asList(all), null, false);
|
||||
}
|
||||
|
||||
public void testSMDDisabledSMD() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String smd = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(smd, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-8.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
}
|
||||
|
||||
public void testSMDDefault() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setEnableSMD(true);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String smd = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(smd, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-1.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
public void testSMDDefaultAnnotations() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setEnableSMD(true);
|
||||
SMDActionTest2 action = new SMDActionTest2();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String smd = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(smd, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("smd-2.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
public void testExcludeNullPropeties() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setExcludeNullProperties(true);
|
||||
TestAction action = new TestAction();
|
||||
action.setFoo("fool");
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String smd = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(smd, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("nulls-1.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
}
|
||||
|
||||
public void testWrapPrefix() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setWrapPrefix("_prefix_");
|
||||
TestAction2 action = new TestAction2();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String out = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(out, true);
|
||||
String normalizedExpected = "_prefix_{\"name\":\"name\"}";
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
}
|
||||
|
||||
public void testSuffix() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setWrapSuffix("_suffix_");
|
||||
TestAction2 action = new TestAction2();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String out = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(out, true);
|
||||
String normalizedExpected = "{\"name\":\"name\"}_suffix_";
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
}
|
||||
|
||||
public void testPrefixAndSuffix() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setWrapPrefix("_prefix_");
|
||||
result.setWrapSuffix("_suffix_");
|
||||
TestAction2 action = new TestAction2();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String out = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(out, true);
|
||||
String normalizedExpected = "_prefix_{\"name\":\"name\"}_suffix_";
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
}
|
||||
|
||||
public void testPrefix() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setExcludeNullProperties(true);
|
||||
result.setPrefix(true);
|
||||
TestAction action = new TestAction();
|
||||
action.setFoo("fool");
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String smd = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(smd, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("prefix-1.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void test() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
|
||||
TestAction action = new TestAction();
|
||||
|
||||
// test scape characters
|
||||
action.setArray(new String[] { "a", "a", "\"", "\\", "/", "\b", "\f", "\n", "\r", "\t" });
|
||||
|
||||
List list = new ArrayList();
|
||||
|
||||
list.add("b");
|
||||
list.add(1);
|
||||
list.add(new int[] { 10, 12 });
|
||||
action.setCollection(list);
|
||||
|
||||
// beans
|
||||
List collection2 = new ArrayList();
|
||||
Bean bean1 = new Bean();
|
||||
|
||||
bean1.setBigDecimal(new BigDecimal("111111.111111"));
|
||||
bean1.setBigInteger(new BigInteger("111111111111"));
|
||||
bean1.setStringField("str");
|
||||
bean1.setBooleanField(true);
|
||||
bean1.setCharField('s');
|
||||
bean1.setDoubleField(10.1);
|
||||
bean1.setFloatField(1.5f);
|
||||
bean1.setIntField(10);
|
||||
bean1.setLongField(100);
|
||||
bean1.setEnumField(AnEnum.ValueA);
|
||||
bean1.setEnumBean(AnEnumBean.One);
|
||||
|
||||
Bean bean2 = new Bean();
|
||||
|
||||
bean2.setStringField(" ");
|
||||
bean2.setBooleanField(false);
|
||||
bean2.setFloatField(1.1f);
|
||||
bean2.setDoubleField(2.2);
|
||||
bean2.setEnumField(AnEnum.ValueB);
|
||||
bean2.setEnumBean(AnEnumBean.Two);
|
||||
|
||||
// circular reference
|
||||
bean1.setObjectField(bean2);
|
||||
bean2.setObjectField(bean1);
|
||||
|
||||
collection2.add(bean1);
|
||||
action.setCollection2(collection2);
|
||||
|
||||
// keep order in map
|
||||
Map map = new LinkedHashMap();
|
||||
|
||||
map.put("a", 1);
|
||||
map.put("c", new float[] { 1.0f, 2.0f });
|
||||
action.setMap(map);
|
||||
|
||||
action.setFoo("foo");
|
||||
// should be ignored, marked 'transient'
|
||||
action.setBar("bar");
|
||||
|
||||
// date
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.YEAR, 1999);
|
||||
calendar.set(Calendar.MONTH, Calendar.DECEMBER);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, 31);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 11);
|
||||
calendar.set(Calendar.MINUTE, 59);
|
||||
calendar.set(Calendar.SECOND, 59);
|
||||
action.setDate(calendar.getTime());
|
||||
action.setDate2(calendar.getTime());
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String json = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testHierarchy() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setIgnoreHierarchy(false);
|
||||
|
||||
TestAction3 action = new TestAction3();
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String json = response.getContentAsString();
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-4.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testCommentWrap() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
|
||||
TestAction action = new TestAction();
|
||||
|
||||
// test scape characters
|
||||
action.setArray(new String[] { "a", "a", "\"", "\\", "/", "\b", "\f", "\n", "\r", "\t" });
|
||||
|
||||
List list = new ArrayList();
|
||||
|
||||
list.add("b");
|
||||
list.add(1);
|
||||
list.add(new int[] { 10, 12 });
|
||||
action.setCollection(list);
|
||||
|
||||
// beans
|
||||
List collection2 = new ArrayList();
|
||||
Bean bean1 = new Bean();
|
||||
|
||||
bean1.setStringField("str");
|
||||
bean1.setBooleanField(true);
|
||||
bean1.setCharField('s');
|
||||
bean1.setDoubleField(10.1);
|
||||
bean1.setFloatField(1.5f);
|
||||
bean1.setIntField(10);
|
||||
bean1.setLongField(100);
|
||||
bean1.setEnumField(null);
|
||||
bean1.setEnumBean(null);
|
||||
|
||||
Bean bean2 = new Bean();
|
||||
|
||||
bean2.setStringField(" ");
|
||||
bean2.setBooleanField(false);
|
||||
bean2.setFloatField(1.1f);
|
||||
bean2.setDoubleField(2.2);
|
||||
bean2.setEnumField(AnEnum.ValueC);
|
||||
bean2.setEnumBean(AnEnumBean.Three);
|
||||
|
||||
// circular reference
|
||||
bean1.setObjectField(bean2);
|
||||
bean2.setObjectField(bean1);
|
||||
|
||||
collection2.add(bean1);
|
||||
action.setCollection2(collection2);
|
||||
|
||||
// keep order in map
|
||||
Map map = new LinkedHashMap();
|
||||
|
||||
map.put("a", 1);
|
||||
map.put("c", new float[] { 1.0f, 2.0f });
|
||||
action.setMap(map);
|
||||
|
||||
action.setFoo("foo");
|
||||
// should be ignored, marked 'transient'
|
||||
action.setBar("bar");
|
||||
|
||||
// date
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.YEAR, 1999);
|
||||
calendar.set(Calendar.MONTH, Calendar.DECEMBER);
|
||||
calendar.set(Calendar.DAY_OF_MONTH, 31);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 11);
|
||||
calendar.set(Calendar.MINUTE, 59);
|
||||
calendar.set(Calendar.SECOND, 59);
|
||||
action.setDate(calendar.getTime());
|
||||
action.setDate2(calendar.getTime());
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.setWrapWithComments(true);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String json = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-3.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
private void executeTest2Action(JSONResult result) throws Exception {
|
||||
TestAction action = new TestAction();
|
||||
|
||||
// beans
|
||||
Bean bean1 = new Bean();
|
||||
|
||||
bean1.setStringField("str");
|
||||
bean1.setBooleanField(true);
|
||||
bean1.setCharField('s');
|
||||
bean1.setDoubleField(10.1);
|
||||
bean1.setFloatField(1.5f);
|
||||
bean1.setIntField(10);
|
||||
bean1.setLongField(100);
|
||||
bean1.setEnumField(AnEnum.ValueA);
|
||||
bean1.setEnumBean(AnEnumBean.One);
|
||||
|
||||
// set root
|
||||
action.setBean(bean1);
|
||||
result.setRoot("bean");
|
||||
|
||||
stack.push(action);
|
||||
this.invocation.setStack(stack);
|
||||
this.invocation.setAction(action);
|
||||
|
||||
result.execute(this.invocation);
|
||||
}
|
||||
|
||||
public void test2() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
|
||||
executeTest2Action(result);
|
||||
String json = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-2.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
public void testJSONP() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setCallbackParameter("callback");
|
||||
request.addParameter("callback", "exec");
|
||||
|
||||
executeTest2Action(result);
|
||||
String json = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("jsonp-1.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
public void testNoCache() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setNoCache(true);
|
||||
|
||||
executeTest2Action(result);
|
||||
|
||||
assertEquals("no-cache", response.getHeader("Cache-Control"));
|
||||
assertEquals("0", response.getHeader("Expires"));
|
||||
assertEquals("No-cache", response.getHeader("Pragma"));
|
||||
}
|
||||
|
||||
public void testContentType() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setContentType("some_super_content");
|
||||
|
||||
executeTest2Action(result);
|
||||
|
||||
assertEquals("some_super_content;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
public void testStatusCode() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setStatusCode(HttpServletResponse.SC_CONTINUE);
|
||||
|
||||
executeTest2Action(result);
|
||||
|
||||
assertEquals(HttpServletResponse.SC_CONTINUE, response.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeats test2 but with the Enum serialized as a bean
|
||||
*/
|
||||
public void test2WithEnumBean() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setEnumAsBean(true);
|
||||
|
||||
executeTest2Action(result);
|
||||
|
||||
String json = response.getContentAsString();
|
||||
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-2-enum.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that properties of given root object are read as shallow
|
||||
* (non-recursive) unless specifically included.
|
||||
*/
|
||||
public void testIncludeProperties() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setIncludeProperties("foo");
|
||||
TestAction action = new TestAction();
|
||||
action.setFoo("fooValue");
|
||||
action.setBean(new Bean());
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String json = response.getContentAsString();
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-9.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
public void testIncludePropertiesWithList() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setIncludeProperties("^list\\[\\d+\\]\\.booleanField");
|
||||
TestAction action = new TestAction();
|
||||
|
||||
List list = new ArrayList();
|
||||
|
||||
list.add(new Bean());
|
||||
list.add(new Bean());
|
||||
list.add(new Bean());
|
||||
|
||||
action.setList(list);
|
||||
|
||||
this.invocation.setAction(action);
|
||||
result.execute(this.invocation);
|
||||
|
||||
String json = response.getContentAsString();
|
||||
String normalizedActual = TestUtils.normalize(json, true);
|
||||
String normalizedExpected = TestUtils.normalize(JSONResultTest.class.getResource("json-10.txt"));
|
||||
assertEquals(normalizedExpected, normalizedActual);
|
||||
assertEquals("application/json;charset=ISO-8859-1", response.getContentType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
this.response = new MockHttpServletResponse();
|
||||
this.request = new MockHttpServletRequest();
|
||||
this.request.setRequestURI("http://sumeruri");
|
||||
this.context = ActionContext.getContext();
|
||||
this.context.put(StrutsStatics.HTTP_RESPONSE, this.response);
|
||||
this.context.put(StrutsStatics.HTTP_REQUEST, this.request);
|
||||
this.stack = context.getValueStack();
|
||||
this.servletContext = new MockServletContext();
|
||||
this.context.put(StrutsStatics.SERVLET_CONTEXT, this.servletContext);
|
||||
this.invocation = new MockActionInvocation();
|
||||
this.invocation.setInvocationContext(this.context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class JSONUtilTest extends TestCase {
|
||||
|
||||
/**
|
||||
* Asserts that a bean can be serialized to JSON and restored as a map
|
||||
*/
|
||||
public void testSerializeDeserialize() throws Exception {
|
||||
Bean bean1 = new Bean();
|
||||
|
||||
bean1.setStringField("str");
|
||||
bean1.setBooleanField(true);
|
||||
bean1.setCharField('s');
|
||||
bean1.setDoubleField(10.1);
|
||||
bean1.setFloatField(1.5f);
|
||||
bean1.setIntField(10);
|
||||
bean1.setLongField(100);
|
||||
bean1.setEnumField(AnEnum.ValueA);
|
||||
bean1.setEnumBean(AnEnumBean.Two);
|
||||
|
||||
String json = JSONUtil.serialize(bean1);
|
||||
|
||||
Map result = (Map) JSONUtil.deserialize(json);
|
||||
assertEquals("str", result.get("stringField"));
|
||||
assertEquals(true, result.get("booleanField"));
|
||||
assertEquals("s", result.get("charField")); // note: this is a
|
||||
// String
|
||||
assertEquals(10.1, result.get("doubleField"));
|
||||
assertEquals(1.5, result.get("floatField")); // note: this is a
|
||||
// Double
|
||||
assertEquals(10L, result.get("intField")); // note: this is a
|
||||
// Long
|
||||
assertEquals(AnEnum.ValueA, AnEnum.valueOf((String) result.get("enumField"))); // note:
|
||||
// this
|
||||
// is a
|
||||
// String
|
||||
assertEquals(AnEnumBean.Two, AnEnumBean.valueOf((String) result.get("enumBean"))); // note:
|
||||
// this
|
||||
// is a
|
||||
// String
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
public class OtherBean {
|
||||
private int primitiveInt;
|
||||
|
||||
private Integer intField;
|
||||
private Boolean booleanField;
|
||||
private Character charField;
|
||||
private Long longField;
|
||||
private Float floatField;
|
||||
private Double doubleField;
|
||||
private Byte byteField;
|
||||
|
||||
public Boolean isBooleanField() {
|
||||
return booleanField;
|
||||
}
|
||||
|
||||
public void setBooleanField(Boolean booleanField) {
|
||||
this.booleanField = booleanField;
|
||||
}
|
||||
|
||||
public Byte getByteField() {
|
||||
return byteField;
|
||||
}
|
||||
|
||||
public void setByteField(Byte byteField) {
|
||||
this.byteField = byteField;
|
||||
}
|
||||
|
||||
public Character getCharField() {
|
||||
return charField;
|
||||
}
|
||||
|
||||
public void setCharField(Character charField) {
|
||||
this.charField = charField;
|
||||
}
|
||||
|
||||
public Double getDoubleField() {
|
||||
return doubleField;
|
||||
}
|
||||
|
||||
public void setDoubleField(Double doubleField) {
|
||||
this.doubleField = doubleField;
|
||||
}
|
||||
|
||||
public Float getFloatField() {
|
||||
return floatField;
|
||||
}
|
||||
|
||||
public void setFloatField(Float floatField) {
|
||||
this.floatField = floatField;
|
||||
}
|
||||
|
||||
public Integer getIntField() {
|
||||
return intField;
|
||||
}
|
||||
|
||||
public void setIntField(Integer intField) {
|
||||
this.intField = intField;
|
||||
}
|
||||
|
||||
public Long getLongField() {
|
||||
return longField;
|
||||
}
|
||||
|
||||
public void setLongField(Long longField) {
|
||||
this.longField = longField;
|
||||
}
|
||||
|
||||
public int getPrimitiveInt() {
|
||||
return primitiveInt;
|
||||
}
|
||||
|
||||
public void setPrimitiveInt(int primitiveInt) {
|
||||
this.primitiveInt = primitiveInt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.struts2.json.annotations.SMDMethod;
|
||||
|
||||
public class SMDActionTest1 {
|
||||
private boolean addWasCalled;
|
||||
@SuppressWarnings("unchecked")
|
||||
private List listParam;
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map mapParam;
|
||||
private Bean beanParam;
|
||||
private String stringParam;
|
||||
private int intParam;
|
||||
private boolean booleanParam;
|
||||
private char charParam;
|
||||
private long longParam;
|
||||
private float floatParam;
|
||||
private double doubleParam;
|
||||
private short shortParam;
|
||||
private Object objectParam;
|
||||
private byte byteParam;
|
||||
|
||||
@SMDMethod
|
||||
public void doSomethingPrimitives(String stringParam, int intParam, boolean booleanParam, char charParam,
|
||||
long longParam, float floatParam, double doubleParam, short shortParam, byte byteParam) {
|
||||
this.stringParam = stringParam;
|
||||
this.intParam = intParam;
|
||||
this.booleanParam = booleanParam;
|
||||
this.charParam = charParam;
|
||||
this.longParam = longParam;
|
||||
this.floatParam = floatParam;
|
||||
this.doubleParam = doubleParam;
|
||||
this.byteParam = byteParam;
|
||||
this.shortParam = shortParam;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SMDMethod
|
||||
public void doSomethingObjects(Bean beanParam, Map mapParam, List listParam) {
|
||||
this.beanParam = beanParam;
|
||||
this.mapParam = mapParam;
|
||||
this.listParam = listParam;
|
||||
}
|
||||
|
||||
@SMDMethod
|
||||
public void add(int a, int b) {
|
||||
this.addWasCalled = true;
|
||||
}
|
||||
|
||||
@SMDMethod
|
||||
public void doSomething() {
|
||||
|
||||
}
|
||||
|
||||
public void methodWithoutAnnotation() {
|
||||
|
||||
}
|
||||
|
||||
public boolean isAddWasCalled() {
|
||||
return this.addWasCalled;
|
||||
}
|
||||
|
||||
public void setAddWasCalled(boolean addWasCalled) {
|
||||
this.addWasCalled = addWasCalled;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List getListParam() {
|
||||
return this.listParam;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setListParam(List listParam) {
|
||||
this.listParam = listParam;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map getMapParam() {
|
||||
return this.mapParam;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setMapParam(Map mapParam) {
|
||||
this.mapParam = mapParam;
|
||||
}
|
||||
|
||||
public Bean getBeanParam() {
|
||||
return this.beanParam;
|
||||
}
|
||||
|
||||
public void setBeanParam(Bean beanParam) {
|
||||
this.beanParam = beanParam;
|
||||
}
|
||||
|
||||
public String getStringParam() {
|
||||
return this.stringParam;
|
||||
}
|
||||
|
||||
public void setStringParam(String stringParam) {
|
||||
this.stringParam = stringParam;
|
||||
}
|
||||
|
||||
public int getIntParam() {
|
||||
return this.intParam;
|
||||
}
|
||||
|
||||
public void setIntParam(int intParam) {
|
||||
this.intParam = intParam;
|
||||
}
|
||||
|
||||
public boolean isBooleanParam() {
|
||||
return this.booleanParam;
|
||||
}
|
||||
|
||||
public void setBooleanParam(boolean booleanParam) {
|
||||
this.booleanParam = booleanParam;
|
||||
}
|
||||
|
||||
public char getCharParam() {
|
||||
return this.charParam;
|
||||
}
|
||||
|
||||
public void setCharParam(char charParam) {
|
||||
this.charParam = charParam;
|
||||
}
|
||||
|
||||
public long getLongParam() {
|
||||
return this.longParam;
|
||||
}
|
||||
|
||||
public void setLongParam(long longParam) {
|
||||
this.longParam = longParam;
|
||||
}
|
||||
|
||||
public float getFloatParam() {
|
||||
return this.floatParam;
|
||||
}
|
||||
|
||||
public void setFloatParam(float floatParam) {
|
||||
this.floatParam = floatParam;
|
||||
}
|
||||
|
||||
public double getDoubleParam() {
|
||||
return this.doubleParam;
|
||||
}
|
||||
|
||||
public void setDoubleParam(double doubleParam) {
|
||||
this.doubleParam = doubleParam;
|
||||
}
|
||||
|
||||
public Object getObjectParam() {
|
||||
return this.objectParam;
|
||||
}
|
||||
|
||||
public void setObjectParam(Object objectParam) {
|
||||
this.objectParam = objectParam;
|
||||
}
|
||||
|
||||
public byte getByteParam() {
|
||||
return this.byteParam;
|
||||
}
|
||||
|
||||
public void setByteParam(byte byteParam) {
|
||||
this.byteParam = byteParam;
|
||||
}
|
||||
|
||||
public short getShortParam() {
|
||||
return this.shortParam;
|
||||
}
|
||||
|
||||
public void setShortParam(short shortParam) {
|
||||
this.shortParam = shortParam;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import org.apache.struts2.json.annotations.SMD;
|
||||
import org.apache.struts2.json.annotations.SMDMethod;
|
||||
import org.apache.struts2.json.annotations.SMDMethodParameter;
|
||||
|
||||
@SMD(objectName = "testaction", serviceType = "service", version = "10.0")
|
||||
public class SMDActionTest2 {
|
||||
private boolean doSomethingInvoked;
|
||||
|
||||
@SMDMethod
|
||||
public void add(@SMDMethodParameter(name = "a")
|
||||
int a, @SMDMethodParameter(name = "b")
|
||||
int b) {
|
||||
}
|
||||
|
||||
@SMDMethod(name = "doSomethingElse")
|
||||
public void doSomething() {
|
||||
doSomethingInvoked = true;
|
||||
}
|
||||
|
||||
@SMDMethod
|
||||
public Bean getBean() {
|
||||
Bean bean = new Bean();
|
||||
bean.setStringField("str");
|
||||
bean.setBooleanField(true);
|
||||
bean.setCharField('s');
|
||||
bean.setDoubleField(10.1);
|
||||
bean.setFloatField(1.5f);
|
||||
bean.setIntField(10);
|
||||
bean.setLongField(100);
|
||||
return bean;
|
||||
}
|
||||
|
||||
public boolean isDoSomethingInvoked() {
|
||||
return doSomethingInvoked;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.struts2.json.annotations.SMDMethod;
|
||||
|
||||
/**
|
||||
* Tests that the SMDMethod annotation can be found in interfaces when
|
||||
* ignoreSMDMethodInterface is false
|
||||
*/
|
||||
public class SMDMethodInterfaceTest extends TestCase {
|
||||
|
||||
public interface InterfaceA {
|
||||
String getA();
|
||||
}
|
||||
|
||||
public interface InterfaceB {
|
||||
@SMDMethod
|
||||
String getB();
|
||||
}
|
||||
|
||||
public interface InterfaceC {
|
||||
String getC();
|
||||
}
|
||||
|
||||
public interface InterfaceD {
|
||||
String getD();
|
||||
}
|
||||
|
||||
public interface InterfaceE {
|
||||
String getE();
|
||||
}
|
||||
|
||||
public static class ClassA extends ClassB implements InterfaceA {
|
||||
|
||||
private String a;
|
||||
private String z;
|
||||
|
||||
public ClassA(String a, String b, String c, String d, String e, String x, String y, String z) {
|
||||
super(b, c, d, e, x, y);
|
||||
this.a = a;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public String getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
@SMDMethod
|
||||
public String getZ() {
|
||||
return z;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClassB extends ClassC implements InterfaceB, InterfaceC {
|
||||
|
||||
private String b;
|
||||
private String c;
|
||||
private String y;
|
||||
|
||||
public ClassB(String b, String c, String d, String e, String x, String y) {
|
||||
super(d, e, x);
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public String getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
public String getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
public String getY() {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClassC implements InterfaceD, InterfaceE {
|
||||
|
||||
private String d;
|
||||
private String e;
|
||||
private String x;
|
||||
|
||||
public ClassC(String d, String e, String x) {
|
||||
this.d = d;
|
||||
this.e = e;
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public String getD() {
|
||||
return d;
|
||||
}
|
||||
|
||||
public String getE() {
|
||||
return e;
|
||||
}
|
||||
|
||||
@SMDMethod
|
||||
public String getX() {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the SMDMethod annotation is only detected on the classes
|
||||
* when ignoreSMDMethodInterfaces is true
|
||||
*/
|
||||
public void testBaseClassOnly() {
|
||||
Method[] smdMethodsA = JSONUtil.listSMDMethods(ClassA.class, true);
|
||||
assertEquals(2, smdMethodsA.length);
|
||||
assertEquals("getZ", smdMethodsA[0].getName());
|
||||
assertEquals("getX", smdMethodsA[1].getName());
|
||||
|
||||
Method[] smdMethodsB = JSONUtil.listSMDMethods(ClassB.class, true);
|
||||
assertEquals(1, smdMethodsB.length);
|
||||
assertEquals("getX", smdMethodsB[0].getName());
|
||||
|
||||
Method[] smdMethodsC = JSONUtil.listSMDMethods(ClassC.class, true);
|
||||
assertEquals(1, smdMethodsC.length);
|
||||
assertEquals("getX", smdMethodsC[0].getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the SMDMethod annotation is also detected on the interfaces
|
||||
* and superclasses when ignoreSMDMethodInterfaces is false
|
||||
*/
|
||||
public void testInterfaces() {
|
||||
Method[] smdMethodsA = JSONUtil.listSMDMethods(ClassA.class, false);
|
||||
assertEquals(3, smdMethodsA.length);
|
||||
assertEquals("getZ", smdMethodsA[0].getName());
|
||||
assertEquals("getX", smdMethodsA[1].getName());
|
||||
assertEquals("getB", smdMethodsA[2].getName());
|
||||
|
||||
Method[] smdMethodsB = JSONUtil.listSMDMethods(ClassB.class, false);
|
||||
assertEquals(2, smdMethodsB.length);
|
||||
assertEquals("getX", smdMethodsB[0].getName());
|
||||
assertEquals("getB", smdMethodsB[1].getName());
|
||||
|
||||
Method[] smdMethodsC = JSONUtil.listSMDMethods(ClassC.class, false);
|
||||
assertEquals(1, smdMethodsC.length);
|
||||
assertEquals("getX", smdMethodsC[0].getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the important case: detects the SMDMethod annotation on a proxy
|
||||
*/
|
||||
public void testWithProxy() {
|
||||
|
||||
InvocationHandler handler = new InvocationHandler() {
|
||||
// dummy implementation
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
// proxy is proxy to an impl of ClassA
|
||||
InterfaceA proxy = (InterfaceA) Proxy.newProxyInstance(ClassA.class.getClassLoader(), new Class[] {
|
||||
InterfaceA.class, InterfaceB.class, InterfaceC.class }, handler);
|
||||
|
||||
// first, without the recursion
|
||||
Method[] smdMethodsA = JSONUtil.listSMDMethods(proxy.getClass(), true);
|
||||
assertEquals(0, smdMethodsA.length);
|
||||
|
||||
// now with the recursion
|
||||
Method[] smdMethodsB = JSONUtil.listSMDMethods(proxy.getClass(), false);
|
||||
assertEquals(1, smdMethodsB.length);
|
||||
assertEquals("getB", smdMethodsB[0].getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
public class SimpleValue {
|
||||
|
||||
private String value;
|
||||
|
||||
public SimpleValue() {
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String v) {
|
||||
this.value = v;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.struts2.json.annotations.JSON;
|
||||
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionSupport;
|
||||
|
||||
/**
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class TestAction extends ActionSupport {
|
||||
private static final long serialVersionUID = -8891365561914451494L;
|
||||
private List collection;
|
||||
private List collection2;
|
||||
private Map map;
|
||||
private String foo;
|
||||
private String result;
|
||||
private String[] array;
|
||||
private Bean[] beanArray;
|
||||
private int[] intArray;
|
||||
private List list;
|
||||
private String bar;
|
||||
private String nogetter;
|
||||
private Date date2;
|
||||
private Bean bean;
|
||||
private Date date;
|
||||
private String foo2 = null;
|
||||
|
||||
public Bean getBean() {
|
||||
return this.bean;
|
||||
}
|
||||
|
||||
public void setBean(Bean bean) {
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
public List getCollection() {
|
||||
return this.collection;
|
||||
}
|
||||
|
||||
public void setCollection(List collection) {
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
public List getCollection2() {
|
||||
return this.collection2;
|
||||
}
|
||||
|
||||
public void setCollection2(List collection2) {
|
||||
this.collection2 = collection2;
|
||||
}
|
||||
|
||||
public Map getMap() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
public void setMap(Map map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public String getFoo() {
|
||||
return this.foo;
|
||||
}
|
||||
|
||||
public void setFoo(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
|
||||
public String getResult() {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
public void setResult(String result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public String[] getArray() {
|
||||
return this.array;
|
||||
}
|
||||
|
||||
public void setArray(String[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public List getList() {
|
||||
return this.list;
|
||||
}
|
||||
|
||||
public void setList(List list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute() throws Exception {
|
||||
if (this.result == null) {
|
||||
this.result = Action.SUCCESS;
|
||||
}
|
||||
|
||||
return this.result;
|
||||
}
|
||||
|
||||
public String doInput() throws Exception {
|
||||
return INPUT;
|
||||
}
|
||||
|
||||
public void setBar(String bar) {
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
@JSON(serialize = false)
|
||||
public String getBar() {
|
||||
return this.bar;
|
||||
}
|
||||
|
||||
public void setNogetter(String nogetter) {
|
||||
this.nogetter = nogetter;
|
||||
}
|
||||
|
||||
@JSON(serialize = false)
|
||||
public int[] getIntArray() {
|
||||
return this.intArray;
|
||||
}
|
||||
|
||||
public void setIntArray(int[] intArray) {
|
||||
this.intArray = intArray;
|
||||
}
|
||||
|
||||
@JSON(serialize = false)
|
||||
public Bean[] getBeanArray() {
|
||||
return this.beanArray;
|
||||
}
|
||||
|
||||
public void setBeanArray(Bean[] beanArray) {
|
||||
this.beanArray = beanArray;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return this.date;
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
@JSON(format = "dd/MM/yy")
|
||||
public Date getDate2() {
|
||||
return this.date2;
|
||||
}
|
||||
|
||||
@JSON(format = "dd/MM/yy")
|
||||
public void setDate2(Date date2) {
|
||||
this.date2 = date2;
|
||||
}
|
||||
|
||||
@JSON(serialize = false)
|
||||
public String getFoo2() {
|
||||
return this.foo2;
|
||||
}
|
||||
|
||||
@JSON(deserialize = false)
|
||||
public void setFoo2(String foo2) {
|
||||
this.foo2 = foo2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
public class TestAction2 {
|
||||
public String getName() {
|
||||
return "name";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
public class TestAction3 extends TestAction2 {
|
||||
public String getName2() {
|
||||
return "name";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
public class TestAction4 {
|
||||
private Bean bean;
|
||||
|
||||
public Bean getBean() {
|
||||
if (this.bean == null)
|
||||
this.bean = new Bean();
|
||||
return this.bean;
|
||||
}
|
||||
|
||||
public void setBean(Bean bean) {
|
||||
this.bean = bean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Utility methods for test classes
|
||||
*/
|
||||
public class TestUtils {
|
||||
/**
|
||||
* normalizes a string so that strings generated on different platforms can
|
||||
* be compared. any group of one or more space, tab, \r, and \n characters
|
||||
* are converted to a single space character
|
||||
*
|
||||
* @param obj
|
||||
* the object to be normalized. normalize will perform its
|
||||
* operation on obj.toString().trim() ;
|
||||
* @param appendSpace
|
||||
* @return the normalized string
|
||||
*/
|
||||
public static String normalize(Object obj, boolean appendSpace) {
|
||||
StringTokenizer st = new StringTokenizer(obj.toString().trim(), " \t\r\n");
|
||||
StringBuffer buffer = new StringBuffer(128);
|
||||
|
||||
while (st.hasMoreTokens()) {
|
||||
buffer.append(st.nextToken());
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public static String normalize(URL url) throws Exception {
|
||||
return normalize(readContent(url), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to verify the contents of text against the contents of the URL
|
||||
* specified. Performs a trim on both ends
|
||||
*
|
||||
* @param url
|
||||
* the HTML snippet that we want to validate against
|
||||
* @throws Exception
|
||||
* if the validation failed
|
||||
*/
|
||||
public static boolean compare(URL url, String text) throws Exception {
|
||||
/**
|
||||
* compare the trimmed values of each buffer and make sure they're
|
||||
* equivalent. however, let's make sure to normalize the strings first
|
||||
* to account for line termination differences between platforms.
|
||||
*/
|
||||
String writerString = TestUtils.normalize(text, true);
|
||||
String bufferString = TestUtils.normalize(readContent(url), true);
|
||||
|
||||
return bufferString.equals(writerString);
|
||||
}
|
||||
|
||||
public static String readContent(URL url) throws Exception {
|
||||
if (url == null)
|
||||
throw new Exception("unable to verify a null URL");
|
||||
|
||||
StringBuffer buffer = new StringBuffer(128);
|
||||
InputStream in = url.openStream();
|
||||
byte[] buf = new byte[4096];
|
||||
int nbytes;
|
||||
|
||||
while ((nbytes = in.read(buf)) > 0) {
|
||||
buffer.append(new String(buf, 0, nbytes));
|
||||
}
|
||||
|
||||
in.close();
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.json;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WrapperClassBean {
|
||||
|
||||
private String stringField;
|
||||
private Integer intField;
|
||||
private int nullIntField;
|
||||
private Boolean booleanField;
|
||||
private boolean primitiveBooleanField1;
|
||||
private boolean primitiveBooleanField2;
|
||||
private boolean primitiveBooleanField3;
|
||||
private Character charField;
|
||||
private Long longField;
|
||||
private Float floatField;
|
||||
private Double doubleField;
|
||||
private Object objectField;
|
||||
private Byte byteField;
|
||||
private List<SimpleValue> listField;
|
||||
private List<Map<String, Long>> listMapField;
|
||||
private Map<String, List<Long>> mapListField;
|
||||
private Map<String, Long>[] arrayMapField;
|
||||
|
||||
public List<SimpleValue> getListField() {
|
||||
return listField;
|
||||
}
|
||||
|
||||
public void setListField(List<SimpleValue> listField) {
|
||||
this.listField = listField;
|
||||
}
|
||||
|
||||
public List<Map<String, Long>> getListMapField() {
|
||||
return listMapField;
|
||||
}
|
||||
|
||||
public void setListMapField(List<Map<String, Long>> listMapField) {
|
||||
this.listMapField = listMapField;
|
||||
}
|
||||
|
||||
public Map<String, List<Long>> getMapListField() {
|
||||
return mapListField;
|
||||
}
|
||||
|
||||
public void setMapListField(Map<String, List<Long>> mapListField) {
|
||||
this.mapListField = mapListField;
|
||||
}
|
||||
|
||||
public Map<String, Long>[] getArrayMapField() {
|
||||
return arrayMapField;
|
||||
}
|
||||
|
||||
public void setArrayMapField(Map<String, Long>[] arrayMapField) {
|
||||
this.arrayMapField = arrayMapField;
|
||||
}
|
||||
|
||||
public Boolean getBooleanField() {
|
||||
return booleanField;
|
||||
}
|
||||
|
||||
public void setBooleanField(Boolean booleanField) {
|
||||
this.booleanField = booleanField;
|
||||
}
|
||||
|
||||
public boolean isPrimitiveBooleanField1() {
|
||||
return primitiveBooleanField1;
|
||||
}
|
||||
|
||||
public void setPrimitiveBooleanField1(boolean primitiveBooleanField1) {
|
||||
this.primitiveBooleanField1 = primitiveBooleanField1;
|
||||
}
|
||||
|
||||
public boolean isPrimitiveBooleanField2() {
|
||||
return primitiveBooleanField2;
|
||||
}
|
||||
|
||||
public void setPrimitiveBooleanField2(boolean primitiveBooleanField2) {
|
||||
this.primitiveBooleanField2 = primitiveBooleanField2;
|
||||
}
|
||||
|
||||
public boolean isPrimitiveBooleanField3() {
|
||||
return primitiveBooleanField3;
|
||||
}
|
||||
|
||||
public void setPrimitiveBooleanField3(boolean primitiveBooleanField3) {
|
||||
this.primitiveBooleanField3 = primitiveBooleanField3;
|
||||
}
|
||||
|
||||
public Byte getByteField() {
|
||||
return byteField;
|
||||
}
|
||||
|
||||
public void setByteField(Byte byteField) {
|
||||
this.byteField = byteField;
|
||||
}
|
||||
|
||||
public Character getCharField() {
|
||||
return charField;
|
||||
}
|
||||
|
||||
public void setCharField(Character charField) {
|
||||
this.charField = charField;
|
||||
}
|
||||
|
||||
public Double getDoubleField() {
|
||||
return doubleField;
|
||||
}
|
||||
|
||||
public void setDoubleField(Double doubleField) {
|
||||
this.doubleField = doubleField;
|
||||
}
|
||||
|
||||
public Float getFloatField() {
|
||||
return floatField;
|
||||
}
|
||||
|
||||
public void setFloatField(Float floatField) {
|
||||
this.floatField = floatField;
|
||||
}
|
||||
|
||||
public Integer getIntField() {
|
||||
return intField;
|
||||
}
|
||||
|
||||
public void setIntField(Integer intField) {
|
||||
this.intField = intField;
|
||||
}
|
||||
|
||||
public int getNullIntField() {
|
||||
return nullIntField;
|
||||
}
|
||||
|
||||
public void setNullIntField(int nullIntField) {
|
||||
this.nullIntField = nullIntField;
|
||||
}
|
||||
|
||||
public Long getLongField() {
|
||||
return longField;
|
||||
}
|
||||
|
||||
public void setLongField(Long longField) {
|
||||
this.longField = longField;
|
||||
}
|
||||
|
||||
public Object getObjectField() {
|
||||
return objectField;
|
||||
}
|
||||
|
||||
public void setObjectField(Object objectField) {
|
||||
this.objectField = objectField;
|
||||
}
|
||||
|
||||
public String getStringField() {
|
||||
return stringField;
|
||||
}
|
||||
|
||||
public void setStringField(String stringField) {
|
||||
this.stringField = stringField;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
aField: NaN
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
{
|
||||
aField: 10
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
aField: 10
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
bad 10
|
||||
@@ -0,0 +1 @@
|
||||
{ a: "aaaa }
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"date": "1999-12-31T23:59:59",
|
||||
"date2": "31\/12\/99",
|
||||
"array": ["str0", "str1"],
|
||||
"intArray": [1, 2],
|
||||
"beanArray": [ {
|
||||
"stringField": "bean1"
|
||||
},
|
||||
{
|
||||
"stringField": "bean2"
|
||||
}
|
||||
],
|
||||
"list": ["a", "a", "\"", "\\", "\/", "\b", "\f", "\n", "\r", "\t"],
|
||||
"collection": ["b", 1, [10, 12]],
|
||||
"collection2": [{
|
||||
"booleanField": true,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"floatField": 1.5,
|
||||
"intField": 10,
|
||||
"longField": 100,
|
||||
"objectField": {
|
||||
"booleanField": false,
|
||||
"charField": "\u0000",
|
||||
"doubleField": 2.2,
|
||||
"floatField": 1.1,
|
||||
"intField": 0,
|
||||
"longField":0,
|
||||
"objectField":null,
|
||||
"stringField":" "
|
||||
},
|
||||
"stringField": "str"
|
||||
}],
|
||||
"foo": "foo",
|
||||
"map": {
|
||||
"a": 1,
|
||||
"c": [1.0, 2.0]
|
||||
},
|
||||
"result": null,
|
||||
"bean": {
|
||||
"booleanField": true,
|
||||
"stringField" : "test",
|
||||
"intField" : 10,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"byteField": 3
|
||||
},
|
||||
"foo2" : "ignoreme"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"list":[{"booleanField":false},{"booleanField":false},{"booleanField":false}]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"bigDecimal": null,
|
||||
"bigInteger": null,
|
||||
"booleanField": true,
|
||||
"byteField": 0,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"enumBean": {
|
||||
"propA":"A",
|
||||
"propB":"B",
|
||||
"_name":"One"
|
||||
},
|
||||
"enumField": {
|
||||
"_name":"ValueA"
|
||||
},
|
||||
"floatField": 1.5,
|
||||
"intField": 10,
|
||||
"longField": 100,
|
||||
"objectField": null,
|
||||
"stringField": "str"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"bigDecimal": null,
|
||||
"bigInteger": null,
|
||||
"booleanField": true,
|
||||
"byteField": 0,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"enumBean": "One",
|
||||
"enumField": "ValueA",
|
||||
"floatField": 1.5,
|
||||
"intField": 10,
|
||||
"longField": 100,
|
||||
"objectField": null,
|
||||
"stringField": "str"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* {
|
||||
"array": ["a", "a", "\"", "\\", "\/", "\b", "\f", "\n", "\r", "\t"],
|
||||
"bean": null,
|
||||
"collection": ["b", 1, [10, 12]],
|
||||
"collection2": [{
|
||||
"bigDecimal": null,
|
||||
"bigInteger": null,
|
||||
"booleanField": true,
|
||||
"byteField": 0,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"enumBean": null,
|
||||
"enumField": null,
|
||||
"floatField": 1.5,
|
||||
"intField": 10,
|
||||
"longField": 100,
|
||||
"objectField": {
|
||||
"bigDecimal": null,
|
||||
"bigInteger": null,
|
||||
"booleanField": false,
|
||||
"byteField": 0,
|
||||
"charField": "\u0000",
|
||||
"doubleField": 2.2,
|
||||
"enumBean": "Three",
|
||||
"enumField": "ValueC",
|
||||
"floatField": 1.1,
|
||||
"intField": 0,
|
||||
"longField":0,
|
||||
"objectField":null,
|
||||
"stringField":" "
|
||||
},
|
||||
"stringField": "str"
|
||||
}],
|
||||
"date": "1999-12-31T11:59:59",
|
||||
"date2": "31\/12\/99",
|
||||
"foo": "foo",
|
||||
"list": null,
|
||||
"map": {
|
||||
"a": 1,
|
||||
"c": [1.0, 2.0]
|
||||
},
|
||||
"result":null
|
||||
} */
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name":"name",
|
||||
"name2":"name"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"booleanField": true,
|
||||
"stringField" : "test",
|
||||
"intField" : 10,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"byteField": 3
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"bigDecimal": 111111.5,
|
||||
"bigInteger": 111111,
|
||||
"booleanField": true,
|
||||
"primitiveBooleanField1": true,
|
||||
"primitiveBooleanField2": false,
|
||||
"primitiveBooleanField3": null,
|
||||
"stringField" : "test\u000e\u000F",
|
||||
"intField" : 10,
|
||||
"nullIntField" : null,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"byteField": 3,
|
||||
"objectField": { "empty": "to test issue 28 (http://code.google.com/p/jsonplugin/issues/detail?id=28) the value of objectField should be simply empty curly braces { } " },
|
||||
"enumField": "ValueA",
|
||||
"listField": [{"value":"1"},{"value":"2"}],
|
||||
"listMapField": [{"id1":2073501,"id2":3}],
|
||||
"mapListField": {"id1":[1,2,3],"id2":[4,3,2,1]},
|
||||
"arrayMapField": [{"id1":2073501,"id2":3}]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"booleanField": "true",
|
||||
"stringField" : "test",
|
||||
"intField" : "10",
|
||||
"charField": "s",
|
||||
"doubleField": "10.1",
|
||||
"byteField": "3",
|
||||
"objectField": { "empty": "to test issue 28 (http://code.google.com/p/jsonplugin/issues/detail?id=28) the value of objectField should be simply empty curly braces { } " },
|
||||
"listField": null,
|
||||
"listMapField": null,
|
||||
"mapListField": null,
|
||||
"arrayMapField": null
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"foo":"fooValue"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"array": ["a", "a", "\"", "\\", "\/", "\b", "\f", "\n", "\r", "\t"],
|
||||
"bean": null,
|
||||
"collection": ["b", 1, [10, 12]],
|
||||
"collection2": [{
|
||||
"bigDecimal": 111111.111111,
|
||||
"bigInteger": 111111111111,
|
||||
"booleanField": true,
|
||||
"byteField": 0,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"enumBean": "One",
|
||||
"enumField": "ValueA",
|
||||
"floatField": 1.5,
|
||||
"intField": 10,
|
||||
"longField": 100,
|
||||
"objectField": {
|
||||
"bigDecimal": null,
|
||||
"bigInteger": null,
|
||||
"booleanField": false,
|
||||
"byteField": 0,
|
||||
"charField": "\u0000",
|
||||
"doubleField": 2.2,
|
||||
"enumBean": "Two",
|
||||
"enumField": "ValueB",
|
||||
"floatField": 1.1,
|
||||
"intField": 0,
|
||||
"longField":0,
|
||||
"objectField":null,
|
||||
"stringField":" "
|
||||
},
|
||||
"stringField": "str"
|
||||
}],
|
||||
"date": "1999-12-31T11:59:59",
|
||||
"date2": "31\/12\/99",
|
||||
"foo": "foo",
|
||||
"list": null,
|
||||
"map": {
|
||||
"a": 1,
|
||||
"c": [1.0, 2.0]
|
||||
},
|
||||
"result":null
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
exec({
|
||||
"bigDecimal": null,
|
||||
"bigInteger": null,
|
||||
"booleanField": true,
|
||||
"byteField": 0,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"enumBean": "One",
|
||||
"enumField": "ValueA",
|
||||
"floatField": 1.5,
|
||||
"intField": 10,
|
||||
"longField": 100,
|
||||
"objectField": null,
|
||||
"stringField": "str"
|
||||
})
|
||||
@@ -0,0 +1 @@
|
||||
{"foo":"fool"}
|
||||
@@ -0,0 +1 @@
|
||||
{}&& {"foo":"fool"}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"methods":[
|
||||
{
|
||||
"name":"add",
|
||||
"parameters":[
|
||||
{"name":"p0"},
|
||||
{"name":"p1"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"doSomething",
|
||||
"parameters":[]
|
||||
},
|
||||
{
|
||||
"name":"doSomethingObjects",
|
||||
"parameters":[
|
||||
{"name":"p0"},
|
||||
{"name":"p1"},
|
||||
{"name":"p2"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name":"doSomethingPrimitives",
|
||||
"parameters":[
|
||||
{"name":"p0"},
|
||||
{"name":"p1"},
|
||||
{"name":"p2"},
|
||||
{"name":"p3"},
|
||||
{"name":"p4"},
|
||||
{"name":"p5"},
|
||||
{"name":"p6"},
|
||||
{"name":"p7"},
|
||||
{"name":"p8"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"objectName":null,
|
||||
"serviceType":"JSON-RPC",
|
||||
"serviceUrl":"http:\/\/sumeruri",
|
||||
"version":".1"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"method": "getBean",
|
||||
"id":15
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"error":null,
|
||||
"id":"2",
|
||||
"result":null
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"error":null,
|
||||
"id":"15",
|
||||
"result": {
|
||||
"bigDecimal": null,
|
||||
"bigInteger": null,
|
||||
"booleanField": true,
|
||||
"byteField": 0,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"enumBean": null,
|
||||
"enumField": null,
|
||||
"floatField": 1.5,
|
||||
"intField": 10,
|
||||
"longField": 100,
|
||||
"objectField": null,
|
||||
"stringField": "str"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"error": {
|
||||
"code":100,
|
||||
"message": "'method' is required for JSON RPC",
|
||||
"name":null,
|
||||
"stack":null
|
||||
},
|
||||
"id":"1",
|
||||
"result":null
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"params": [],
|
||||
"method": "doSomething",
|
||||
"id":1
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"params": [],
|
||||
"method": "doSomethingElse",
|
||||
"id":1
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"methods": [
|
||||
{
|
||||
"name": "add",
|
||||
"parameters": [
|
||||
{"name":"a"},
|
||||
{"name":"b"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "doSomethingElse",
|
||||
"parameters":[]
|
||||
},
|
||||
{
|
||||
"name": "getBean",
|
||||
"parameters":[]
|
||||
}
|
||||
],
|
||||
"objectName": "testaction",
|
||||
"serviceType": "service",
|
||||
"serviceUrl":"http:\/\/sumeruri",
|
||||
"version": "10.0"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"params": [0, 0],
|
||||
"method": "add",
|
||||
"id":1
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"params": [0, 0],
|
||||
"id":1
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"params": [0, 0],
|
||||
"method": "zzz",
|
||||
"id":1
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"params": ["string", 1, true, 'c', 2, 3.3, 4.4, 5, 6],
|
||||
"method": "doSomethingPrimitives",
|
||||
"id":"2"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"params": [
|
||||
{
|
||||
"booleanField": true,
|
||||
"stringField" : "test",
|
||||
"intField" : 10,
|
||||
"charField": "s",
|
||||
"doubleField": 10.1,
|
||||
"byteField": 3
|
||||
},
|
||||
{
|
||||
"a": 1,
|
||||
"c": [1.0, 2.0]
|
||||
},
|
||||
["str0", "str1"]
|
||||
],
|
||||
"method": "doSomethingObjects",
|
||||
"id":"2"
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"addWasCalled":false,
|
||||
"beanParam":null,
|
||||
"booleanParam":false,
|
||||
"byteParam":0,
|
||||
"charParam":"\u0000",
|
||||
"doubleParam":0.0,
|
||||
"floatParam":0.0,
|
||||
"intParam":0,
|
||||
"listParam":null,
|
||||
"longParam":0,
|
||||
"mapParam":null,
|
||||
"objectParam":null,
|
||||
"shortParam":0,
|
||||
"stringParam":null
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"params": [0, 0],
|
||||
"method": "methodWithoutAnnotation",
|
||||
"id":1
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
Reference in New Issue
Block a user