Skip to content

Commit

Permalink
[json-lib] fix for 2513691
Browse files Browse the repository at this point in the history
  • Loading branch information
aalmiray committed May 17, 2009
1 parent c01ead2 commit 9797a5c
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 19 deletions.
18 changes: 12 additions & 6 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,28 @@
version="2.3-SNAPSHOT"
date="n/a"
desc="Current development">
<action
dev="Andres Almiray"
type="fix"
issue="2513691">
Support for reading both properties and public fields
</action>
<action
dev="Andres Almiray"
type="fix"
issue="2776558">
undefined not an acceptable parser token
</action>
<action
dev="Andres Almiray"
type="fix"
issue="2431487">
dev="Andres Almiray"
type="fix"
issue="2431487">
Allow JSONArray/JSONObject to be Serializable
</action>
<action
dev="Andres Almiray"
type="fix"
issue="2682840">
dev="Andres Almiray"
type="fix"
issue="2682840">
JsonValueProcessor not processing primitive arrays (breaking change!)
</action>
<action
Expand Down
50 changes: 46 additions & 4 deletions src/main/java/net/sf/json/JSONObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,8 @@ public static Object toBean( JSONObject jsonObject, JsonConfig 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){
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()
Expand Down Expand Up @@ -764,6 +765,44 @@ private static JSONObject _fromBean( Object bean, JsonConfig 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++ ) {
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 );
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 );
}
}
}
catch( Exception e ){
log.trace( "Couldn't read public fields.", e );
}
}catch( JSONException jsone ){
removeInstance( bean );
fireErrorEvent( jsone, jsonConfig );
Expand Down Expand Up @@ -1238,13 +1277,16 @@ private static Class findTargetClass( String key, Map classMap ) {

private static boolean isTransientField( String name, Class beanClass ) {
try{
Field field = beanClass.getDeclaredField( name );
return (field.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT;
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()
Expand All @@ -1269,7 +1311,7 @@ private static void setProperty( Object bean, String key, Object value, JsonConf
throws Exception {
PropertySetStrategy propertySetStrategy = jsonConfig.getPropertySetStrategy() != null ? jsonConfig.getPropertySetStrategy()
: PropertySetStrategy.DEFAULT;
propertySetStrategy.setProperty( bean, key, value );
propertySetStrategy.setProperty( bean, key, value, jsonConfig );
}

private static void setValue( JSONObject jsonObject, String key, Object value, Class type,
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/net/sf/json/JsonConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public class JsonConfig {
private boolean ignoreDefaultExcludes;
private boolean ignoreJPATransient;
private boolean ignoreTransientFields;
private boolean ignorePublicFields = true;
private boolean javascriptCompliant;
private JavaIdentifierTransformer javaIdentifierTransformer = DEFAULT_JAVA_IDENTIFIER_TRANSFORMER;
private PropertyFilter javaPropertyFilter;
Expand Down Expand Up @@ -208,6 +209,7 @@ public JsonConfig copy() {
jsc.handleJettisonSingleElementArray = handleJettisonSingleElementArray;
jsc.ignoreDefaultExcludes = ignoreDefaultExcludes;
jsc.ignoreTransientFields = ignoreTransientFields;
jsc.ignorePublicFields = ignorePublicFields;
jsc.javaIdentifierTransformer = javaIdentifierTransformer;
jsc.javascriptCompliant = javascriptCompliant;
jsc.keyMap.putAll( keyMap );
Expand Down Expand Up @@ -707,6 +709,15 @@ public boolean isIgnoreTransientFields() {
return ignoreTransientFields;
}

/**
* Returns true if public fields of a bean will be ignored.<br>
* Default value is true.<br>
* [Java -&gt; JSON]
*/
public boolean isIgnorePublicFields() {
return ignorePublicFields;
}

/**
* Returns true if Javascript compatibility is turned on.<br>
* Default value is false.<br>
Expand Down Expand Up @@ -904,6 +915,7 @@ public void reset() {
excludes = EMPTY_EXCLUDES;
ignoreDefaultExcludes = false;
ignoreTransientFields = false;
ignorePublicFields = true;
javascriptCompliant = false;
javaIdentifierTransformer = DEFAULT_JAVA_IDENTIFIER_TRANSFORMER;
cycleDetectionStrategy = DEFAULT_CYCLE_DETECTION_STRATEGY;
Expand Down Expand Up @@ -1067,6 +1079,14 @@ public void setIgnoreTransientFields( boolean ignoreTransientFields ) {
this.ignoreTransientFields = ignoreTransientFields;
}

/**
* Sets if public fields would be skipped when building.<br>
* [Java -&gt; JSON]
*/
public void setIgnorePublicFields( boolean ignorePublicFields ) {
this.ignorePublicFields = ignorePublicFields;
}

/**
* Sets if Javascript compatibility is enabled when building.<br>
* [Java -&gt; JSON]
Expand Down
34 changes: 29 additions & 5 deletions src/main/java/net/sf/json/util/PropertySetStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

package net.sf.json.util;

import java.lang.reflect.Field;
import java.util.Map;

import net.sf.json.JSONException;
import net.sf.json.JsonConfig;

import org.apache.commons.beanutils.PropertyUtils;

Expand All @@ -33,18 +35,40 @@ public abstract class PropertySetStrategy {
public static final PropertySetStrategy DEFAULT = new DefaultPropertySetStrategy();

public abstract void setProperty( Object bean, String key, Object value ) throws JSONException;

public void setProperty( Object bean, String key, Object value, JsonConfig jsonConfig ) throws JSONException {
setProperty( bean, key, value );
}

private static final class DefaultPropertySetStrategy extends PropertySetStrategy {
public void setProperty( Object bean, String key, Object value ) throws JSONException {
setProperty( bean, key, value, new JsonConfig());
}

public void setProperty( Object bean, String key, Object value, JsonConfig jsonConfig ) throws JSONException {
if( bean instanceof Map ){
((Map) bean).put( key, value );
}else{
try{
PropertyUtils.setSimpleProperty( bean, key, value );
}catch( Exception e ){
throw new JSONException( e );
} else {
if( !jsonConfig.isIgnorePublicFields() ) {
try {
Field field = bean.getClass().getField( key );
if( field != null ) field.set( bean, value );
} catch( Exception e ){
_setProperty( bean, key, value );
}
} else {
_setProperty( bean, key, value );
}
}
}

private void _setProperty( Object bean, String key, Object value ) {
try {
PropertyUtils.setSimpleProperty( bean, key, value );
} catch( Exception e ) {
throw new JSONException( e );
}
}

}
}
50 changes: 46 additions & 4 deletions src/main/jdk15/net/sf/json/JSONObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,8 @@ public static Object toBean( JSONObject jsonObject, JsonConfig 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 ){
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()
Expand Down Expand Up @@ -958,6 +959,44 @@ private static JSONObject _fromBean( Object bean, JsonConfig 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++ ) {
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 );
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 );
}
}
}
catch( Exception e ){
log.trace( "Couldn't read public fields.", e );
}
}catch( JSONException jsone ){
removeInstance( bean );
fireErrorEvent( jsone, jsonConfig );
Expand Down Expand Up @@ -1466,14 +1505,17 @@ private static Class findTargetClass( String key, Map classMap ) {

private static boolean isTransientField( String name, Class beanClass ) {
try{
Field field = beanClass.getDeclaredField( name );
return (field.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT;
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 );
Expand Down Expand Up @@ -1503,7 +1545,7 @@ private static void setProperty( Object bean, String key, Object value, JsonConf
throws Exception {
PropertySetStrategy propertySetStrategy = jsonConfig.getPropertySetStrategy() != null ? jsonConfig.getPropertySetStrategy()
: PropertySetStrategy.DEFAULT;
propertySetStrategy.setProperty( bean, key, value );
propertySetStrategy.setProperty( bean, key, value, jsonConfig );
}

private static void setValue( JSONObject jsonObject, String key, Object value, Class type,
Expand Down
35 changes: 35 additions & 0 deletions src/test/java/net/sf/json/TestUserSubmitted.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import net.sf.json.sample.BeanC;
import net.sf.json.sample.ChildBean;
import net.sf.json.sample.DateBean;
import net.sf.json.sample.FieldBean;
import net.sf.json.sample.IdBean;
import net.sf.json.sample.InterfaceBean;
import net.sf.json.sample.JSONTestBean;
Expand Down Expand Up @@ -677,6 +678,40 @@ public void testJSONObject_JavascriptCompliant2() {
Assertions.assertEquals( JSONNull.getInstance(), object.get("key2") );
}

public void testJSONObject_fromObject_FieldBean() {
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.setIgnorePublicFields( false );
FieldBean bean = new FieldBean();
bean.setValue( 42 );
bean.string = "stringy";
JSONObject jsonObject = JSONObject.fromObject( bean );
assertNotNull( jsonObject );
assertEquals( 42, jsonObject.getInt( "value" ));
assertFalse( jsonObject.has( "string" ));
jsonObject = JSONObject.fromObject( bean, jsonConfig );
assertNotNull( jsonObject );
assertEquals( 42, jsonObject.getInt( "value" ));
assertEquals( "stringy", jsonObject.getString( "string" ));
}

public void testJSONObject_toBean_FieldBean() {
JSONObject jsonObject = new JSONObject();
jsonObject.element( "value", 42 );
jsonObject.element( "string", "stringy" );
FieldBean bean1 = (FieldBean) JSONObject.toBean( jsonObject, FieldBean.class );
assertNotNull( bean1 );
assertEquals( 42, bean1.getValue());
assertNull( bean1.string );

JsonConfig jsonConfig = new JsonConfig();
jsonConfig.setIgnorePublicFields( false );
jsonConfig.setRootClass( FieldBean.class );
FieldBean bean2 = (FieldBean) JSONObject.toBean( jsonObject, jsonConfig );
assertNotNull( bean2 );
assertEquals( 42, bean1.getValue());
assertEquals( "stringy", bean2.string );
}

public static class RunnableImpl implements Runnable {
public void run() {

Expand Down
Loading

0 comments on commit 9797a5c

Please sign in to comment.