JSONObject 原始碼
/*
* 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 net.sf.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 net.sf.json.processors.JsonBeanProcessor;
import net.sf.json.processors.JsonValueProcessor;
import net.sf.json.processors.JsonVerifier;
import net.sf.json.processors.PropertyNameProcessor;
import net.sf.json.regexp.RegexpUtils;
import net.sf.json.util.CycleDetectionStrategy;
import net.sf.json.util.JSONTokener;
import net.sf.json.util.JSONUtils;
import net.sf.json.util.PropertyFilter;
import net.sf.json.util.PropertySetStrategy;
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;
/**
* 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
*/
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;
}
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." );
}
if( value != null ){
value = processValue( key, value, jsonConfig );
_setInternal( key, value, jsonConfig );
}else{
remove( key );
}
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" );
}
}
}
相關文章
- alibaba fastjson的JsonObject有序的實現和原始碼分析ASTJSONObject原始碼
- JSONObject.parseObject 、JSONObject.toJSONStringJSONObject
- 從原始碼分析JSONObject因版本差異導致toString格式異常問題原始碼JSONObject
- List集合轉JSONObjectJSONObject
- JSONObject和JSONArrayJSONObject
- JSONObject的過濾設定JSONObject
- JSONObject的toBean 和 fromObject (轉)JSONObjectBean
- JSONObject與JSONArray的使用JSONObject
- 原始碼原始碼原始碼樹品原始碼原始碼
- Gson轉換與JSONObject區別JSONObject
- JsonArray和JsonObject遍歷方法JSONObject
- 如何區分jsonarray 和 jsonobjectJSONObject
- 【JSON解析】淺談JSONObject的使用JSONObject
- java.lang.NoClassDefFoundError: org/json/JSONObjectJavaErrorJSONObject
- JSONObject與JSONArray 解析JSON格式字串JSONObject字串
- ClassNotFoundException: org.json.JSONObject解決辦法ExceptionJSONObject
- Spark原始碼-SparkContext原始碼解析Spark原始碼Context
- Retrofit原始碼分析三 原始碼分析原始碼
- 【Android原始碼】Fragment 原始碼分析Android原始碼Fragment
- 【Android原始碼】Intent 原始碼分析Android原始碼Intent
- Hadoop原始碼篇--Client原始碼Hadoop原始碼client
- 原始碼:原始碼
- 集合原始碼分析[2]-AbstractList 原始碼分析原始碼
- 集合原始碼分析[1]-Collection 原始碼分析原始碼
- ReactNative原始碼解析-初識原始碼React原始碼
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- 集合原始碼分析[3]-ArrayList 原始碼分析原始碼
- Flutter 原始碼系列:DropdownButton 原始碼淺析Flutter原始碼
- 原始碼|jdk原始碼之HashMap分析(一)原始碼JDKHashMap
- 原始碼|jdk原始碼之HashMap分析(二)原始碼JDKHashMap
- Guava 原始碼分析之 EventBus 原始碼分析Guava原始碼
- Spring原始碼系列:BeanDefinition原始碼解析Spring原始碼Bean
- Java集合原始碼剖析——ArrayList原始碼剖析Java原始碼
- 【Android原始碼】AlertDialog 原始碼分析Android原始碼
- 【Java集合原始碼剖析】ArrayList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】Vector原始碼剖析Java原始碼
- 【Java集合原始碼剖析】HashMap原始碼剖析Java原始碼HashMap
- 【Java集合原始碼剖析】Hashtable原始碼剖析Java原始碼