Java tutorial
/* * Copyright 2002-2009 the original author or authors. * * 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 * * 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 com.xwtec.xwserver.util.json; import java.beans.PropertyDescriptor; import java.io.IOException; import java.io.Writer; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import net.sf.ezmorph.Morpher; import net.sf.ezmorph.array.ObjectArrayMorpher; import net.sf.ezmorph.bean.BeanMorpher; import net.sf.ezmorph.object.IdentityObjectMorpher; import org.apache.commons.beanutils.DynaBean; import org.apache.commons.beanutils.DynaProperty; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.collections.map.ListOrderedMap; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.xwtec.xwserver.util.json.processors.JsonBeanProcessor; import com.xwtec.xwserver.util.json.processors.JsonValueProcessor; import com.xwtec.xwserver.util.json.processors.JsonVerifier; import com.xwtec.xwserver.util.json.processors.PropertyNameProcessor; import com.xwtec.xwserver.util.json.regexp.RegexpUtils; import com.xwtec.xwserver.util.json.util.CycleDetectionStrategy; import com.xwtec.xwserver.util.json.util.JSONTokener; import com.xwtec.xwserver.util.json.util.JSONUtils; import com.xwtec.xwserver.util.json.util.PropertyFilter; import com.xwtec.xwserver.util.json.util.PropertySetStrategy; /** * A JSONObject is an unordered collection of name/value pairs. Its external * form is a string wrapped in curly braces with colons between the names and * values, and commas between the values and names. The internal form is an * object having <code>get</code> and <code>opt</code> methods for accessing * the values by name, and <code>put</code> methods for adding or replacing * values by name. The values can be any of these types: <code>Boolean</code>, * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>, * <code>String</code>, or the <code>JSONNull</code> object. A JSONObject * constructor can be used to convert an external form JSON text into an * internal form whose values can be retrieved with the <code>get</code> and * <code>opt</code> methods, or to convert values into a JSON text using the * <code>element</code> and <code>toString</code> methods. A * <code>get</code> method returns a value if one can be found, and throws an * exception if one cannot be found. An <code>opt</code> method returns a * default value instead of throwing an exception, and so is useful for * obtaining optional values. * <p> * The generic <code>get()</code> and <code>opt()</code> methods return an * object, which you can cast or query for type. There are also typed * <code>get</code> and <code>opt</code> methods that do type checking and * type coercion for you. * <p> * The <code>put</code> methods adds values to an object. For example, * * <pre> * myString = new JSONObject().put("JSON", "Hello, World!").toString();</pre> * * produces the string <code>{"JSON": "Hello, World"}</code>. * <p> * The texts produced by the <code>toString</code> methods strictly conform to * the JSON syntax rules. The constructors are more forgiving in the texts they * will accept: * <ul> * <li>An extra <code>,</code> <small>(comma)</small> may appear just * before the closing brace.</li> * <li>Strings may be quoted with <code>'</code> <small>(single quote)</small>.</li> * <li>Strings do not need to be quoted at all if they do not begin with a * quote or single quote, and if they do not contain leading or trailing spaces, * and if they do not contain any of these characters: * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and * if they are not the reserved words <code>true</code>, <code>false</code>, * or <code>null</code>.</li> * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as * by <code>:</code>.</li> * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> * as well as by <code>,</code> <small>(comma)</small>.</li> * <li>Numbers may have the <code>0-</code> <small>(octal)</small> or * <code>0x-</code> <small>(hex)</small> prefix.</li> * <li>Comments written in the slashshlash, slashstar, and hash conventions * will be ignored.</li> * </ul> * * @author JSON.org */ @SuppressWarnings({ "unchecked", "serial" }) public final class JSONObject extends AbstractJSON implements JSON, Map, Comparable { private static final Log log = LogFactory.getLog(JSONObject.class); /** * Creates a JSONObject.<br> * Inspects the object type to call the correct JSONObject factory method. * Accepts JSON formatted strings, Maps, DynaBeans and JavaBeans. * * @param object * @throws JSONException if the object can not be converted to a proper * JSONObject. */ public static JSONObject fromObject(Object object) { return fromObject(object, new JsonConfig()); } /** * Creates a JSONObject.<br> * Inspects the object type to call the correct JSONObject factory method. * Accepts JSON formatted strings, Maps, DynaBeans and JavaBeans. * * @param object * @throws JSONException if the object can not be converted to a proper * JSONObject. */ public static JSONObject fromObject(Object object, JsonConfig jsonConfig) { if (object == null || JSONUtils.isNull(object)) { return new JSONObject(true); } else if (object instanceof JSONObject) { return _fromJSONObject((JSONObject) object, jsonConfig); } else if (object instanceof DynaBean) { return _fromDynaBean((DynaBean) object, jsonConfig); } else if (object instanceof JSONTokener) { return _fromJSONTokener((JSONTokener) object, jsonConfig); } else if (object instanceof JSONString) { return _fromJSONString((JSONString) object, jsonConfig); } else if (object instanceof Map) { return _fromMap((Map) object, jsonConfig); } else if (object instanceof String) { return _fromString((String) object, jsonConfig); } else if (JSONUtils.isNumber(object) || JSONUtils.isBoolean(object) || JSONUtils.isString(object)) { return new JSONObject(); } else if (JSONUtils.isArray(object)) { throw new JSONException("'object' is an array. Use JSONArray instead"); } else { return _fromBean(object, jsonConfig); } } /** * Creates a JSONDynaBean from a JSONObject. */ public static Object toBean(JSONObject jsonObject) { if (jsonObject == null || jsonObject.isNullObject()) { return null; } DynaBean dynaBean = null; JsonConfig jsonConfig = new JsonConfig(); Map props = JSONUtils.getProperties(jsonObject); dynaBean = JSONUtils.newDynaBean(jsonObject, jsonConfig); for (Iterator entries = jsonObject.names(jsonConfig).iterator(); entries.hasNext();) { String name = (String) entries.next(); String key = JSONUtils.convertToJavaIdentifier(name, jsonConfig); Class type = (Class) props.get(name); Object value = jsonObject.get(name); try { if (!JSONUtils.isNull(value)) { if (value instanceof JSONArray) { dynaBean.set(key, JSONArray.toCollection((JSONArray) value)); } else if (String.class.isAssignableFrom(type) || Boolean.class.isAssignableFrom(type) || JSONUtils.isNumber(type) || Character.class.isAssignableFrom(type) || JSONFunction.class.isAssignableFrom(type)) { dynaBean.set(key, value); } else { dynaBean.set(key, toBean((JSONObject) value)); } } else { if (type.isPrimitive()) { // assume assigned default value log.warn("Tried to assign null value to " + key + ":" + type.getName()); dynaBean.set(key, JSONUtils.getMorpherRegistry().morph(type, null)); } else { dynaBean.set(key, null); } } } catch (JSONException jsone) { throw jsone; } catch (Exception e) { throw new JSONException("Error while setting property=" + name + " type" + type, e); } } return dynaBean; } /** * Creates a bean from a JSONObject, with a specific target class.<br> */ public static Object toBean(JSONObject jsonObject, Class beanClass) { JsonConfig jsonConfig = new JsonConfig(); jsonConfig.setRootClass(beanClass); return toBean(jsonObject, jsonConfig); } /** * Creates a bean from a JSONObject, with a specific target class.<br> * If beanClass is null, this method will return a graph of DynaBeans. Any * attribute that is a JSONObject and matches a key in the classMap will be * converted to that target class.<br> * The classMap has the following conventions: * <ul> * <li>Every key must be an String.</li> * <li>Every value must be a Class.</li> * <li>A key may be a regular expression.</li> * </ul> */ public static Object toBean(JSONObject jsonObject, Class beanClass, Map classMap) { JsonConfig jsonConfig = new JsonConfig(); jsonConfig.setRootClass(beanClass); jsonConfig.setClassMap(classMap); return toBean(jsonObject, jsonConfig); } /** * Creates a bean from a JSONObject, with the specific configuration. */ public static Object toBean(JSONObject jsonObject, JsonConfig jsonConfig) { if (jsonObject == null || jsonObject.isNullObject()) { return null; } Class beanClass = jsonConfig.getRootClass(); Map classMap = jsonConfig.getClassMap(); if (beanClass == null) { return toBean(jsonObject); } if (classMap == null) { classMap = Collections.EMPTY_MAP; } Object bean = null; try { if (beanClass.isInterface()) { if (!Map.class.isAssignableFrom(beanClass)) { throw new JSONException("beanClass is an interface. " + beanClass); } else { bean = new HashMap(); } } else { bean = jsonConfig.getNewBeanInstanceStrategy().newInstance(beanClass, jsonObject); } } catch (JSONException jsone) { throw jsone; } catch (Exception e) { throw new JSONException(e); } Map props = JSONUtils.getProperties(jsonObject); PropertyFilter javaPropertyFilter = jsonConfig.getJavaPropertyFilter(); for (Iterator entries = jsonObject.names(jsonConfig).iterator(); entries.hasNext();) { String name = (String) entries.next(); Class type = (Class) props.get(name); Object value = jsonObject.get(name); if (javaPropertyFilter != null && javaPropertyFilter.apply(bean, name, value)) { continue; } String key = Map.class.isAssignableFrom(beanClass) && jsonConfig.isSkipJavaIdentifierTransformationInMapKeys() ? name : JSONUtils.convertToJavaIdentifier(name, jsonConfig); PropertyNameProcessor propertyNameProcessor = jsonConfig.findJavaPropertyNameProcessor(beanClass); if (propertyNameProcessor != null) { key = propertyNameProcessor.processPropertyName(beanClass, key); } try { if (Map.class.isAssignableFrom(beanClass)) { // no type info available for conversion if (JSONUtils.isNull(value)) { setProperty(bean, key, value, jsonConfig); } else if (value instanceof JSONArray) { setProperty(bean, key, convertPropertyValueToCollection(key, value, jsonConfig, name, classMap, List.class), jsonConfig); } else if (String.class.isAssignableFrom(type) || JSONUtils.isBoolean(type) || JSONUtils.isNumber(type) || JSONUtils.isString(type) || JSONFunction.class.isAssignableFrom(type)) { if (jsonConfig.isHandleJettisonEmptyElement() && "".equals(value)) { setProperty(bean, key, null, jsonConfig); } else { setProperty(bean, key, value, jsonConfig); } } else { Class targetClass = findTargetClass(key, classMap); targetClass = targetClass == null ? findTargetClass(name, classMap) : targetClass; JsonConfig jsc = jsonConfig.copy(); jsc.setRootClass(targetClass); jsc.setClassMap(classMap); if (targetClass != null) { setProperty(bean, key, toBean((JSONObject) value, jsc), jsonConfig); } else { setProperty(bean, key, toBean((JSONObject) value), jsonConfig); } } } else { PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor(bean, key); if (pd != null && pd.getWriteMethod() == null) { log.info("Property '" + key + "' of " + bean.getClass() + " has no write method. SKIPPED."); continue; } if (pd != null) { Class targetType = pd.getPropertyType(); if (!JSONUtils.isNull(value)) { if (value instanceof JSONArray) { if (List.class.isAssignableFrom(pd.getPropertyType())) { setProperty(bean, key, convertPropertyValueToCollection(key, value, jsonConfig, name, classMap, pd.getPropertyType()), jsonConfig); } else if (Set.class.isAssignableFrom(pd.getPropertyType())) { setProperty(bean, key, convertPropertyValueToCollection(key, value, jsonConfig, name, classMap, pd.getPropertyType()), jsonConfig); } else { setProperty(bean, key, convertPropertyValueToArray(key, value, targetType, jsonConfig, classMap), jsonConfig); } } else if (String.class.isAssignableFrom(type) || JSONUtils.isBoolean(type) || JSONUtils.isNumber(type) || JSONUtils.isString(type) || JSONFunction.class.isAssignableFrom(type)) { if (pd != null) { if (jsonConfig.isHandleJettisonEmptyElement() && "".equals(value)) { setProperty(bean, key, null, jsonConfig); } else if (!targetType.isInstance(value)) { setProperty(bean, key, morphPropertyValue(key, value, type, targetType), jsonConfig); } else { setProperty(bean, key, value, jsonConfig); } } else if (beanClass == null || bean instanceof Map) { setProperty(bean, key, value, jsonConfig); } else { log.warn("Tried to assign property " + key + ":" + type.getName() + " to bean of class " + bean.getClass().getName()); } } else { if (jsonConfig.isHandleJettisonSingleElementArray()) { JSONArray array = new JSONArray().element(value, jsonConfig); Class newTargetClass = findTargetClass(key, classMap); newTargetClass = newTargetClass == null ? findTargetClass(name, classMap) : newTargetClass; JsonConfig jsc = jsonConfig.copy(); jsc.setRootClass(newTargetClass); jsc.setClassMap(classMap); if (targetType.isArray()) { setProperty(bean, key, JSONArray.toArray(array, jsc), jsonConfig); } else if (JSONArray.class.isAssignableFrom(targetType)) { setProperty(bean, key, array, jsonConfig); } else if (List.class.isAssignableFrom(targetType) || Set.class.isAssignableFrom(targetType)) { jsc.setCollectionType(targetType); setProperty(bean, key, JSONArray.toCollection(array, jsc), jsonConfig); } else { setProperty(bean, key, toBean((JSONObject) value, jsc), jsonConfig); } } else { if (targetType == Object.class || targetType.isInterface()) { Class targetTypeCopy = targetType; targetType = findTargetClass(key, classMap); targetType = targetType == null ? findTargetClass(name, classMap) : targetType; targetType = targetType == null && targetTypeCopy.isInterface() ? targetTypeCopy : targetType; } JsonConfig jsc = jsonConfig.copy(); jsc.setRootClass(targetType); jsc.setClassMap(classMap); setProperty(bean, key, toBean((JSONObject) value, jsc), jsonConfig); } } } else { if (type.isPrimitive()) { // assume assigned default value log.warn("Tried to assign null value to " + key + ":" + type.getName()); setProperty(bean, key, JSONUtils.getMorpherRegistry().morph(type, null), jsonConfig); } else { setProperty(bean, key, null, jsonConfig); } } } else { // pd is null if (!JSONUtils.isNull(value)) { if (value instanceof JSONArray) { setProperty(bean, key, convertPropertyValueToCollection(key, value, jsonConfig, name, classMap, List.class), jsonConfig); } else if (String.class.isAssignableFrom(type) || JSONUtils.isBoolean(type) || JSONUtils.isNumber(type) || JSONUtils.isString(type) || JSONFunction.class.isAssignableFrom(type)) { if (beanClass == null || bean instanceof Map || jsonConfig.getPropertySetStrategy() != null || !jsonConfig.isIgnorePublicFields()) { setProperty(bean, key, value, jsonConfig); } else { log.warn("Tried to assign property " + key + ":" + type.getName() + " to bean of class " + bean.getClass().getName()); } } else { if (jsonConfig.isHandleJettisonSingleElementArray()) { Class newTargetClass = findTargetClass(key, classMap); newTargetClass = newTargetClass == null ? findTargetClass(name, classMap) : newTargetClass; JsonConfig jsc = jsonConfig.copy(); jsc.setRootClass(newTargetClass); jsc.setClassMap(classMap); setProperty(bean, key, toBean((JSONObject) value, jsc), jsonConfig); } else { setProperty(bean, key, value, jsonConfig); } } } else { if (type.isPrimitive()) { // assume assigned default value log.warn("Tried to assign null value to " + key + ":" + type.getName()); setProperty(bean, key, JSONUtils.getMorpherRegistry().morph(type, null), jsonConfig); } else { setProperty(bean, key, null, jsonConfig); } } } } } catch (JSONException jsone) { throw jsone; } catch (Exception e) { throw new JSONException("Error while setting property=" + name + " type " + type, e); } } return bean; } /** * Creates a bean from a JSONObject, with the specific configuration. */ public static Object toBean(JSONObject jsonObject, Object root, JsonConfig jsonConfig) { if (jsonObject == null || jsonObject.isNullObject() || root == null) { return root; } Class rootClass = root.getClass(); if (rootClass.isInterface()) { throw new JSONException("Root bean is an interface. " + rootClass); } Map classMap = jsonConfig.getClassMap(); if (classMap == null) { classMap = Collections.EMPTY_MAP; } Map props = JSONUtils.getProperties(jsonObject); PropertyFilter javaPropertyFilter = jsonConfig.getJavaPropertyFilter(); for (Iterator entries = jsonObject.names(jsonConfig).iterator(); entries.hasNext();) { String name = (String) entries.next(); Class type = (Class) props.get(name); Object value = jsonObject.get(name); if (javaPropertyFilter != null && javaPropertyFilter.apply(root, name, value)) { continue; } String key = JSONUtils.convertToJavaIdentifier(name, jsonConfig); try { PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor(root, key); if (pd != null && pd.getWriteMethod() == null) { log.info("Property '" + key + "' of " + root.getClass() + " has no write method. SKIPPED."); continue; } if (!JSONUtils.isNull(value)) { if (value instanceof JSONArray) { if (pd == null || List.class.isAssignableFrom(pd.getPropertyType())) { Class targetClass = findTargetClass(key, classMap); targetClass = targetClass == null ? findTargetClass(name, classMap) : targetClass; Object newRoot = jsonConfig.getNewBeanInstanceStrategy().newInstance(targetClass, null); List list = JSONArray.toList((JSONArray) value, newRoot, jsonConfig); setProperty(root, key, list, jsonConfig); } else { Class innerType = JSONUtils.getInnerComponentType(pd.getPropertyType()); Class targetInnerType = findTargetClass(key, classMap); if (innerType.equals(Object.class) && targetInnerType != null && !targetInnerType.equals(Object.class)) { innerType = targetInnerType; } Object newRoot = jsonConfig.getNewBeanInstanceStrategy().newInstance(innerType, null); Object array = JSONArray.toArray((JSONArray) value, newRoot, jsonConfig); if (innerType.isPrimitive() || JSONUtils.isNumber(innerType) || Boolean.class.isAssignableFrom(innerType) || JSONUtils.isString(innerType)) { array = JSONUtils.getMorpherRegistry() .morph(Array.newInstance(innerType, 0).getClass(), array); } else if (!array.getClass().equals(pd.getPropertyType())) { if (!pd.getPropertyType().equals(Object.class)) { Morpher morpher = JSONUtils.getMorpherRegistry() .getMorpherFor(Array.newInstance(innerType, 0).getClass()); if (IdentityObjectMorpher.getInstance().equals(morpher)) { ObjectArrayMorpher beanMorpher = new ObjectArrayMorpher( new BeanMorpher(innerType, JSONUtils.getMorpherRegistry())); JSONUtils.getMorpherRegistry().registerMorpher(beanMorpher); } array = JSONUtils.getMorpherRegistry() .morph(Array.newInstance(innerType, 0).getClass(), array); } } setProperty(root, key, array, jsonConfig); } } else if (String.class.isAssignableFrom(type) || JSONUtils.isBoolean(type) || JSONUtils.isNumber(type) || JSONUtils.isString(type) || JSONFunction.class.isAssignableFrom(type)) { if (pd != null) { if (jsonConfig.isHandleJettisonEmptyElement() && "".equals(value)) { setProperty(root, key, null, jsonConfig); } else if (!pd.getPropertyType().isInstance(value)) { Morpher morpher = JSONUtils.getMorpherRegistry() .getMorpherFor(pd.getPropertyType()); if (IdentityObjectMorpher.getInstance().equals(morpher)) { log.warn("Can't transform property '" + key + "' from " + type.getName() + " into " + pd.getPropertyType().getName() + ". Will register a default BeanMorpher"); JSONUtils.getMorpherRegistry().registerMorpher( new BeanMorpher(pd.getPropertyType(), JSONUtils.getMorpherRegistry())); } setProperty(root, key, JSONUtils.getMorpherRegistry().morph(pd.getPropertyType(), value), jsonConfig); } else { setProperty(root, key, value, jsonConfig); } } else if (root instanceof Map) { setProperty(root, key, value, jsonConfig); } else { log.warn("Tried to assign property " + key + ":" + type.getName() + " to bean of class " + root.getClass().getName()); } } else { if (pd != null) { Class targetClass = pd.getPropertyType(); if (jsonConfig.isHandleJettisonSingleElementArray()) { JSONArray array = new JSONArray().element(value, jsonConfig); Class newTargetClass = findTargetClass(key, classMap); newTargetClass = newTargetClass == null ? findTargetClass(name, classMap) : newTargetClass; Object newRoot = jsonConfig.getNewBeanInstanceStrategy().newInstance(newTargetClass, null); if (targetClass.isArray()) { setProperty(root, key, JSONArray.toArray(array, newRoot, jsonConfig), jsonConfig); } else if (Collection.class.isAssignableFrom(targetClass)) { setProperty(root, key, JSONArray.toList(array, newRoot, jsonConfig), jsonConfig); } else if (JSONArray.class.isAssignableFrom(targetClass)) { setProperty(root, key, array, jsonConfig); } else { setProperty(root, key, toBean((JSONObject) value, newRoot, jsonConfig), jsonConfig); } } else { if (targetClass == Object.class) { targetClass = findTargetClass(key, classMap); targetClass = targetClass == null ? findTargetClass(name, classMap) : targetClass; } Object newRoot = jsonConfig.getNewBeanInstanceStrategy().newInstance(targetClass, null); setProperty(root, key, toBean((JSONObject) value, newRoot, jsonConfig), jsonConfig); } } else if (root instanceof Map) { Class targetClass = findTargetClass(key, classMap); targetClass = targetClass == null ? findTargetClass(name, classMap) : targetClass; Object newRoot = jsonConfig.getNewBeanInstanceStrategy().newInstance(targetClass, null); setProperty(root, key, toBean((JSONObject) value, newRoot, jsonConfig), jsonConfig); } else { log.warn("Tried to assign property " + key + ":" + type.getName() + " to bean of class " + rootClass.getName()); } } } else { if (type.isPrimitive()) { // assume assigned default value log.warn("Tried to assign null value to " + key + ":" + type.getName()); setProperty(root, key, JSONUtils.getMorpherRegistry().morph(type, null), jsonConfig); } else { setProperty(root, key, null, jsonConfig); } } } catch (JSONException jsone) { throw jsone; } catch (Exception e) { throw new JSONException("Error while setting property=" + name + " type " + type, e); } } return root; } /** * Creates a JSONObject from a POJO.<br> * Supports nested maps, POJOs, and arrays/collections. * * @param bean An object with POJO conventions * @throws JSONException if the bean can not be converted to a proper * JSONObject. */ private static JSONObject _fromBean(Object bean, JsonConfig jsonConfig) { if (!addInstance(bean)) { try { return jsonConfig.getCycleDetectionStrategy().handleRepeatedReferenceAsObject(bean); } catch (JSONException jsone) { removeInstance(bean); fireErrorEvent(jsone, jsonConfig); throw jsone; } catch (RuntimeException e) { removeInstance(bean); JSONException jsone = new JSONException(e); fireErrorEvent(jsone, jsonConfig); throw jsone; } } fireObjectStartEvent(jsonConfig); JsonBeanProcessor processor = jsonConfig.findJsonBeanProcessor(bean.getClass()); if (processor != null) { JSONObject json = null; try { json = processor.processBean(bean, jsonConfig); if (json == null) { json = (JSONObject) jsonConfig.findDefaultValueProcessor(bean.getClass()) .getDefaultValue(bean.getClass()); if (json == null) { json = new JSONObject(true); } } removeInstance(bean); fireObjectEndEvent(jsonConfig); } catch (JSONException jsone) { removeInstance(bean); fireErrorEvent(jsone, jsonConfig); throw jsone; } catch (RuntimeException e) { removeInstance(bean); JSONException jsone = new JSONException(e); fireErrorEvent(jsone, jsonConfig); throw jsone; } return json; } Class beanClass = bean.getClass(); PropertyNameProcessor propertyNameProcessor = jsonConfig.findJsonPropertyNameProcessor(beanClass); Collection exclusions = jsonConfig.getMergedExcludes(beanClass); JSONObject jsonObject = new JSONObject(); try { PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(bean); PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter(); for (int i = 0; i < pds.length; i++) { boolean bypass = false; String key = pds[i].getName(); if (exclusions.contains(key)) { continue; } if (jsonConfig.isIgnoreTransientFields() && isTransientField(key, beanClass)) { continue; } Class type = pds[i].getPropertyType(); try { pds[i].getReadMethod(); } catch (Exception e) { // bug 2565295 String warning = "Property '" + key + "' of " + beanClass + " has no read method. SKIPPED"; fireWarnEvent(warning, jsonConfig); log.info(warning); continue; } if (pds[i].getReadMethod() != null) { Object value = PropertyUtils.getProperty(bean, key); if (jsonPropertyFilter != null && jsonPropertyFilter.apply(bean, key, value)) { continue; } JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(beanClass, type, key); if (jsonValueProcessor != null) { value = jsonValueProcessor.processObjectValue(key, value, jsonConfig); bypass = true; if (!JsonVerifier.isValidJsonValue(value)) { throw new JSONException("Value is not a valid JSON value. " + value); } } if (propertyNameProcessor != null) { key = propertyNameProcessor.processPropertyName(beanClass, key); } setValue(jsonObject, key, value, type, jsonConfig, bypass); } else { String warning = "Property '" + key + "' of " + beanClass + " has no read method. SKIPPED"; fireWarnEvent(warning, jsonConfig); log.info(warning); } } // inspect public fields, this operation may fail under // a SecurityManager so we will eat all exceptions try { if (!jsonConfig.isIgnorePublicFields()) { Field[] fields = beanClass.getFields(); for (int i = 0; i < fields.length; i++) { boolean bypass = false; Field field = fields[i]; String key = field.getName(); if (exclusions.contains(key)) { continue; } if (jsonConfig.isIgnoreTransientFields() && isTransientField(field)) { continue; } Class type = field.getType(); Object value = field.get(bean); if (jsonPropertyFilter != null && jsonPropertyFilter.apply(bean, key, value)) { continue; } JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(beanClass, type, key); if (jsonValueProcessor != null) { value = jsonValueProcessor.processObjectValue(key, value, jsonConfig); bypass = true; if (!JsonVerifier.isValidJsonValue(value)) { throw new JSONException("Value is not a valid JSON value. " + value); } } if (propertyNameProcessor != null) { key = propertyNameProcessor.processPropertyName(beanClass, key); } setValue(jsonObject, key, value, type, jsonConfig, bypass); } } } catch (Exception e) { log.trace("Couldn't read public fields.", e); } } catch (JSONException jsone) { removeInstance(bean); fireErrorEvent(jsone, jsonConfig); throw jsone; } catch (Exception e) { removeInstance(bean); JSONException jsone = new JSONException(e); fireErrorEvent(jsone, jsonConfig); throw jsone; } removeInstance(bean); fireObjectEndEvent(jsonConfig); return jsonObject; } private static JSONObject _fromDynaBean(DynaBean bean, JsonConfig jsonConfig) { if (bean == null) { fireObjectStartEvent(jsonConfig); fireObjectEndEvent(jsonConfig); return new JSONObject(true); } if (!addInstance(bean)) { try { return jsonConfig.getCycleDetectionStrategy().handleRepeatedReferenceAsObject(bean); } catch (JSONException jsone) { removeInstance(bean); fireErrorEvent(jsone, jsonConfig); throw jsone; } catch (RuntimeException e) { removeInstance(bean); JSONException jsone = new JSONException(e); fireErrorEvent(jsone, jsonConfig); throw jsone; } } fireObjectStartEvent(jsonConfig); JSONObject jsonObject = new JSONObject(); try { DynaProperty[] props = bean.getDynaClass().getDynaProperties(); Collection exclusions = jsonConfig.getMergedExcludes(); PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter(); for (int i = 0; i < props.length; i++) { boolean bypass = false; DynaProperty dynaProperty = props[i]; String key = dynaProperty.getName(); if (exclusions.contains(key)) { continue; } Class type = dynaProperty.getType(); Object value = bean.get(dynaProperty.getName()); if (jsonPropertyFilter != null && jsonPropertyFilter.apply(bean, key, value)) { continue; } JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(type, key); if (jsonValueProcessor != null) { value = jsonValueProcessor.processObjectValue(key, value, jsonConfig); bypass = true; if (!JsonVerifier.isValidJsonValue(value)) { throw new JSONException("Value is not a valid JSON value. " + value); } } setValue(jsonObject, key, value, type, jsonConfig, bypass); } } catch (JSONException jsone) { removeInstance(bean); fireErrorEvent(jsone, jsonConfig); throw jsone; } catch (RuntimeException e) { removeInstance(bean); JSONException jsone = new JSONException(e); fireErrorEvent(jsone, jsonConfig); throw jsone; } removeInstance(bean); fireObjectEndEvent(jsonConfig); return jsonObject; } private static JSONObject _fromJSONObject(JSONObject object, JsonConfig jsonConfig) { if (object == null || object.isNullObject()) { fireObjectStartEvent(jsonConfig); fireObjectEndEvent(jsonConfig); return new JSONObject(true); } if (!addInstance(object)) { try { return jsonConfig.getCycleDetectionStrategy().handleRepeatedReferenceAsObject(object); } catch (JSONException jsone) { removeInstance(object); fireErrorEvent(jsone, jsonConfig); throw jsone; } catch (RuntimeException e) { removeInstance(object); JSONException jsone = new JSONException(e); fireErrorEvent(jsone, jsonConfig); throw jsone; } } fireObjectStartEvent(jsonConfig); JSONArray sa = object.names(jsonConfig); Collection exclusions = jsonConfig.getMergedExcludes(); JSONObject jsonObject = new JSONObject(); PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter(); for (Iterator i = sa.iterator(); i.hasNext();) { Object k = i.next(); if (k == null) { throw new JSONException("JSON keys cannot be null."); } if (!(k instanceof String) && !jsonConfig.isAllowNonStringKeys()) { throw new ClassCastException("JSON keys must be strings."); } String key = String.valueOf(k); if ("null".equals(key)) { throw new NullPointerException("JSON keys must not be null nor the 'null' string."); } if (exclusions.contains(key)) { continue; } Object value = object.opt(key); if (jsonPropertyFilter != null && jsonPropertyFilter.apply(object, key, value)) { continue; } if (jsonObject.properties.containsKey(key)) { jsonObject.accumulate(key, value, jsonConfig); firePropertySetEvent(key, value, true, jsonConfig); } else { jsonObject.setInternal(key, value, jsonConfig); firePropertySetEvent(key, value, false, jsonConfig); } } removeInstance(object); fireObjectEndEvent(jsonConfig); return jsonObject; } private static JSONObject _fromJSONString(JSONString string, JsonConfig jsonConfig) { return _fromJSONTokener(new JSONTokener(string.toJSONString()), jsonConfig); } private static JSONObject _fromJSONTokener(JSONTokener tokener, JsonConfig jsonConfig) { try { char c; String key; Object value; if (tokener.matches("null.*")) { fireObjectStartEvent(jsonConfig); fireObjectEndEvent(jsonConfig); return new JSONObject(true); } if (tokener.nextClean() != '{') { throw tokener.syntaxError("A JSONObject text must begin with '{'"); } fireObjectStartEvent(jsonConfig); Collection exclusions = jsonConfig.getMergedExcludes(); PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter(); JSONObject jsonObject = new JSONObject(); for (;;) { c = tokener.nextClean(); switch (c) { case 0: throw tokener.syntaxError("A JSONObject text must end with '}'"); case '}': fireObjectEndEvent(jsonConfig); return jsonObject; default: tokener.back(); key = tokener.nextValue(jsonConfig).toString(); } /* * The key is followed by ':'. We will also tolerate '=' or '=>'. */ c = tokener.nextClean(); if (c == '=') { if (tokener.next() != '>') { tokener.back(); } } else if (c != ':') { throw tokener.syntaxError("Expected a ':' after a key"); } char peek = tokener.peek(); boolean quoted = peek == '"' || peek == '\''; Object v = tokener.nextValue(jsonConfig); if (quoted || !JSONUtils.isFunctionHeader(v)) { if (exclusions.contains(key)) { switch (tokener.nextClean()) { case ';': case ',': if (tokener.nextClean() == '}') { fireObjectEndEvent(jsonConfig); return jsonObject; } tokener.back(); break; case '}': fireObjectEndEvent(jsonConfig); return jsonObject; default: throw tokener.syntaxError("Expected a ',' or '}'"); } continue; } if (jsonPropertyFilter == null || !jsonPropertyFilter.apply(tokener, key, v)) { if (quoted && v instanceof String && (JSONUtils.mayBeJSON((String) v) || JSONUtils.isFunction(v))) { v = JSONUtils.DOUBLE_QUOTE + v + JSONUtils.DOUBLE_QUOTE; } if (jsonObject.properties.containsKey(key)) { jsonObject.accumulate(key, v, jsonConfig); firePropertySetEvent(key, v, true, jsonConfig); } else { jsonObject.element(key, v, jsonConfig); firePropertySetEvent(key, v, false, jsonConfig); } } } else { // read params if any String params = JSONUtils.getFunctionParams((String) v); // read function text int i = 0; StringBuffer sb = new StringBuffer(); for (;;) { char ch = tokener.next(); if (ch == 0) { break; } if (ch == '{') { i++; } if (ch == '}') { i--; } sb.append(ch); if (i == 0) { break; } } if (i != 0) { throw tokener.syntaxError("Unbalanced '{' or '}' on prop: " + v); } // trim '{' at start and '}' at end String text = sb.toString(); text = text.substring(1, text.length() - 1).trim(); value = new JSONFunction((params != null) ? StringUtils.split(params, ",") : null, text); if (jsonPropertyFilter == null || !jsonPropertyFilter.apply(tokener, key, value)) { if (jsonObject.properties.containsKey(key)) { jsonObject.accumulate(key, value, jsonConfig); firePropertySetEvent(key, value, true, jsonConfig); } else { jsonObject.element(key, value, jsonConfig); firePropertySetEvent(key, value, false, jsonConfig); } } } /* * Pairs are separated by ','. We will also tolerate ';'. */ switch (tokener.nextClean()) { case ';': case ',': if (tokener.nextClean() == '}') { fireObjectEndEvent(jsonConfig); return jsonObject; } tokener.back(); break; case '}': fireObjectEndEvent(jsonConfig); return jsonObject; default: throw tokener.syntaxError("Expected a ',' or '}'"); } } } catch (JSONException jsone) { fireErrorEvent(jsone, jsonConfig); throw jsone; } } private static JSONObject _fromMap(Map map, JsonConfig jsonConfig) { if (map == null) { fireObjectStartEvent(jsonConfig); fireObjectEndEvent(jsonConfig); return new JSONObject(true); } if (!addInstance(map)) { try { return jsonConfig.getCycleDetectionStrategy().handleRepeatedReferenceAsObject(map); } catch (JSONException jsone) { removeInstance(map); fireErrorEvent(jsone, jsonConfig); throw jsone; } catch (RuntimeException e) { removeInstance(map); JSONException jsone = new JSONException(e); fireErrorEvent(jsone, jsonConfig); throw jsone; } } fireObjectStartEvent(jsonConfig); Collection exclusions = jsonConfig.getMergedExcludes(); JSONObject jsonObject = new JSONObject(); PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter(); try { for (Iterator entries = map.entrySet().iterator(); entries.hasNext();) { boolean bypass = false; Map.Entry entry = (Map.Entry) entries.next(); Object k = entry.getKey(); if (k == null) { throw new JSONException("JSON keys cannot be null."); } if (!(k instanceof String) && !jsonConfig.isAllowNonStringKeys()) { throw new ClassCastException("JSON keys must be strings."); } String key = String.valueOf(k); if ("null".equals(key)) { throw new NullPointerException("JSON keys must not be null nor the 'null' string."); } if (exclusions.contains(key)) { continue; } Object value = entry.getValue(); if (jsonPropertyFilter != null && jsonPropertyFilter.apply(map, key, value)) { continue; } if (value != null) { JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(value.getClass(), key); if (jsonValueProcessor != null) { value = jsonValueProcessor.processObjectValue(key, value, jsonConfig); bypass = true; if (!JsonVerifier.isValidJsonValue(value)) { throw new JSONException("Value is not a valid JSON value. " + value); } } setValue(jsonObject, key, value, value.getClass(), jsonConfig, bypass); } else { if (jsonObject.properties.containsKey(key)) { jsonObject.accumulate(key, JSONNull.getInstance()); firePropertySetEvent(key, JSONNull.getInstance(), true, jsonConfig); } else { jsonObject.element(key, JSONNull.getInstance()); firePropertySetEvent(key, JSONNull.getInstance(), false, jsonConfig); } } } } catch (JSONException jsone) { removeInstance(map); fireErrorEvent(jsone, jsonConfig); throw jsone; } catch (RuntimeException e) { removeInstance(map); JSONException jsone = new JSONException(e); fireErrorEvent(jsone, jsonConfig); throw jsone; } removeInstance(map); fireObjectEndEvent(jsonConfig); return jsonObject; } private static JSONObject _fromString(String str, JsonConfig jsonConfig) { if (str == null || "null".equals(str)) { fireObjectStartEvent(jsonConfig); fireObjectEndEvent(jsonConfig); return new JSONObject(true); } return _fromJSONTokener(new JSONTokener(str), jsonConfig); } private static Object convertPropertyValueToArray(String key, Object value, Class targetType, JsonConfig jsonConfig, Map classMap) { Class innerType = JSONUtils.getInnerComponentType(targetType); Class targetInnerType = findTargetClass(key, classMap); if (innerType.equals(Object.class) && targetInnerType != null && !targetInnerType.equals(Object.class)) { innerType = targetInnerType; } JsonConfig jsc = jsonConfig.copy(); jsc.setRootClass(innerType); jsc.setClassMap(classMap); Object array = JSONArray.toArray((JSONArray) value, jsc); if (innerType.isPrimitive() || JSONUtils.isNumber(innerType) || Boolean.class.isAssignableFrom(innerType) || JSONUtils.isString(innerType)) { array = JSONUtils.getMorpherRegistry().morph(Array.newInstance(innerType, 0).getClass(), array); } else if (!array.getClass().equals(targetType)) { if (!targetType.equals(Object.class)) { Morpher morpher = JSONUtils.getMorpherRegistry() .getMorpherFor(Array.newInstance(innerType, 0).getClass()); if (IdentityObjectMorpher.getInstance().equals(morpher)) { ObjectArrayMorpher beanMorpher = new ObjectArrayMorpher( new BeanMorpher(innerType, JSONUtils.getMorpherRegistry())); JSONUtils.getMorpherRegistry().registerMorpher(beanMorpher); } array = JSONUtils.getMorpherRegistry().morph(Array.newInstance(innerType, 0).getClass(), array); } } return array; } /** * modified by: houxu@xwtec.cn * ??:The method convertPropertyValueToList(String, Object, JsonConfig, String, Map) from the type JSONObject is never used locally */ // private static List convertPropertyValueToList( String key, Object value, JsonConfig jsonConfig,String name, Map classMap ) { // // Class targetClass = findTargetClass( key, classMap ); // targetClass = targetClass == null ? findTargetClass( name, classMap ) : targetClass; // JsonConfig jsc = jsonConfig.copy(); // jsc.setRootClass( targetClass ); // jsc.setClassMap( classMap ); // List list = (List) JSONArray.toCollection( (JSONArray) value, jsc ); // // return list; // } private static Collection convertPropertyValueToCollection(String key, Object value, JsonConfig jsonConfig, String name, Map classMap, Class collectionType) { Class targetClass = findTargetClass(key, classMap); targetClass = targetClass == null ? findTargetClass(name, classMap) : targetClass; JsonConfig jsc = jsonConfig.copy(); jsc.setRootClass(targetClass); jsc.setClassMap(classMap); jsc.setCollectionType(collectionType); return JSONArray.toCollection((JSONArray) value, jsc); } /** * Locates a Class associated to a specifi key.<br> * The key may be a regexp. */ private static Class findTargetClass(String key, Map classMap) { // try get first Class targetClass = (Class) classMap.get(key); if (targetClass == null) { // try with regexp // this will hit performance as it must iterate over all the keys // and create a RegexpMatcher for each key for (Iterator i = classMap.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); if (RegexpUtils.getMatcher((String) entry.getKey()).matches(key)) { targetClass = (Class) entry.getValue(); break; } } } return targetClass; } private static boolean isTransientField(String name, Class beanClass) { try { return isTransientField(beanClass.getDeclaredField(name)); } catch (Exception e) { // swallow exception } return false; } private static boolean isTransientField(Field field) { return (field.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT; } private static Object morphPropertyValue(String key, Object value, Class type, Class targetType) { Morpher morpher = JSONUtils.getMorpherRegistry().getMorpherFor(targetType); if (IdentityObjectMorpher.getInstance().equals(morpher)) { log.warn("Can't transform property '" + key + "' from " + type.getName() + " into " + targetType.getName() + ". Will register a default BeanMorpher"); JSONUtils.getMorpherRegistry() .registerMorpher(new BeanMorpher(targetType, JSONUtils.getMorpherRegistry())); } value = JSONUtils.getMorpherRegistry().morph(targetType, value); return value; } /** * Sets a property on the target bean.<br> * Bean may be a Map or a POJO. */ private static void setProperty(Object bean, String key, Object value, JsonConfig jsonConfig) throws Exception { PropertySetStrategy propertySetStrategy = jsonConfig.getPropertySetStrategy() != null ? jsonConfig.getPropertySetStrategy() : PropertySetStrategy.DEFAULT; propertySetStrategy.setProperty(bean, key, value, jsonConfig); } private static void setValue(JSONObject jsonObject, String key, Object value, Class type, JsonConfig jsonConfig, boolean bypass) { boolean accumulated = false; if (value == null) { value = jsonConfig.findDefaultValueProcessor(type).getDefaultValue(type); if (!JsonVerifier.isValidJsonValue(value)) { throw new JSONException("Value is not a valid JSON value. " + value); } } if (jsonObject.properties.containsKey(key)) { if (String.class.isAssignableFrom(type)) { Object o = jsonObject.opt(key); if (o instanceof JSONArray) { ((JSONArray) o).addString((String) value); } else { jsonObject.properties.put(key, new JSONArray().element(o).addString((String) value)); } } else { jsonObject.accumulate(key, value, jsonConfig); } accumulated = true; } else { if (bypass || String.class.isAssignableFrom(type)) { jsonObject.properties.put(key, value); } else { jsonObject.setInternal(key, value, jsonConfig); } } value = jsonObject.opt(key); if (accumulated) { JSONArray array = (JSONArray) value; value = array.get(array.size() - 1); } firePropertySetEvent(key, value, accumulated, jsonConfig); } // ------------------------------------------------------ /** identifies this object as null */ private boolean nullObject; /** * The Map where the JSONObject's properties are kept. */ private Map properties; /** * Construct an empty JSONObject. */ public JSONObject() { this.properties = new ListOrderedMap(); } /** * Creates a JSONObject that is null. */ public JSONObject(boolean isNull) { this(); this.nullObject = isNull; } /** * Accumulate values under a key. It is similar to the element method except * that if there is already an object stored under the key then a JSONArray * is stored under the key to hold all of the accumulated values. If there is * already a JSONArray, then the new value is appended to it. In contrast, * the replace method replaces the previous value. * * @param key A key string. * @param value An object to be accumulated under the key. * @return this. * @throws JSONException If the value is an invalid number or if the key is * null. */ public JSONObject accumulate(String key, boolean value) { return _accumulate(key, value ? Boolean.TRUE : Boolean.FALSE, new JsonConfig()); } /** * Accumulate values under a key. It is similar to the element method except * that if there is already an object stored under the key then a JSONArray * is stored under the key to hold all of the accumulated values. If there is * already a JSONArray, then the new value is appended to it. In contrast, * the replace method replaces the previous value. * * @param key A key string. * @param value An object to be accumulated under the key. * @return this. * @throws JSONException If the value is an invalid number or if the key is * null. */ public JSONObject accumulate(String key, double value) { return _accumulate(key, new Double(value), new JsonConfig()); } /** * Accumulate values under a key. It is similar to the element method except * that if there is already an object stored under the key then a JSONArray * is stored under the key to hold all of the accumulated values. If there is * already a JSONArray, then the new value is appended to it. In contrast, * the replace method replaces the previous value. * * @param key A key string. * @param value An object to be accumulated under the key. * @return this. * @throws JSONException If the value is an invalid number or if the key is * null. */ public JSONObject accumulate(String key, int value) { return _accumulate(key, new Integer(value), new JsonConfig()); } /** * Accumulate values under a key. It is similar to the element method except * that if there is already an object stored under the key then a JSONArray * is stored under the key to hold all of the accumulated values. If there is * already a JSONArray, then the new value is appended to it. In contrast, * the replace method replaces the previous value. * * @param key A key string. * @param value An object to be accumulated under the key. * @return this. * @throws JSONException If the value is an invalid number or if the key is * null. */ public JSONObject accumulate(String key, long value) { return _accumulate(key, new Long(value), new JsonConfig()); } /** * Accumulate values under a key. It is similar to the element method except * that if there is already an object stored under the key then a JSONArray * is stored under the key to hold all of the accumulated values. If there is * already a JSONArray, then the new value is appended to it. In contrast, * the replace method replaces the previous value. * * @param key A key string. * @param value An object to be accumulated under the key. * @return this. * @throws JSONException If the value is an invalid number or if the key is * null. */ public JSONObject accumulate(String key, Object value) { return _accumulate(key, value, new JsonConfig()); } /** * Accumulate values under a key. It is similar to the element method except * that if there is already an object stored under the key then a JSONArray * is stored under the key to hold all of the accumulated values. If there is * already a JSONArray, then the new value is appended to it. In contrast, * the replace method replaces the previous value. * * @param key A key string. * @param value An object to be accumulated under the key. * @return this. * @throws JSONException If the value is an invalid number or if the key is * null. */ public JSONObject accumulate(String key, Object value, JsonConfig jsonConfig) { return _accumulate(key, value, jsonConfig); } public void accumulateAll(Map map) { accumulateAll(map, new JsonConfig()); } public void accumulateAll(Map map, JsonConfig jsonConfig) { if (map instanceof JSONObject) { for (Iterator entries = map.entrySet().iterator(); entries.hasNext();) { Map.Entry entry = (Map.Entry) entries.next(); String key = (String) entry.getKey(); Object value = entry.getValue(); accumulate(key, value, jsonConfig); } } else { for (Iterator entries = map.entrySet().iterator(); entries.hasNext();) { Map.Entry entry = (Map.Entry) entries.next(); String key = String.valueOf(entry.getKey()); Object value = entry.getValue(); accumulate(key, value, jsonConfig); } } } public void clear() { properties.clear(); } public int compareTo(Object obj) { if (obj != null && (obj instanceof JSONObject)) { JSONObject other = (JSONObject) obj; int size1 = size(); int size2 = other.size(); if (size1 < size2) { return -1; } else if (size1 > size2) { return 1; } else if (this.equals(other)) { return 0; } } return -1; } public boolean containsKey(Object key) { return properties.containsKey(key); } public boolean containsValue(Object value) { return containsValue(value, new JsonConfig()); } public boolean containsValue(Object value, JsonConfig jsonConfig) { try { value = processValue(value, jsonConfig); } catch (JSONException e) { return false; } return properties.containsValue(value); } /** * Remove a name and its value, if present. * * @param key A key string. * @return this. */ public JSONObject discard(String key) { verifyIsNull(); this.properties.remove(key); return this; } /** * Put a key/boolean pair in the JSONObject. * * @param key A key string. * @param value A boolean which is the value. * @return this. * @throws JSONException If the key is null. */ public JSONObject element(String key, boolean value) { verifyIsNull(); return element(key, value ? Boolean.TRUE : Boolean.FALSE); } /** * Put a key/value pair in the JSONObject, where the value will be a * JSONArray which is produced from a Collection. * * @param key A key string. * @param value A Collection value. * @return this. * @throws JSONException */ public JSONObject element(String key, Collection value) { return element(key, value, new JsonConfig()); } /** * Put a key/value pair in the JSONObject, where the value will be a * JSONArray which is produced from a Collection. * * @param key A key string. * @param value A Collection value. * @return this. * @throws JSONException */ public JSONObject element(String key, Collection value, JsonConfig jsonConfig) { verifyIsNull(); if (!(value instanceof JSONArray)) { value = JSONArray.fromObject(value, jsonConfig); } return setInternal(key, value, jsonConfig); } /** * Put a key/double pair in the JSONObject. * * @param key A key string. * @param value A double which is the value. * @return this. * @throws JSONException If the key is null or if the number is invalid. */ public JSONObject element(String key, double value) { verifyIsNull(); Double d = new Double(value); JSONUtils.testValidity(d); return element(key, d); } /** * Put a key/int pair in the JSONObject. * * @param key A key string. * @param value An int which is the value. * @return this. * @throws JSONException If the key is null. */ public JSONObject element(String key, int value) { verifyIsNull(); return element(key, new Integer(value)); } /** * Put a key/long pair in the JSONObject. * * @param key A key string. * @param value A long which is the value. * @return this. * @throws JSONException If the key is null. */ public JSONObject element(String key, long value) { verifyIsNull(); return element(key, new Long(value)); } /** * Put a key/value pair in the JSONObject, where the value will be a * JSONObject which is produced from a Map. * * @param key A key string. * @param value A Map value. * @return this. * @throws JSONException */ public JSONObject element(String key, Map value) { return element(key, value, new JsonConfig()); } /** * Put a key/value pair in the JSONObject, where the value will be a * JSONObject which is produced from a Map. * * @param key A key string. * @param value A Map value. * @return this. * @throws JSONException */ public JSONObject element(String key, Map value, JsonConfig jsonConfig) { verifyIsNull(); if (value instanceof JSONObject) { return setInternal(key, value, jsonConfig); } else { return element(key, JSONObject.fromObject(value, jsonConfig), jsonConfig); } } /** * Put a key/value pair in the JSONObject. If the value is null, then the key * will be removed from the JSONObject if it is present.<br> * If there is a previous value assigned to the key, it will call accumulate. * * @param key A key string. * @param value An object which is the value. It should be of one of these * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, * String, or the JSONNull object. * @return this. * @throws JSONException If the value is non-finite number or if the key is * null. */ public JSONObject element(String key, Object value) { return element(key, value, new JsonConfig()); } /** * Put a key/value pair in the JSONObject. If the value is null, then the key * will be removed from the JSONObject if it is present.<br> * If there is a previous value assigned to the key, it will call accumulate. * * @param key A key string. * @param value An object which is the value. It should be of one of these * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, * String, or the JSONNull object. * @return this. * @throws JSONException If the value is non-finite number or if the key is * null. */ public JSONObject element(String key, Object value, JsonConfig jsonConfig) { verifyIsNull(); if (key == null) { throw new JSONException("Null key."); } /** modified by houxu 2013-09-02 * ?JSONNull? * null,? if( value != null ){ value = processValue( key, value, jsonConfig ); _setInternal( key, value, jsonConfig ); }else{ remove( key ); } */ if ((value instanceof JSONNull) || value == null) value = ""; value = processValue(key, value, jsonConfig); _setInternal(key, value, jsonConfig); return this; } /** * Put a key/value pair in the JSONObject, but only if the key and the value * are both non-null. * * @param key A key string. * @param value An object which is the value. It should be of one of these * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, * String, or the JSONNull object. * @return this. * @throws JSONException If the value is a non-finite number. */ public JSONObject elementOpt(String key, Object value) { return elementOpt(key, value, new JsonConfig()); } /** * Put a key/value pair in the JSONObject, but only if the key and the value * are both non-null. * * @param key A key string. * @param value An object which is the value. It should be of one of these * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, * String, or the JSONNull object. * @return this. * @throws JSONException If the value is a non-finite number. */ public JSONObject elementOpt(String key, Object value, JsonConfig jsonConfig) { verifyIsNull(); if (key != null && value != null) { element(key, value, jsonConfig); } return this; } public Set entrySet() { return Collections.unmodifiableSet(properties.entrySet()); } public boolean equals(Object obj) { if (obj == this) { return true; } if (obj == null) { return false; } if (!(obj instanceof JSONObject)) { return false; } JSONObject other = (JSONObject) obj; if (isNullObject()) { if (other.isNullObject()) { return true; } else { return false; } } else { if (other.isNullObject()) { return false; } } if (other.size() != size()) { return false; } for (Iterator keys = properties.keySet().iterator(); keys.hasNext();) { String key = (String) keys.next(); if (!other.properties.containsKey(key)) { return false; } Object o1 = properties.get(key); Object o2 = other.properties.get(key); if (JSONNull.getInstance().equals(o1)) { if (JSONNull.getInstance().equals(o2)) { continue; } else { return false; } } else { if (JSONNull.getInstance().equals(o2)) { return false; } } if (o1 instanceof String && o2 instanceof JSONFunction) { if (!o1.equals(String.valueOf(o2))) { return false; } } else if (o1 instanceof JSONFunction && o2 instanceof String) { if (!o2.equals(String.valueOf(o1))) { return false; } } else if (o1 instanceof JSONObject && o2 instanceof JSONObject) { if (!o1.equals(o2)) { return false; } } else if (o1 instanceof JSONArray && o2 instanceof JSONArray) { if (!o1.equals(o2)) { return false; } } else if (o1 instanceof JSONFunction && o2 instanceof JSONFunction) { if (!o1.equals(o2)) { return false; } } else { if (o1 instanceof String) { if (!o1.equals(String.valueOf(o2))) { return false; } } else if (o2 instanceof String) { if (!o2.equals(String.valueOf(o1))) { return false; } } else { Morpher m1 = JSONUtils.getMorpherRegistry().getMorpherFor(o1.getClass()); Morpher m2 = JSONUtils.getMorpherRegistry().getMorpherFor(o2.getClass()); if (m1 != null && m1 != IdentityObjectMorpher.getInstance()) { if (!o1.equals(JSONUtils.getMorpherRegistry().morph(o1.getClass(), o2))) { return false; } } else if (m2 != null && m2 != IdentityObjectMorpher.getInstance()) { if (!JSONUtils.getMorpherRegistry().morph(o1.getClass(), o1).equals(o2)) { return false; } } else { if (!o1.equals(o2)) { return false; } } } } } return true; } public Object get(Object key) { if (key instanceof String) { return get((String) key); } return null; } /** * Get the value object associated with a key. * * @param key A key string. * @return The object associated with the key. * @throws JSONException if this.isNull() returns true. */ public Object get(String key) { verifyIsNull(); return this.properties.get(key); } /** * Get the boolean value associated with a key. * * @param key A key string. * @return The truth. * @throws JSONException if the value is not a Boolean or the String "true" * or "false". */ public boolean getBoolean(String key) { verifyIsNull(); Object o = get(key); if (o != null) { if (o.equals(Boolean.FALSE) || (o instanceof String && ((String) o).equalsIgnoreCase("false"))) { return false; } else if (o.equals(Boolean.TRUE) || (o instanceof String && ((String) o).equalsIgnoreCase("true"))) { return true; } } throw new JSONException("JSONObject[" + JSONUtils.quote(key) + "] is not a Boolean."); } /** * Get the double value associated with a key. * * @param key A key string. * @return The numeric value. * @throws JSONException if the key is not found or if the value is not a * Number object and cannot be converted to a number. */ public double getDouble(String key) { verifyIsNull(); Object o = get(key); if (o != null) { try { return o instanceof Number ? ((Number) o).doubleValue() : Double.parseDouble((String) o); } catch (Exception e) { throw new JSONException("JSONObject[" + JSONUtils.quote(key) + "] is not a number."); } } throw new JSONException("JSONObject[" + JSONUtils.quote(key) + "] is not a number."); } /** * Get the int value associated with a key. If the number value is too large * for an int, it will be clipped. * * @param key A key string. * @return The integer value. * @throws JSONException if the key is not found or if the value cannot be * converted to an integer. */ public int getInt(String key) { verifyIsNull(); Object o = get(key); if (o != null) { return o instanceof Number ? ((Number) o).intValue() : (int) getDouble(key); } throw new JSONException("JSONObject[" + JSONUtils.quote(key) + "] is not a number."); } /** * Get the JSONArray value associated with a key. * * @param key A key string. * @return A JSONArray which is the value. * @throws JSONException if the key is not found or if the value is not a * JSONArray. */ public JSONArray getJSONArray(String key) { verifyIsNull(); Object o = get(key); if (o != null && o instanceof JSONArray) { return (JSONArray) o; } throw new JSONException("JSONObject[" + JSONUtils.quote(key) + "] is not a JSONArray."); } /** * Get the JSONObject value associated with a key. * * @param key A key string. * @return A JSONObject which is the value. * @throws JSONException if the key is not found or if the value is not a * JSONObject. */ public JSONObject getJSONObject(String key) { verifyIsNull(); Object o = get(key); if (JSONNull.getInstance().equals(o)) { return new JSONObject(true); } else if (o instanceof JSONObject) { return (JSONObject) o; } throw new JSONException("JSONObject[" + JSONUtils.quote(key) + "] is not a JSONObject."); } /** * Get the long value associated with a key. If the number value is too long * for a long, it will be clipped. * * @param key A key string. * @return The long value. * @throws JSONException if the key is not found or if the value cannot be * converted to a long. */ public long getLong(String key) { verifyIsNull(); Object o = get(key); if (o != null) { return o instanceof Number ? ((Number) o).longValue() : (long) getDouble(key); } throw new JSONException("JSONObject[" + JSONUtils.quote(key) + "] is not a number."); } /** * Get the string associated with a key. * * @param key A key string. * @return A string which is the value. * @throws JSONException if the key is not found. */ public String getString(String key) { verifyIsNull(); Object o = get(key); if (o != null) { return o.toString(); } throw new JSONException("JSONObject[" + JSONUtils.quote(key) + "] not found."); } /** * Determine if the JSONObject contains a specific key. * * @param key A key string. * @return true if the key exists in the JSONObject. */ public boolean has(String key) { verifyIsNull(); return this.properties.containsKey(key); } public int hashCode() { int hashcode = 19; if (isNullObject()) { return hashcode + JSONNull.getInstance().hashCode(); } for (Iterator entries = properties.entrySet().iterator(); entries.hasNext();) { Map.Entry entry = (Map.Entry) entries.next(); Object key = entry.getKey(); Object value = entry.getValue(); hashcode += key.hashCode() + JSONUtils.hashCode(value); } return hashcode; } public boolean isArray() { return false; } public boolean isEmpty() { // verifyIsNull(); return this.properties.isEmpty(); } /** * Returs if this object is a null JSONObject. */ public boolean isNullObject() { return nullObject; } /** * Get an enumeration of the keys of the JSONObject. * * @return An iterator of the keys. */ public Iterator keys() { verifyIsNull(); return keySet().iterator(); } public Set keySet() { return Collections.unmodifiableSet(properties.keySet()); } /** * Produce a JSONArray containing the names of the elements of this * JSONObject. * * @return A JSONArray containing the key strings, or null if the JSONObject * is empty. */ public JSONArray names() { return names(new JsonConfig()); } /** * Produce a JSONArray containing the names of the elements of this * JSONObject. * * @return A JSONArray containing the key strings, or null if the JSONObject * is empty. */ public JSONArray names(JsonConfig jsonConfig) { verifyIsNull(); JSONArray ja = new JSONArray(); Iterator keys = keys(); while (keys.hasNext()) { ja.element(keys.next(), jsonConfig); } return ja; } /** * Get an optional value associated with a key. * * @param key A key string. * @return An object which is the value, or null if there is no value. */ public Object opt(String key) { verifyIsNull(); return key == null ? null : this.properties.get(key); } /** * Get an optional boolean associated with a key. It returns false if there * is no such key, or if the value is not Boolean.TRUE or the String "true". * * @param key A key string. * @return The truth. */ public boolean optBoolean(String key) { verifyIsNull(); return optBoolean(key, false); } /** * Get an optional boolean associated with a key. It returns the defaultValue * if there is no such key, or if it is not a Boolean or the String "true" or * "false" (case insensitive). * * @param key A key string. * @param defaultValue The default. * @return The truth. */ public boolean optBoolean(String key, boolean defaultValue) { verifyIsNull(); try { return getBoolean(key); } catch (Exception e) { return defaultValue; } } /** * Get an optional double associated with a key, or NaN if there is no such * key or if its value is not a number. If the value is a string, an attempt * will be made to evaluate it as a number. * * @param key A string which is the key. * @return An object which is the value. */ public double optDouble(String key) { verifyIsNull(); return optDouble(key, Double.NaN); } /** * Get an optional double associated with a key, or the defaultValue if there * is no such key or if its value is not a number. If the value is a string, * an attempt will be made to evaluate it as a number. * * @param key A key string. * @param defaultValue The default. * @return An object which is the value. */ public double optDouble(String key, double defaultValue) { verifyIsNull(); try { Object o = opt(key); return o instanceof Number ? ((Number) o).doubleValue() : new Double((String) o).doubleValue(); } catch (Exception e) { return defaultValue; } } /** * Get an optional int value associated with a key, or zero if there is no * such key or if the value is not a number. If the value is a string, an * attempt will be made to evaluate it as a number. * * @param key A key string. * @return An object which is the value. */ public int optInt(String key) { verifyIsNull(); return optInt(key, 0); } /** * Get an optional int value associated with a key, or the default if there * is no such key or if the value is not a number. If the value is a string, * an attempt will be made to evaluate it as a number. * * @param key A key string. * @param defaultValue The default. * @return An object which is the value. */ public int optInt(String key, int defaultValue) { verifyIsNull(); try { return getInt(key); } catch (Exception e) { return defaultValue; } } /** * Get an optional JSONArray associated with a key. It returns null if there * is no such key, or if its value is not a JSONArray. * * @param key A key string. * @return A JSONArray which is the value. */ public JSONArray optJSONArray(String key) { verifyIsNull(); Object o = opt(key); return o instanceof JSONArray ? (JSONArray) o : null; } /** * Get an optional JSONObject associated with a key. It returns null if there * is no such key, or if its value is not a JSONObject. * * @param key A key string. * @return A JSONObject which is the value. */ public JSONObject optJSONObject(String key) { verifyIsNull(); Object o = opt(key); return o instanceof JSONObject ? (JSONObject) o : null; } /** * Get an optional long value associated with a key, or zero if there is no * such key or if the value is not a number. If the value is a string, an * attempt will be made to evaluate it as a number. * * @param key A key string. * @return An object which is the value. */ public long optLong(String key) { verifyIsNull(); return optLong(key, 0); } /** * Get an optional long value associated with a key, or the default if there * is no such key or if the value is not a number. If the value is a string, * an attempt will be made to evaluate it as a number. * * @param key A key string. * @param defaultValue The default. * @return An object which is the value. */ public long optLong(String key, long defaultValue) { verifyIsNull(); try { return getLong(key); } catch (Exception e) { return defaultValue; } } /** * Get an optional string associated with a key. It returns an empty string * if there is no such key. If the value is not a string and is not null, * then it is coverted to a string. * * @param key A key string. * @return A string which is the value. */ public String optString(String key) { verifyIsNull(); return optString(key, ""); } /** * Get an optional string associated with a key. It returns the defaultValue * if there is no such key. * * @param key A key string. * @param defaultValue The default. * @return A string which is the value. */ public String optString(String key, String defaultValue) { verifyIsNull(); Object o = opt(key); return o != null ? o.toString() : defaultValue; } public Object put(Object key, Object value) { if (key == null) { throw new IllegalArgumentException("key is null."); } Object previous = properties.get(key); element(String.valueOf(key), value); return previous; } public void putAll(Map map) { putAll(map, new JsonConfig()); } public void putAll(Map map, JsonConfig jsonConfig) { if (map instanceof JSONObject) { for (Iterator entries = map.entrySet().iterator(); entries.hasNext();) { Map.Entry entry = (Map.Entry) entries.next(); String key = (String) entry.getKey(); Object value = entry.getValue(); this.properties.put(key, value); } } else { for (Iterator entries = map.entrySet().iterator(); entries.hasNext();) { Map.Entry entry = (Map.Entry) entries.next(); String key = String.valueOf(entry.getKey()); Object value = entry.getValue(); element(key, value, jsonConfig); } } } public Object remove(Object key) { return properties.remove(key); } /** * Remove a name and its value, if present. * * @param key The name to be removed. * @return The value that was associated with the name, or null if there was * no value. */ public Object remove(String key) { verifyIsNull(); return this.properties.remove(key); } /** * Get the number of keys stored in the JSONObject. * * @return The number of keys in the JSONObject. */ public int size() { // verifyIsNull(); return this.properties.size(); } /** * Produce a JSONArray containing the values of the members of this * JSONObject. * * @param names A JSONArray containing a list of key strings. This determines * the sequence of the values in the result. * @return A JSONArray of values. * @throws JSONException If any of the values are non-finite numbers. */ public JSONArray toJSONArray(JSONArray names) { verifyIsNull(); if (names == null || names.size() == 0) { return null; } JSONArray ja = new JSONArray(); for (int i = 0; i < names.size(); i += 1) { ja.element(this.opt(names.getString(i))); } return ja; } /** * Make a JSON text of this JSONObject. For compactness, no whitespace is * added. If this would not result in a syntactically correct JSON text, then * null will be returned instead. * <p> * Warning: This method assumes that the data structure is acyclical. * * @return a printable, displayable, portable, transmittable representation * of the object, beginning with <code>{</code> <small>(left * brace)</small> and ending with <code>}</code> <small>(right * brace)</small>. */ public String toString() { if (isNullObject()) { return JSONNull.getInstance().toString(); } try { Iterator keys = keys(); StringBuffer sb = new StringBuffer("{"); while (keys.hasNext()) { if (sb.length() > 1) { sb.append(','); } Object o = keys.next(); sb.append(JSONUtils.quote(o.toString())); sb.append(':'); sb.append(JSONUtils.valueToString(this.properties.get(o))); } sb.append('}'); return sb.toString(); } catch (Exception e) { return null; } } /** * Make a prettyprinted JSON text of this JSONObject. * <p> * Warning: This method assumes that the data structure is acyclical. * * @param indentFactor The number of spaces to add to each level of * indentation. * @return a printable, displayable, portable, transmittable representation * of the object, beginning with <code>{</code> <small>(left * brace)</small> and ending with <code>}</code> <small>(right * brace)</small>. * @throws JSONException If the object contains an invalid number. */ public String toString(int indentFactor) { if (isNullObject()) { return JSONNull.getInstance().toString(); } if (indentFactor == 0) { return this.toString(); } return toString(indentFactor, 0); } /** * Make a prettyprinted JSON text of this JSONObject. * <p> * Warning: This method assumes that the data structure is acyclical. * * @param indentFactor The number of spaces to add to each level of * indentation. * @param indent The indentation of the top level. * @return a printable, displayable, transmittable representation of the * object, beginning with <code>{</code> <small>(left brace)</small> * and ending with <code>}</code> <small>(right brace)</small>. * @throws JSONException If the object contains an invalid number. */ public String toString(int indentFactor, int indent) { if (isNullObject()) { return JSONNull.getInstance().toString(); } int i; int n = size(); if (n == 0) { return "{}"; } if (indentFactor == 0) { return this.toString(); } Iterator keys = keys(); StringBuffer sb = new StringBuffer("{"); int newindent = indent + indentFactor; Object o; if (n == 1) { o = keys.next(); sb.append(JSONUtils.quote(o.toString())); sb.append(": "); sb.append(JSONUtils.valueToString(this.properties.get(o), indentFactor, indent)); } else { while (keys.hasNext()) { o = keys.next(); if (sb.length() > 1) { sb.append(",\n"); } else { sb.append('\n'); } for (i = 0; i < newindent; i += 1) { sb.append(' '); } sb.append(JSONUtils.quote(o.toString())); sb.append(": "); sb.append(JSONUtils.valueToString(this.properties.get(o), indentFactor, newindent)); } if (sb.length() > 1) { sb.append('\n'); for (i = 0; i < indent; i += 1) { sb.append(' '); } } for (i = 0; i < indent; i += 1) { sb.insert(0, ' '); } } sb.append('}'); return sb.toString(); } public Collection values() { return Collections.unmodifiableCollection(properties.values()); } /** * Write the contents of the JSONObject as JSON text to a writer. For * compactness, no whitespace is added. * <p> * Warning: This method assumes that the data structure is acyclical. * * @return The writer. * @throws JSONException */ public Writer write(Writer writer) { try { if (isNullObject()) { writer.write(JSONNull.getInstance().toString()); return writer; } boolean b = false; Iterator keys = keys(); writer.write('{'); while (keys.hasNext()) { if (b) { writer.write(','); } Object k = keys.next(); writer.write(JSONUtils.quote(k.toString())); writer.write(':'); Object v = this.properties.get(k); if (v instanceof JSONObject) { ((JSONObject) v).write(writer); } else if (v instanceof JSONArray) { ((JSONArray) v).write(writer); } else { writer.write(JSONUtils.valueToString(v)); } b = true; } writer.write('}'); return writer; } catch (IOException e) { throw new JSONException(e); } } private JSONObject _accumulate(String key, Object value, JsonConfig jsonConfig) { if (isNullObject()) { throw new JSONException("Can't accumulate on null object"); } if (!has(key)) { setInternal(key, value, jsonConfig); } else { Object o = opt(key); if (o instanceof JSONArray) { ((JSONArray) o).element(value, jsonConfig); } else { setInternal(key, new JSONArray().element(o).element(value, jsonConfig), jsonConfig); } } return this; } protected Object _processValue(Object value, JsonConfig jsonConfig) { if (value instanceof JSONTokener) { return _fromJSONTokener((JSONTokener) value, jsonConfig); } return super._processValue(value, jsonConfig); } /** * Put a key/value pair in the JSONObject. * * @param key A key string. * @param value An object which is the value. It should be of one of these * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, * String, or the JSONNull object. * @return this. * @throws JSONException If the value is non-finite number or if the key is * null. */ private JSONObject _setInternal(String key, Object value, JsonConfig jsonConfig) { verifyIsNull(); if (key == null) { throw new JSONException("Null key."); } if (JSONUtils.isString(value) && JSONUtils.mayBeJSON(String.valueOf(value))) { this.properties.put(key, value); } else { /* Object jo = _processValue( value, jsonConfig ); if( CycleDetectionStrategy.IGNORE_PROPERTY_OBJ == jo || CycleDetectionStrategy.IGNORE_PROPERTY_ARR == jo ){ // do nothing }else{ this.properties.put( key, jo ); } */ if (CycleDetectionStrategy.IGNORE_PROPERTY_OBJ == value || CycleDetectionStrategy.IGNORE_PROPERTY_ARR == value) { // do nothing } else { this.properties.put(key, value); } } return this; } private Object processValue(Object value, JsonConfig jsonConfig) { if (value != null) { JsonValueProcessor processor = jsonConfig.findJsonValueProcessor(value.getClass()); if (processor != null) { value = processor.processObjectValue(null, value, jsonConfig); if (!JsonVerifier.isValidJsonValue(value)) { throw new JSONException("Value is not a valid JSON value. " + value); } } } return _processValue(value, jsonConfig); } private Object processValue(String key, Object value, JsonConfig jsonConfig) { if (value != null) { JsonValueProcessor processor = jsonConfig.findJsonValueProcessor(value.getClass(), key); if (processor != null) { value = processor.processObjectValue(null, value, jsonConfig); if (!JsonVerifier.isValidJsonValue(value)) { throw new JSONException("Value is not a valid JSON value. " + value); } } } return _processValue(value, jsonConfig); } /** * Put a key/value pair in the JSONObject. * * @param key A key string. * @param value An object which is the value. It should be of one of these * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, * String, or the JSONNull object. * @return this. * @throws JSONException If the value is non-finite number or if the key is * null. */ private JSONObject setInternal(String key, Object value, JsonConfig jsonConfig) { return _setInternal(key, processValue(key, value, jsonConfig), jsonConfig); } /** * Checks if this object is a "null" object. */ private void verifyIsNull() { if (isNullObject()) { throw new JSONException("null object"); } } }