mirror of
https://github.com/apache/struts.git
synced 2026-08-31 19:35:40 +00:00
Merge pull request #167 from yasserzamani/WW-4034
WW-4034 Allows to use custom JSONwriter
This commit is contained in:
@@ -0,0 +1,709 @@
|
||||
/*
|
||||
* 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 com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ProxyUtil;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.json.annotations.JSON;
|
||||
import org.apache.struts2.json.annotations.JSONFieldBridge;
|
||||
import org.apache.struts2.json.annotations.JSONParameter;
|
||||
import org.apache.struts2.json.bridge.FieldBridge;
|
||||
import org.apache.struts2.json.bridge.ParameterizedBridge;
|
||||
|
||||
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.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.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Serializes an object into JavaScript Object Notation (JSON). If cyclic
|
||||
* references are detected they will be nulled out.
|
||||
* </p>
|
||||
*/
|
||||
public class DefaultJSONWriter implements JSONWriter {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(DefaultJSONWriter.class);
|
||||
|
||||
private static char[] hex = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
private static final ConcurrentMap<Class<?>, BeanInfo> BEAN_INFO_CACHE_IGNORE_HIERARCHY = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentMap<Class<?>, BeanInfo> BEAN_INFO_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private StringBuilder buf = new StringBuilder();
|
||||
private Stack<Object> 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;
|
||||
private boolean cacheBeanInfo = true;
|
||||
private boolean excludeProxyProperties;
|
||||
|
||||
@Inject(value = JSONConstants.RESULT_EXCLUDE_PROXY_PROPERTIES, required = false)
|
||||
public void setExcludeProxyProperties(String excludeProxyProperties) {
|
||||
setExcludeProxyProperties(Boolean.parseBoolean(excludeProxyProperties));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object Object to be serialized into JSON
|
||||
* @return JSON string for object
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
@Override
|
||||
public String write(Object object) throws JSONException {
|
||||
return this.write(object, null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object
|
||||
* Object to be serialized into JSON
|
||||
* @param excludeProperties
|
||||
* Patterns matching properties to ignore
|
||||
* @param includeProperties
|
||||
* Patterns matching properties to include
|
||||
* @param excludeNullProperties
|
||||
* enable/disable excluding of null properties
|
||||
* @return JSON string for object
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
@Override
|
||||
public String write(Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean excludeNullProperties) throws JSONException {
|
||||
this.excludeNullProperties = excludeNullProperties;
|
||||
this.buf.setLength(0);
|
||||
this.stack.clear();
|
||||
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
|
||||
*
|
||||
* @param object Object to be serialized into JSON
|
||||
* @param method method
|
||||
*
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
protected 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 {
|
||||
LOG.debug("Cyclic reference detected on {}", object);
|
||||
this.add("null");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.process(object, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize object into json
|
||||
*
|
||||
* @param object Object to be serialized into JSON
|
||||
* @param method method
|
||||
*
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
protected 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);
|
||||
} 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 {
|
||||
processCustom(object, method);
|
||||
}
|
||||
|
||||
this.stack.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize custom object into json
|
||||
*
|
||||
* @param object object
|
||||
* @param method method
|
||||
*
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
protected void processCustom(Object object, Method method) throws JSONException {
|
||||
this.bean(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instrospect bean and serialize its properties
|
||||
*
|
||||
* @param object object
|
||||
*
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
protected void bean(Object object) throws JSONException {
|
||||
this.add("{");
|
||||
|
||||
BeanInfo info;
|
||||
|
||||
try {
|
||||
Class clazz = excludeProxyProperties ? ProxyUtil.ultimateTargetClass(object) : object.getClass();
|
||||
|
||||
info = ((object == this.root) && this.ignoreHierarchy)
|
||||
? getBeanInfoIgnoreHierarchy(clazz)
|
||||
: getBeanInfo(clazz);
|
||||
|
||||
PropertyDescriptor[] props = info.getPropertyDescriptors();
|
||||
|
||||
boolean hasData = false;
|
||||
for (PropertyDescriptor prop : props) {
|
||||
String name = prop.getName();
|
||||
Method accessor = prop.getReadMethod();
|
||||
Method baseAccessor = findBaseAccessor(clazz, accessor);
|
||||
|
||||
if (baseAccessor != null) {
|
||||
if (baseAccessor.isAnnotationPresent(JSON.class)) {
|
||||
JSONAnnotationFinder jsonFinder = new JSONAnnotationFinder(baseAccessor).invoke();
|
||||
|
||||
if (!jsonFinder.shouldSerialize()) continue;
|
||||
if (jsonFinder.getName() != null) {
|
||||
name = jsonFinder.getName();
|
||||
}
|
||||
}
|
||||
// ignore "class" and others
|
||||
if (this.shouldExcludeProperty(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);
|
||||
if (baseAccessor.isAnnotationPresent(JSONFieldBridge.class)) {
|
||||
value = getBridgedValue(baseAccessor, value);
|
||||
}
|
||||
|
||||
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("}");
|
||||
}
|
||||
|
||||
protected BeanInfo getBeanInfoIgnoreHierarchy(final Class<?> clazz) throws IntrospectionException {
|
||||
BeanInfo beanInfo = BEAN_INFO_CACHE_IGNORE_HIERARCHY.get(clazz);
|
||||
if (beanInfo != null) {
|
||||
return beanInfo;
|
||||
}
|
||||
beanInfo = Introspector.getBeanInfo(clazz, clazz.getSuperclass());
|
||||
BEAN_INFO_CACHE_IGNORE_HIERARCHY.put(clazz, beanInfo);
|
||||
return beanInfo;
|
||||
}
|
||||
|
||||
protected BeanInfo getBeanInfo(final Class<?> clazz) throws IntrospectionException {
|
||||
BeanInfo beanInfo = BEAN_INFO_CACHE.get(clazz);
|
||||
if (beanInfo != null) {
|
||||
return beanInfo;
|
||||
}
|
||||
beanInfo = Introspector.getBeanInfo(clazz);
|
||||
BEAN_INFO_CACHE.put(clazz, beanInfo);
|
||||
return beanInfo;
|
||||
}
|
||||
|
||||
protected Object getBridgedValue(Method baseAccessor, Object value) throws InstantiationException, IllegalAccessException {
|
||||
JSONFieldBridge fieldBridgeAnn = baseAccessor.getAnnotation(JSONFieldBridge.class);
|
||||
if (fieldBridgeAnn != null) {
|
||||
Class impl = fieldBridgeAnn.impl();
|
||||
FieldBridge instance = (FieldBridge) impl.newInstance();
|
||||
|
||||
if (fieldBridgeAnn.params().length > 0 && ParameterizedBridge.class.isAssignableFrom(impl)) {
|
||||
Map<String, String> params = new HashMap<>(fieldBridgeAnn.params().length);
|
||||
for (JSONParameter param : fieldBridgeAnn.params()) {
|
||||
params.put(param.name(), param.value());
|
||||
}
|
||||
((ParameterizedBridge) instance).setParameterValues(params);
|
||||
}
|
||||
value = instance.objectToString(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected Method findBaseAccessor(Class clazz, Method accessor) {
|
||||
Method baseAccessor = null;
|
||||
if (clazz.getName().contains("$$EnhancerByCGLIB$$")) {
|
||||
try {
|
||||
baseAccessor = Thread.currentThread().getContextClassLoader().loadClass(
|
||||
clazz.getName().substring(0, clazz.getName().indexOf("$$"))).getMethod(
|
||||
accessor.getName(), accessor.getParameterTypes());
|
||||
} catch (Exception ex) {
|
||||
LOG.debug(ex.getMessage(), ex);
|
||||
}
|
||||
} else if (clazz.getName().contains("$$_javassist")) {
|
||||
try {
|
||||
baseAccessor = Class.forName(
|
||||
clazz.getName().substring(0, clazz.getName().indexOf("_$$")))
|
||||
.getMethod(accessor.getName(), accessor.getParameterTypes());
|
||||
} catch (Exception ex) {
|
||||
LOG.debug(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
//in hibernate4.3.7,because javassist3.18.1's class name generate rule is '_$$_jvst'+...
|
||||
} else if(clazz.getName().contains("$$_jvst")){
|
||||
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 {
|
||||
return accessor;
|
||||
}
|
||||
return baseAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instrospect an Enum and serialize it as a name/value pair or as a bean
|
||||
* including all its own properties
|
||||
*
|
||||
* @param enumeration the enum
|
||||
*
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
protected void enumeration(Enum enumeration) throws JSONException {
|
||||
if (enumAsBean) {
|
||||
this.bean(enumeration);
|
||||
} else {
|
||||
this.string(enumeration.name());
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean shouldExcludeProperty(PropertyDescriptor prop) throws SecurityException, NoSuchFieldException {
|
||||
String name = prop.getName();
|
||||
return name.equals("class")
|
||||
|| name.equals("declaringClass")
|
||||
|| name.equals("cachedSuperClass")
|
||||
|| name.equals("metaClass");
|
||||
}
|
||||
|
||||
protected String expandExpr(int i) {
|
||||
return this.exprStack + "[" + i + "]";
|
||||
}
|
||||
|
||||
protected String expandExpr(String property) {
|
||||
if (this.exprStack.length() == 0) {
|
||||
return property;
|
||||
}
|
||||
return this.exprStack + "." + property;
|
||||
}
|
||||
|
||||
protected String setExprStack(String expr) {
|
||||
String s = this.exprStack;
|
||||
this.exprStack = expr;
|
||||
return s;
|
||||
}
|
||||
|
||||
protected 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
|
||||
*/
|
||||
protected boolean add(String name, Object value, Method method, boolean hasData) throws JSONException {
|
||||
if (excludeNullProperties && value == null) {
|
||||
return false;
|
||||
}
|
||||
if (hasData) {
|
||||
this.add(',');
|
||||
}
|
||||
this.add('"');
|
||||
this.add(name);
|
||||
this.add("\":");
|
||||
this.value(value, method);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add map to buffer
|
||||
*/
|
||||
protected 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();
|
||||
if (excludeNullProperties && entry.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object key = entry.getKey();
|
||||
if (key == null) {
|
||||
LOG.error("Cannot build expression for null key in {}", exprStack);
|
||||
continue;
|
||||
}
|
||||
|
||||
String expr = null;
|
||||
if (this.buildExpr) {
|
||||
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)) {
|
||||
if (LOG.isWarnEnabled()) {
|
||||
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
|
||||
*/
|
||||
protected 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
|
||||
*/
|
||||
protected 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
|
||||
*/
|
||||
protected 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
|
||||
*/
|
||||
protected void bool(boolean b) {
|
||||
this.add(b ? "true" : "false");
|
||||
}
|
||||
|
||||
/**
|
||||
* escape characters
|
||||
*
|
||||
* @param obj the object to escape
|
||||
*/
|
||||
protected 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
|
||||
*/
|
||||
protected void add(Object obj) {
|
||||
this.buf.append(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add char to buffer
|
||||
*/
|
||||
protected void add(char c) {
|
||||
this.buf.append(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent as unicode
|
||||
*
|
||||
* @param c character to be encoded
|
||||
*/
|
||||
protected 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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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)
|
||||
*/
|
||||
@Override
|
||||
public void setEnumAsBean(boolean enumAsBean) {
|
||||
this.enumAsBean = enumAsBean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDateFormatter(String defaultDateFormat) {
|
||||
if (defaultDateFormat != null) {
|
||||
this.formatter = new SimpleDateFormat(defaultDateFormat);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCacheBeanInfo(boolean cacheBeanInfo) {
|
||||
this.cacheBeanInfo = cacheBeanInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExcludeProxyProperties(boolean excludeProxyProperties) {
|
||||
this.excludeProxyProperties = excludeProxyProperties;
|
||||
}
|
||||
|
||||
protected static class JSONAnnotationFinder {
|
||||
private boolean serialize = true;
|
||||
private Method accessor;
|
||||
private String name;
|
||||
|
||||
public JSONAnnotationFinder(Method accessor) {
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
public boolean shouldSerialize() {
|
||||
return serialize;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
public JSONAnnotationFinder invoke() {
|
||||
JSON json = accessor.getAnnotation(JSON.class);
|
||||
serialize = json.serialize();
|
||||
if (serialize && json.name().length() > 0) {
|
||||
name = json.name();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,5 +25,6 @@ package org.apache.struts2.json;
|
||||
*/
|
||||
public class JSONConstants {
|
||||
|
||||
public static final String JSON_WRITER = "struts.json.writer";
|
||||
public static final String RESULT_EXCLUDE_PROXY_PROPERTIES = "struts.json.result.excludeProxyProperties";
|
||||
}
|
||||
|
||||
@@ -73,6 +73,13 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
private String jsonContentType = "application/json";
|
||||
private String jsonRpcContentType = "application/json-rpc";
|
||||
|
||||
private JSONUtil jsonUtil;
|
||||
|
||||
@Inject
|
||||
public void setJsonUtil(JSONUtil jsonUtil) {
|
||||
this.jsonUtil = jsonUtil;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
HttpServletRequest request = ServletActionContext.getRequest();
|
||||
@@ -170,7 +177,7 @@ public class JSONInterceptor extends AbstractInterceptor {
|
||||
result = rpcResponse;
|
||||
}
|
||||
|
||||
String json = JSONUtil.serialize(result, excludeProperties, getIncludeProperties(),
|
||||
String json = jsonUtil.serialize(result, excludeProperties, getIncludeProperties(),
|
||||
ignoreHierarchy, excludeNullProperties);
|
||||
json = addCallbackIfApplicable(request, json);
|
||||
boolean writeGzip = enableGZIP && JSONUtil.isGzipInRequest(request);
|
||||
|
||||
@@ -102,6 +102,7 @@ public class JSONResult implements Result {
|
||||
private String wrapPrefix;
|
||||
private String wrapSuffix;
|
||||
private boolean devMode = false;
|
||||
private JSONUtil jsonUtil;
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
|
||||
public void setDefaultEncoding(String val) {
|
||||
@@ -112,7 +113,12 @@ public class JSONResult implements Result {
|
||||
public void setDevMode(String val) {
|
||||
this.devMode = BooleanUtils.toBoolean(val);
|
||||
}
|
||||
|
||||
|
||||
@Inject
|
||||
public void setJsonUtil(JSONUtil jsonUtil) {
|
||||
this.jsonUtil = jsonUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of regular expressions of properties to exclude from the JSON
|
||||
* output.
|
||||
@@ -219,7 +225,7 @@ public class JSONResult implements Result {
|
||||
}
|
||||
|
||||
protected String createJSONString(HttpServletRequest request, Object rootObject) throws JSONException {
|
||||
String json = JSONUtil.serialize(rootObject, excludeProperties, includeProperties, ignoreHierarchy,
|
||||
String json = jsonUtil.serialize(rootObject, excludeProperties, includeProperties, ignoreHierarchy,
|
||||
enumAsBean, excludeNullProperties, defaultDateFormat, cacheBeanInfo);
|
||||
json = addCallbackIfApplicable(request, json);
|
||||
return json;
|
||||
|
||||
@@ -41,6 +41,7 @@ import java.util.zip.GZIPOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -60,11 +61,16 @@ public class JSONUtil {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(JSONUtil.class);
|
||||
|
||||
private static JSONWriter writer = new JSONWriter();
|
||||
private JSONWriter writer;
|
||||
|
||||
public void setWriter(JSONWriter writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public static void setWriter(JSONWriter writer) {
|
||||
JSONUtil.writer = writer;
|
||||
public void setContainer(Container container) {
|
||||
setWriter(container.getInstance(JSONWriter.class, container.getInstance(String.class,
|
||||
JSONConstants.JSON_WRITER)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +83,7 @@ public class JSONUtil {
|
||||
* @return JSON string
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public static String serialize(Object object, boolean cacheBeanInfo) throws JSONException {
|
||||
public String serialize(Object object, boolean cacheBeanInfo) throws JSONException {
|
||||
writer.setCacheBeanInfo(cacheBeanInfo);
|
||||
return writer.write(object);
|
||||
}
|
||||
@@ -100,7 +106,7 @@ public class JSONUtil {
|
||||
* @return JSON string
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public static String serialize(Object object, Collection<Pattern> excludeProperties,
|
||||
public String serialize(Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean excludeNullProperties)
|
||||
throws JSONException {
|
||||
return serialize(object, excludeProperties, includeProperties,
|
||||
@@ -127,7 +133,7 @@ public class JSONUtil {
|
||||
* @return JSON string
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public static String serialize(Object object, Collection<Pattern> excludeProperties,
|
||||
public String serialize(Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean excludeNullProperties,
|
||||
boolean cacheBeanInfo)
|
||||
throws JSONException {
|
||||
@@ -158,7 +164,7 @@ public class JSONUtil {
|
||||
* @return JSON string
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public static String serialize(Object object, Collection<Pattern> excludeProperties,
|
||||
public String serialize(Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean enumAsBean,
|
||||
boolean excludeNullProperties, String defaultDateFormat) throws JSONException {
|
||||
return serialize(object, excludeProperties, includeProperties, ignoreHierarchy, enumAsBean,
|
||||
@@ -189,7 +195,7 @@ public class JSONUtil {
|
||||
* @return JSON string
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public static String serialize(Object object, Collection<Pattern> excludeProperties,
|
||||
public String serialize(Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean ignoreHierarchy, boolean enumAsBean,
|
||||
boolean excludeNullProperties, String defaultDateFormat, boolean cacheBeanInfo) throws JSONException {
|
||||
writer.setIgnoreHierarchy(ignoreHierarchy);
|
||||
@@ -209,7 +215,7 @@ public class JSONUtil {
|
||||
* @throws IOException in case of IO errors
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public static void serialize(Writer writer, Object object) throws IOException, JSONException {
|
||||
public void serialize(Writer writer, Object object) throws IOException, JSONException {
|
||||
serialize(writer, object, CACHE_BEAN_INFO_DEFAULT);
|
||||
}
|
||||
|
||||
@@ -225,7 +231,7 @@ public class JSONUtil {
|
||||
* @throws IOException in case of IO errors
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public static void serialize(Writer writer, Object object, boolean cacheBeanInfo) throws IOException, JSONException {
|
||||
public void serialize(Writer writer, Object object, boolean cacheBeanInfo) throws IOException, JSONException {
|
||||
writer.write(serialize(object, cacheBeanInfo));
|
||||
}
|
||||
|
||||
@@ -247,7 +253,7 @@ public class JSONUtil {
|
||||
* @throws IOException in case of IO errors
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public static void serialize(Writer writer, Object object, Collection<Pattern> excludeProperties,
|
||||
public void serialize(Writer writer, Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean excludeNullProperties) throws IOException,
|
||||
JSONException {
|
||||
serialize(writer, object, excludeProperties, includeProperties, excludeNullProperties, CACHE_BEAN_INFO_DEFAULT);
|
||||
@@ -273,7 +279,7 @@ public class JSONUtil {
|
||||
* @throws IOException in case of IO errors
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public static void serialize(Writer writer, Object object, Collection<Pattern> excludeProperties,
|
||||
public void serialize(Writer writer, Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean excludeNullProperties, boolean cacheBeanInfo)
|
||||
throws IOException, JSONException {
|
||||
writer.write(serialize(object, excludeProperties, includeProperties, true, excludeNullProperties, cacheBeanInfo));
|
||||
|
||||
@@ -1,698 +1,44 @@
|
||||
/*
|
||||
* $Id$
|
||||
* Copyright 2017 The Apache Software Foundation.
|
||||
*
|
||||
* 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
|
||||
* Licensed 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
|
||||
* 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.
|
||||
* 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 com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.ProxyUtil;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.json.annotations.JSON;
|
||||
import org.apache.struts2.json.annotations.JSONFieldBridge;
|
||||
import org.apache.struts2.json.annotations.JSONParameter;
|
||||
import org.apache.struts2.json.bridge.FieldBridge;
|
||||
import org.apache.struts2.json.bridge.ParameterizedBridge;
|
||||
|
||||
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.Method;
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.text.StringCharacterIterator;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.Collection;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Serializes an object into JavaScript Object Notation (JSON). If cyclic
|
||||
* references are detected they will be nulled out.
|
||||
* Should serialize an object into JavaScript Object Notation (JSON). If cyclic
|
||||
* references are detected they should be nulled out.
|
||||
* </p>
|
||||
*/
|
||||
public class JSONWriter {
|
||||
public interface JSONWriter {
|
||||
boolean ENUM_AS_BEAN_DEFAULT = false;
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(JSONWriter.class);
|
||||
String write(Object object) throws JSONException;
|
||||
|
||||
/**
|
||||
* By default, enums are serialised as name=value pairs
|
||||
*/
|
||||
public static final boolean ENUM_AS_BEAN_DEFAULT = false;
|
||||
String write(Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean excludeNullProperties) throws JSONException;
|
||||
|
||||
private static char[] hex = "0123456789ABCDEF".toCharArray();
|
||||
void setIgnoreHierarchy(boolean ignoreHierarchy);
|
||||
|
||||
private static final ConcurrentMap<Class<?>, BeanInfo> BEAN_INFO_CACHE_IGNORE_HIERARCHY = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentMap<Class<?>, BeanInfo> BEAN_INFO_CACHE = new ConcurrentHashMap<>();
|
||||
void setEnumAsBean(boolean enumAsBean);
|
||||
|
||||
private StringBuilder buf = new StringBuilder();
|
||||
private Stack<Object> 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;
|
||||
private boolean cacheBeanInfo = true;
|
||||
private boolean excludeProxyProperties = false;
|
||||
void setDateFormatter(String defaultDateFormat);
|
||||
|
||||
@Inject(value = JSONConstants.RESULT_EXCLUDE_PROXY_PROPERTIES, required = false)
|
||||
public void setExcludeProxyProperties(String excludeProxyProperties) {
|
||||
this.excludeProxyProperties = Boolean.parseBoolean(excludeProxyProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object Object to be serialized into JSON
|
||||
* @return JSON string for object
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public String write(Object object) throws JSONException {
|
||||
return this.write(object, null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object
|
||||
* Object to be serialized into JSON
|
||||
* @param excludeProperties
|
||||
* Patterns matching properties to ignore
|
||||
* @param includeProperties
|
||||
* Patterns matching properties to include
|
||||
* @param excludeNullProperties
|
||||
* enable/disable excluding of null properties
|
||||
* @return JSON string for object
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
public String write(Object object, Collection<Pattern> excludeProperties,
|
||||
Collection<Pattern> includeProperties, boolean excludeNullProperties) throws JSONException {
|
||||
this.excludeNullProperties = excludeNullProperties;
|
||||
this.buf.setLength(0);
|
||||
this.stack.clear();
|
||||
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
|
||||
*
|
||||
* @param object Object to be serialized into JSON
|
||||
* @param method method
|
||||
*
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
protected 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 {
|
||||
LOG.debug("Cyclic reference detected on {}", object);
|
||||
this.add("null");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.process(object, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize object into json
|
||||
*
|
||||
* @param object Object to be serialized into JSON
|
||||
* @param method method
|
||||
*
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
protected 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);
|
||||
} 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 {
|
||||
processCustom(object, method);
|
||||
}
|
||||
|
||||
this.stack.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize custom object into json
|
||||
*
|
||||
* @param object object
|
||||
* @param method method
|
||||
*
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
protected void processCustom(Object object, Method method) throws JSONException {
|
||||
this.bean(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instrospect bean and serialize its properties
|
||||
*
|
||||
* @param object object
|
||||
*
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
protected void bean(Object object) throws JSONException {
|
||||
this.add("{");
|
||||
|
||||
BeanInfo info;
|
||||
|
||||
try {
|
||||
Class clazz = excludeProxyProperties ? ProxyUtil.ultimateTargetClass(object) : object.getClass();
|
||||
|
||||
info = ((object == this.root) && this.ignoreHierarchy)
|
||||
? getBeanInfoIgnoreHierarchy(clazz)
|
||||
: getBeanInfo(clazz);
|
||||
|
||||
PropertyDescriptor[] props = info.getPropertyDescriptors();
|
||||
|
||||
boolean hasData = false;
|
||||
for (PropertyDescriptor prop : props) {
|
||||
String name = prop.getName();
|
||||
Method accessor = prop.getReadMethod();
|
||||
Method baseAccessor = findBaseAccessor(clazz, accessor);
|
||||
|
||||
if (baseAccessor != null) {
|
||||
if (baseAccessor.isAnnotationPresent(JSON.class)) {
|
||||
JSONAnnotationFinder jsonFinder = new JSONAnnotationFinder(baseAccessor).invoke();
|
||||
|
||||
if (!jsonFinder.shouldSerialize()) continue;
|
||||
if (jsonFinder.getName() != null) {
|
||||
name = jsonFinder.getName();
|
||||
}
|
||||
}
|
||||
// ignore "class" and others
|
||||
if (this.shouldExcludeProperty(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);
|
||||
if (baseAccessor.isAnnotationPresent(JSONFieldBridge.class)) {
|
||||
value = getBridgedValue(baseAccessor, value);
|
||||
}
|
||||
|
||||
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("}");
|
||||
}
|
||||
|
||||
protected BeanInfo getBeanInfoIgnoreHierarchy(final Class<?> clazz) throws IntrospectionException {
|
||||
BeanInfo beanInfo = BEAN_INFO_CACHE_IGNORE_HIERARCHY.get(clazz);
|
||||
if (beanInfo != null) {
|
||||
return beanInfo;
|
||||
}
|
||||
beanInfo = Introspector.getBeanInfo(clazz, clazz.getSuperclass());
|
||||
BEAN_INFO_CACHE_IGNORE_HIERARCHY.put(clazz, beanInfo);
|
||||
return beanInfo;
|
||||
}
|
||||
|
||||
protected BeanInfo getBeanInfo(final Class<?> clazz) throws IntrospectionException {
|
||||
BeanInfo beanInfo = BEAN_INFO_CACHE.get(clazz);
|
||||
if (beanInfo != null) {
|
||||
return beanInfo;
|
||||
}
|
||||
beanInfo = Introspector.getBeanInfo(clazz);
|
||||
BEAN_INFO_CACHE.put(clazz, beanInfo);
|
||||
return beanInfo;
|
||||
}
|
||||
|
||||
protected Object getBridgedValue(Method baseAccessor, Object value) throws InstantiationException, IllegalAccessException {
|
||||
JSONFieldBridge fieldBridgeAnn = baseAccessor.getAnnotation(JSONFieldBridge.class);
|
||||
if (fieldBridgeAnn != null) {
|
||||
Class impl = fieldBridgeAnn.impl();
|
||||
FieldBridge instance = (FieldBridge) impl.newInstance();
|
||||
|
||||
if (fieldBridgeAnn.params().length > 0 && ParameterizedBridge.class.isAssignableFrom(impl)) {
|
||||
Map<String, String> params = new HashMap<>(fieldBridgeAnn.params().length);
|
||||
for (JSONParameter param : fieldBridgeAnn.params()) {
|
||||
params.put(param.name(), param.value());
|
||||
}
|
||||
((ParameterizedBridge) instance).setParameterValues(params);
|
||||
}
|
||||
value = instance.objectToString(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected Method findBaseAccessor(Class clazz, Method accessor) {
|
||||
Method baseAccessor = null;
|
||||
if (clazz.getName().contains("$$EnhancerByCGLIB$$")) {
|
||||
try {
|
||||
baseAccessor = Thread.currentThread().getContextClassLoader().loadClass(
|
||||
clazz.getName().substring(0, clazz.getName().indexOf("$$"))).getMethod(
|
||||
accessor.getName(), accessor.getParameterTypes());
|
||||
} catch (Exception ex) {
|
||||
LOG.debug(ex.getMessage(), ex);
|
||||
}
|
||||
} else if (clazz.getName().contains("$$_javassist")) {
|
||||
try {
|
||||
baseAccessor = Class.forName(
|
||||
clazz.getName().substring(0, clazz.getName().indexOf("_$$")))
|
||||
.getMethod(accessor.getName(), accessor.getParameterTypes());
|
||||
} catch (Exception ex) {
|
||||
LOG.debug(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
//in hibernate4.3.7,because javassist3.18.1's class name generate rule is '_$$_jvst'+...
|
||||
} else if(clazz.getName().contains("$$_jvst")){
|
||||
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 {
|
||||
return accessor;
|
||||
}
|
||||
return baseAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instrospect an Enum and serialize it as a name/value pair or as a bean
|
||||
* including all its own properties
|
||||
*
|
||||
* @param enumeration the enum
|
||||
*
|
||||
* @throws JSONException in case of error during serialize
|
||||
*/
|
||||
protected void enumeration(Enum enumeration) throws JSONException {
|
||||
if (enumAsBean) {
|
||||
this.bean(enumeration);
|
||||
} else {
|
||||
this.string(enumeration.name());
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean shouldExcludeProperty(PropertyDescriptor prop) throws SecurityException, NoSuchFieldException {
|
||||
String name = prop.getName();
|
||||
return name.equals("class")
|
||||
|| name.equals("declaringClass")
|
||||
|| name.equals("cachedSuperClass")
|
||||
|| name.equals("metaClass");
|
||||
}
|
||||
|
||||
protected String expandExpr(int i) {
|
||||
return this.exprStack + "[" + i + "]";
|
||||
}
|
||||
|
||||
protected String expandExpr(String property) {
|
||||
if (this.exprStack.length() == 0) {
|
||||
return property;
|
||||
}
|
||||
return this.exprStack + "." + property;
|
||||
}
|
||||
|
||||
protected String setExprStack(String expr) {
|
||||
String s = this.exprStack;
|
||||
this.exprStack = expr;
|
||||
return s;
|
||||
}
|
||||
|
||||
protected 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
|
||||
*/
|
||||
protected boolean add(String name, Object value, Method method, boolean hasData) throws JSONException {
|
||||
if (excludeNullProperties && value == null) {
|
||||
return false;
|
||||
}
|
||||
if (hasData) {
|
||||
this.add(',');
|
||||
}
|
||||
this.add('"');
|
||||
this.add(name);
|
||||
this.add("\":");
|
||||
this.value(value, method);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add map to buffer
|
||||
*/
|
||||
protected 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();
|
||||
if (excludeNullProperties && entry.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object key = entry.getKey();
|
||||
if (key == null) {
|
||||
LOG.error("Cannot build expression for null key in {}", exprStack);
|
||||
continue;
|
||||
}
|
||||
|
||||
String expr = null;
|
||||
if (this.buildExpr) {
|
||||
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)) {
|
||||
if (LOG.isWarnEnabled()) {
|
||||
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
|
||||
*/
|
||||
protected 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
|
||||
*/
|
||||
protected 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
|
||||
*/
|
||||
protected 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
|
||||
*/
|
||||
protected void bool(boolean b) {
|
||||
this.add(b ? "true" : "false");
|
||||
}
|
||||
|
||||
/**
|
||||
* escape characters
|
||||
*
|
||||
* @param obj the object to escape
|
||||
*/
|
||||
protected 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
|
||||
*/
|
||||
protected void add(Object obj) {
|
||||
this.buf.append(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add char to buffer
|
||||
*/
|
||||
protected void add(char c) {
|
||||
this.buf.append(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent as unicode
|
||||
*
|
||||
* @param c character to be encoded
|
||||
*/
|
||||
protected 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;
|
||||
}
|
||||
|
||||
public void setDateFormatter(String defaultDateFormat) {
|
||||
if (defaultDateFormat != null) {
|
||||
this.formatter = new SimpleDateFormat(defaultDateFormat);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCacheBeanInfo(boolean cacheBeanInfo) {
|
||||
this.cacheBeanInfo = cacheBeanInfo;
|
||||
}
|
||||
|
||||
protected static class JSONAnnotationFinder {
|
||||
private boolean serialize = true;
|
||||
private Method accessor;
|
||||
private String name;
|
||||
|
||||
public JSONAnnotationFinder(Method accessor) {
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
public boolean shouldSerialize() {
|
||||
return serialize;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
public JSONAnnotationFinder invoke() {
|
||||
JSON json = accessor.getAnnotation(JSON.class);
|
||||
serialize = json.serialize();
|
||||
if (serialize && json.name().length() > 0) {
|
||||
name = json.name();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
void setCacheBeanInfo(boolean cacheBeanInfo);
|
||||
|
||||
void setExcludeProxyProperties(boolean excludeProxyProperties);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
"http://struts.apache.org/dtds/struts-2.5.dtd">
|
||||
|
||||
<struts>
|
||||
<bean class="org.apache.struts2.json.JSONWriter"/>
|
||||
<bean class="org.apache.struts2.json.JSONUtil" static="true"/>
|
||||
<bean type="org.apache.struts2.json.JSONWriter" name="struts" class="org.apache.struts2.json.DefaultJSONWriter"
|
||||
scope="prototype"/>
|
||||
<constant name="struts.json.writer" value="struts"/>
|
||||
<!-- TODO: Make DefaultJSONWriter thread-safe to remove "prototype"s -->
|
||||
<bean class="org.apache.struts2.json.JSONUtil" scope="prototype"/>
|
||||
|
||||
<package name="json-default" extends="struts-default">
|
||||
|
||||
|
||||
+11
-11
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class JSONWriterTest extends StrutsTestCase{
|
||||
public class DefaultJSONWriterTest extends StrutsTestCase{
|
||||
@Test
|
||||
public void testWrite() throws Exception {
|
||||
Bean bean1=new Bean();
|
||||
@@ -27,10 +27,10 @@ public class JSONWriterTest extends StrutsTestCase{
|
||||
bean1.setEnumField(AnEnum.ValueA);
|
||||
bean1.setEnumBean(AnEnumBean.Two);
|
||||
|
||||
JSONWriter jsonWriter = new JSONWriter();
|
||||
JSONWriter jsonWriter = new DefaultJSONWriter();
|
||||
jsonWriter.setEnumAsBean(false);
|
||||
String json = jsonWriter.write(bean1);
|
||||
TestUtils.assertEquals(JSONWriter.class.getResource("jsonwriter-write-bean-01.txt"), json);
|
||||
TestUtils.assertEquals(DefaultJSONWriter.class.getResource("jsonwriter-write-bean-01.txt"), json);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -52,11 +52,11 @@ public class JSONWriterTest extends StrutsTestCase{
|
||||
m.put("c", "z");
|
||||
bean1.setMap(m);
|
||||
|
||||
JSONWriter jsonWriter = new JSONWriter();
|
||||
JSONWriter jsonWriter = new DefaultJSONWriter();
|
||||
jsonWriter.setEnumAsBean(false);
|
||||
jsonWriter.setIgnoreHierarchy(false);
|
||||
String json = jsonWriter.write(bean1, null, null, true);
|
||||
TestUtils.assertEquals(JSONWriter.class.getResource("jsonwriter-write-bean-03.txt"), json);
|
||||
TestUtils.assertEquals(DefaultJSONWriter.class.getResource("jsonwriter-write-bean-03.txt"), json);
|
||||
}
|
||||
|
||||
private class BeanWithMap extends Bean{
|
||||
@@ -85,11 +85,11 @@ public class JSONWriterTest extends StrutsTestCase{
|
||||
bean1.setEnumBean(AnEnumBean.Two);
|
||||
bean1.setUrl(new URL("http://www.google.com"));
|
||||
|
||||
JSONWriter jsonWriter = new JSONWriter();
|
||||
JSONWriter jsonWriter = new DefaultJSONWriter();
|
||||
jsonWriter.setEnumAsBean(false);
|
||||
jsonWriter.setIgnoreHierarchy(false);
|
||||
String json = jsonWriter.write(bean1);
|
||||
TestUtils.assertEquals(JSONWriter.class.getResource("jsonwriter-write-bean-02.txt"), json);
|
||||
TestUtils.assertEquals(DefaultJSONWriter.class.getResource("jsonwriter-write-bean-02.txt"), json);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,11 +108,11 @@ public class JSONWriterTest extends StrutsTestCase{
|
||||
errors.add("Field is required");
|
||||
bean1.setErrors(errors);
|
||||
|
||||
JSONWriter jsonWriter = new JSONWriter();
|
||||
JSONWriter jsonWriter = new DefaultJSONWriter();
|
||||
jsonWriter.setEnumAsBean(false);
|
||||
jsonWriter.setIgnoreHierarchy(false);
|
||||
String json = jsonWriter.write(bean1);
|
||||
TestUtils.assertEquals(JSONWriter.class.getResource("jsonwriter-write-bean-04.txt"), json);
|
||||
TestUtils.assertEquals(DefaultJSONWriter.class.getResource("jsonwriter-write-bean-04.txt"), json);
|
||||
}
|
||||
|
||||
private class BeanWithList extends Bean {
|
||||
@@ -147,7 +147,7 @@ public class JSONWriterTest extends StrutsTestCase{
|
||||
SingleDateBean dateBean = new SingleDateBean();
|
||||
dateBean.setDate(sdf.parse("2012-12-23 10:10:10 GMT"));
|
||||
|
||||
JSONWriter jsonWriter = new JSONWriter();
|
||||
JSONWriter jsonWriter = new DefaultJSONWriter();
|
||||
jsonWriter.setEnumAsBean(false);
|
||||
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
|
||||
@@ -162,7 +162,7 @@ public class JSONWriterTest extends StrutsTestCase{
|
||||
SingleDateBean dateBean = new SingleDateBean();
|
||||
dateBean.setDate(sdf.parse("2012-12-23 10:10:10 GMT"));
|
||||
|
||||
JSONWriter jsonWriter = new JSONWriter();
|
||||
JSONWriter jsonWriter = new DefaultJSONWriter();
|
||||
jsonWriter.setEnumAsBean(false);
|
||||
jsonWriter.setDateFormatter("MM-dd-yyyy");
|
||||
String json = jsonWriter.write(dateBean);
|
||||
@@ -46,7 +46,7 @@ public class JSONEnumTest extends TestCase {
|
||||
bean1.setEnumField(AnEnum.ValueA);
|
||||
bean1.setEnumBean(AnEnumBean.Two);
|
||||
|
||||
JSONWriter jsonWriter = new JSONWriter();
|
||||
JSONWriter jsonWriter = new DefaultJSONWriter();
|
||||
jsonWriter.setEnumAsBean(false);
|
||||
String json = jsonWriter.write(bean1);
|
||||
|
||||
@@ -88,7 +88,7 @@ public class JSONEnumTest extends TestCase {
|
||||
bean1.setEnumField(AnEnum.ValueA);
|
||||
bean1.setEnumBean(AnEnumBean.Two);
|
||||
|
||||
JSONWriter jsonWriter = new JSONWriter();
|
||||
JSONWriter jsonWriter = new DefaultJSONWriter();
|
||||
jsonWriter.setEnumAsBean(true);
|
||||
String json = jsonWriter.write(bean1);
|
||||
|
||||
|
||||
@@ -95,6 +95,9 @@ public class JSONInterceptorTest extends StrutsTestCase {
|
||||
this.request.addHeader("Content-Type", "application/json-rpc");
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
interceptor.setJsonUtil(jsonUtil);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
@@ -115,6 +118,9 @@ public class JSONInterceptorTest extends StrutsTestCase {
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
interceptor.setJsonUtil(jsonUtil);
|
||||
SMDActionTest2 action = new SMDActionTest2();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
@@ -133,6 +139,9 @@ public class JSONInterceptorTest extends StrutsTestCase {
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
interceptor.setJsonUtil(jsonUtil);
|
||||
SMDActionTest2 action = new SMDActionTest2();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
@@ -151,6 +160,9 @@ public class JSONInterceptorTest extends StrutsTestCase {
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
interceptor.setJsonUtil(jsonUtil);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
@@ -196,6 +208,9 @@ public class JSONInterceptorTest extends StrutsTestCase {
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
interceptor.setJsonUtil(jsonUtil);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
@@ -231,6 +246,9 @@ public class JSONInterceptorTest extends StrutsTestCase {
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
interceptor.setJsonUtil(jsonUtil);
|
||||
SMDActionTest2 action = new SMDActionTest2();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
@@ -256,6 +274,9 @@ public class JSONInterceptorTest extends StrutsTestCase {
|
||||
|
||||
JSONInterceptor interceptor = new JSONInterceptor();
|
||||
interceptor.setEnableSMD(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
interceptor.setJsonUtil(jsonUtil);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
|
||||
this.invocation.setAction(action);
|
||||
|
||||
@@ -62,7 +62,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
Map map = new HashMap();
|
||||
map.put("createtime", new Date());
|
||||
try {
|
||||
JSONUtil.serialize(map, JSONUtil.CACHE_BEAN_INFO_DEFAULT);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
jsonUtil.serialize(map, JSONUtil.CACHE_BEAN_INFO_DEFAULT);
|
||||
} catch (JSONException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
@@ -71,12 +73,15 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testJSONWriterEndlessLoopOnExludedProperties() throws JSONException {
|
||||
Pattern all = Pattern.compile(".*");
|
||||
|
||||
JSONWriter writer = new JSONWriter();
|
||||
JSONWriter writer = new DefaultJSONWriter();
|
||||
writer.write(Arrays.asList("a", "b"), Arrays.asList(all), null, false);
|
||||
}
|
||||
|
||||
public void testSMDDisabledSMD() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
stack.push(action);
|
||||
|
||||
@@ -93,6 +98,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testSMDDefault() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setEnableSMD(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
SMDActionTest1 action = new SMDActionTest1();
|
||||
stack.push(action);
|
||||
|
||||
@@ -110,6 +118,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testSMDDefaultAnnotations() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setEnableSMD(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
SMDActionTest2 action = new SMDActionTest2();
|
||||
stack.push(action);
|
||||
|
||||
@@ -127,6 +138,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testExcludeNullPropeties() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setExcludeNullProperties(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
TestAction action = new TestAction();
|
||||
stack.push(action);
|
||||
action.setFoo("fool");
|
||||
@@ -144,6 +158,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testWrapPrefix() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setWrapPrefix("_prefix_");
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
TestAction2 action = new TestAction2();
|
||||
stack.push(action);
|
||||
|
||||
@@ -160,6 +177,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testSuffix() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setWrapSuffix("_suffix_");
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
TestAction2 action = new TestAction2();
|
||||
stack.push(action);
|
||||
|
||||
@@ -176,6 +196,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testCustomDateFormat() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setDefaultDateFormat("MM-dd-yyyy");
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
|
||||
|
||||
SingleDateBean dateBean = new SingleDateBean();
|
||||
@@ -194,6 +217,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setWrapPrefix("_prefix_");
|
||||
result.setWrapSuffix("_suffix_");
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
TestAction2 action = new TestAction2();
|
||||
stack.push(action);
|
||||
|
||||
@@ -211,6 +237,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setExcludeNullProperties(true);
|
||||
result.setPrefix(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
TestAction action = new TestAction();
|
||||
stack.push(action);
|
||||
action.setFoo("fool");
|
||||
@@ -228,6 +257,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void test() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
|
||||
TestAction action = new TestAction();
|
||||
stack.push(action);
|
||||
@@ -311,6 +343,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testHierarchy() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setIgnoreHierarchy(false);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
|
||||
TestAction3 action = new TestAction3();
|
||||
stack.push(action);
|
||||
@@ -327,6 +362,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testCommentWrap() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
|
||||
TestAction action = new TestAction();
|
||||
stack.push(action);
|
||||
@@ -406,6 +444,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
}
|
||||
|
||||
private void executeTest2Action(JSONResult result) throws Exception {
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
TestAction action = new TestAction();
|
||||
stack.push(action);
|
||||
|
||||
@@ -448,6 +489,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testJSONP() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setCallbackParameter("callback");
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
request.addParameter("callback", "exec");
|
||||
|
||||
executeTest2Action(result);
|
||||
@@ -462,6 +506,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testNoCache() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setNoCache(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
|
||||
executeTest2Action(result);
|
||||
|
||||
@@ -473,6 +520,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testContentType() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setContentType("some_super_content");
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
|
||||
executeTest2Action(result);
|
||||
|
||||
@@ -482,6 +532,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testStatusCode() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setStatusCode(HttpServletResponse.SC_CONTINUE);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
|
||||
executeTest2Action(result);
|
||||
|
||||
@@ -494,6 +547,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void test2WithEnumBean() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setEnumAsBean(true);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
|
||||
executeTest2Action(result);
|
||||
|
||||
@@ -512,6 +568,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testIncludeProperties() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setIncludeProperties("foo");
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
TestAction action = new TestAction();
|
||||
stack.push(action);
|
||||
action.setFoo("fooValue");
|
||||
@@ -529,6 +588,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testIncludePropertiesWithList() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setIncludeProperties("^list\\[\\d+\\]\\.booleanField");
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
TestAction action = new TestAction();
|
||||
stack.push(action);
|
||||
|
||||
@@ -553,6 +615,9 @@ public class JSONResultTest extends StrutsTestCase {
|
||||
public void testIncludePropertiesWithSetList() throws Exception {
|
||||
JSONResult result = new JSONResult();
|
||||
result.setIncludeProperties("^set\\[\\d+\\]\\.list\\[\\d+\\]\\.booleanField");
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
result.setJsonUtil(jsonUtil);
|
||||
TestAction action = new TestAction();
|
||||
stack.push(action);
|
||||
|
||||
|
||||
@@ -45,7 +45,9 @@ public class JSONUtilTest extends TestCase {
|
||||
bean1.setEnumField(AnEnum.ValueA);
|
||||
bean1.setEnumBean(AnEnumBean.Two);
|
||||
|
||||
String json = JSONUtil.serialize(bean1, JSONUtil.CACHE_BEAN_INFO_DEFAULT);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
String json = jsonUtil.serialize(bean1, JSONUtil.CACHE_BEAN_INFO_DEFAULT);
|
||||
|
||||
Map result = (Map) JSONUtil.deserialize(json);
|
||||
assertEquals("str", result.get("stringField"));
|
||||
@@ -72,7 +74,9 @@ public class JSONUtilTest extends TestCase {
|
||||
// This additional 'listOfLists' pattern should be omitted, but not with current version of JSONUtil
|
||||
List<Pattern> includeProperties = JSONUtil.processIncludePatterns(JSONUtil.asSet("listOfLists,listOfLists\\[\\d+\\]\\[\\d+\\]"), JSONUtil.REGEXP_PATTERN);
|
||||
|
||||
String actual = JSONUtil.serialize(bean, null, new ArrayList<Pattern>(includeProperties), false, false);
|
||||
JSONUtil jsonUtil = new JSONUtil();
|
||||
jsonUtil.setWriter(new DefaultJSONWriter());
|
||||
String actual = jsonUtil.serialize(bean, null, new ArrayList<Pattern>(includeProperties), false, false);
|
||||
|
||||
assertEquals("{\"listOfLists\":[[\"1\",\"2\"],[\"3\",\"4\"],[\"5\",\"6\"],[\"7\",\"8\"],[\"9\",\"0\"]]}", actual);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user