@@ -39,6 +39,7 @@ of this software and associated documentation files (the "Software"), to deal
3939import java .util .Collection ;
4040import java .util .Enumeration ;
4141import java .util .HashMap ;
42+ import java .util .HashSet ;
4243import java .util .Iterator ;
4344import java .util .Locale ;
4445import java .util .Map ;
@@ -365,6 +366,11 @@ public JSONObject(Object bean) {
365366 this .populateMap (bean );
366367 }
367368
369+ private JSONObject (Object bean , Set <Object > objectsRecord ) {
370+ this ();
371+ this .populateMap (bean , objectsRecord );
372+ }
373+
368374 /**
369375 * Construct a JSONObject from an Object, using reflection to find the
370376 * public members. The resulting JSONObject's keys will be the strings from
@@ -1520,6 +1526,10 @@ public String optString(String key, String defaultValue) {
15201526 * the bean
15211527 */
15221528 private void populateMap (Object bean ) {
1529+ populateMap (bean , new HashSet <Object >());
1530+ }
1531+
1532+ private void populateMap (Object bean , Set <Object > objectsRecord ) {
15231533 Class <?> klass = bean .getClass ();
15241534
15251535 // If klass is a System class then set includeSuperClass to false.
@@ -1540,10 +1550,22 @@ && isValidMethodName(method.getName())) {
15401550 try {
15411551 final Object result = method .invoke (bean );
15421552 if (result != null ) {
1543- this .map .put (key , wrap (result ));
1553+ // check cyclic dependency and throw error if needed
1554+ // the wrap and populateMap combination method is
1555+ // itself DFS recursive
1556+ if (objectsRecord .contains (result )) {
1557+ throw recursivelyDefinedObjectException (key );
1558+ }
1559+
1560+ objectsRecord .add (result );
1561+
1562+ this .map .put (key , wrap (result , objectsRecord ));
1563+
1564+ objectsRecord .remove (result );
1565+
15441566 // we don't use the result anywhere outside of wrap
15451567 // if it's a resource we should be sure to close it
1546- // after calling toString
1568+ // after calling toString
15471569 if (result instanceof Closeable ) {
15481570 try {
15491571 ((Closeable ) result ).close ();
@@ -2431,6 +2453,10 @@ public static String valueToString(Object value) throws JSONException {
24312453 * @return The wrapped value
24322454 */
24332455 public static Object wrap (Object object ) {
2456+ return wrap (object , null );
2457+ }
2458+
2459+ private static Object wrap (Object object , Set <Object > objectsRecord ) {
24342460 try {
24352461 if (NULL .equals (object )) {
24362462 return NULL ;
@@ -2465,7 +2491,15 @@ public static Object wrap(Object object) {
24652491 || object .getClass ().getClassLoader () == null ) {
24662492 return object .toString ();
24672493 }
2468- return new JSONObject (object );
2494+ if (objectsRecord != null ) {
2495+ return new JSONObject (object , objectsRecord );
2496+ }
2497+ else {
2498+ return new JSONObject (object );
2499+ }
2500+ }
2501+ catch (JSONException exception ) {
2502+ throw exception ;
24692503 } catch (Exception exception ) {
24702504 return null ;
24712505 }
@@ -2676,4 +2710,15 @@ private static JSONException wrongValueFormatException(
26762710 "JSONObject[" + quote (key ) + "] is not a " + valueType + " (" + value + ")."
26772711 , cause );
26782712 }
2713+
2714+ /**
2715+ * Create a new JSONException in a common format for recursive object definition.
2716+ * @param key name of the key
2717+ * @return JSONException that can be thrown.
2718+ */
2719+ private static JSONException recursivelyDefinedObjectException (String key ) {
2720+ return new JSONException (
2721+ "JavaBean object contains recursively defined member variable of key " + quote (key )
2722+ );
2723+ }
26792724}
0 commit comments