Skip to content

Commit

Permalink
Adding JSONParserConfiguration for configuring the depth of nested maps
Browse files Browse the repository at this point in the history
  • Loading branch information
sk02241994 committed Dec 22, 2023
1 parent 5518282 commit d493fee
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 34 deletions.
69 changes: 57 additions & 12 deletions src/main/java/org/json/JSONArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,22 @@ public JSONArray(String source) throws JSONException {
* A Collection.
*/
public JSONArray(Collection<?> collection) {
this(collection, 0);
this(collection, 0, new JSONParserConfiguration());
}

protected JSONArray(Collection<?> collection, int recursionDepth) {
if (recursionDepth > JSONObject.RECURSION_DEPTH_LIMIT) {
throw new JSONException("JSONArray has reached recursion depth limit of " + JSONObject.RECURSION_DEPTH_LIMIT);
public JSONArray(Collection<?> collection, JSONParserConfiguration jsonParserConfiguration) {
this(collection, 0, jsonParserConfiguration);
}

protected JSONArray(Collection<?> collection, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) {
throw new JSONException("JSONArray has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth());
}
if (collection == null) {
this.myArrayList = new ArrayList<Object>();
} else {
this.myArrayList = new ArrayList<Object>(collection.size());
this.addAll(collection, true, recursionDepth);
this.addAll(collection, true, recursionDepth, jsonParserConfiguration);
}
}

Expand Down Expand Up @@ -1345,7 +1349,27 @@ public JSONArray put(int index, long value) throws JSONException {
* If a key in the map is <code>null</code>
*/
public JSONArray put(int index, Map<?, ?> value) throws JSONException {
this.put(index, new JSONObject(value));
this.put(index, new JSONObject(value, new JSONParserConfiguration()));
return this;
}

/**
* Put a value in the JSONArray, where the value will be a JSONObject that
* is produced from a Map.
*
* @param index
* The subscript
* @param value
* The Map value.
* @param jsonParserConfiguration
* Configuration for recursive depth
* @return
* @throws JSONException
* If the index is negative or if the value is an invalid
* number.
*/
public JSONArray put(int index, Map<?, ?> value, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
this.put(index, new JSONObject(value, jsonParserConfiguration));
return this;
}

Expand Down Expand Up @@ -1790,11 +1814,11 @@ public boolean isEmpty() {
* variable to keep the count of how nested the object creation is happening.
*
*/
private void addAll(Collection<?> collection, boolean wrap, int recursionDepth) {
private void addAll(Collection<?> collection, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
this.myArrayList.ensureCapacity(this.myArrayList.size() + collection.size());
if (wrap) {
for (Object o: collection){
this.put(JSONObject.wrap(o, recursionDepth + 1));
this.put(JSONObject.wrap(o, recursionDepth + 1, jsonParserConfiguration));
}
} else {
for (Object o: collection){
Expand Down Expand Up @@ -1823,7 +1847,14 @@ private void addAll(Iterable<?> iter, boolean wrap) {
}
}
}


/**
* Add an array's elements to the JSONArray.
*
* @param array
* @param wrap
* @throws JSONException
*/
private void addAll(Object array, boolean wrap) throws JSONException {
this.addAll(array, wrap, 0);
}
Expand All @@ -1836,23 +1867,37 @@ private void addAll(Object array, boolean wrap) throws JSONException {
* JSONArray, Collection, or Iterable, an exception will be
* thrown.
* @param wrap
* @param recursionDepth
*/
private void addAll(Object array, boolean wrap, int recursionDepth) {
addAll(array, wrap, recursionDepth, new JSONParserConfiguration());
}
/**
* Add an array's elements to the JSONArray.
*`
* @param array
* Array. If the parameter passed is null, or not an array,
* JSONArray, Collection, or Iterable, an exception will be
* thrown.
* @param wrap
* {@code true} to call {@link JSONObject#wrap(Object)} for each item,
* {@code false} to add the items directly
* @param recursionDepth
* Variable to keep the count of how nested the object creation is happening.
*
* @param recursionDepth
* Variable to pass parser custom configuration for json parsing.
* @throws JSONException
* If not an array or if an array value is non-finite number.
* @throws NullPointerException
* Thrown if the array parameter is null.
*/
private void addAll(Object array, boolean wrap, int recursionDepth) throws JSONException {
private void addAll(Object array, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
if (array.getClass().isArray()) {
int length = Array.getLength(array);
this.myArrayList.ensureCapacity(this.myArrayList.size() + length);
if (wrap) {
for (int i = 0; i < length; i += 1) {
this.put(JSONObject.wrap(Array.get(array, i), recursionDepth + 1));
this.put(JSONObject.wrap(Array.get(array, i), recursionDepth + 1, jsonParserConfiguration));
}
} else {
for (int i = 0; i < length; i += 1) {
Expand Down
27 changes: 15 additions & 12 deletions src/main/java/org/json/JSONObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ public String toString() {
* The map where the JSONObject's properties are kept.
*/
private final Map<String, Object> map;
public static final int RECURSION_DEPTH_LIMIT = 1000;

public Class<? extends Map> getMapType() {
return map.getClass();
Expand Down Expand Up @@ -277,16 +276,20 @@ public JSONObject(JSONTokener x) throws JSONException {
* If a key in the map is <code>null</code>
*/
public JSONObject(Map<?, ?> m) {
this(m, 0);
this(m, 0, new JSONParserConfiguration());
}

public JSONObject(Map<?, ?> m, JSONParserConfiguration jsonParserConfiguration) {
this(m, 0, jsonParserConfiguration);
}

/**
* Construct a JSONObject from a map with recursion depth.
*
*/
protected JSONObject(Map<?, ?> m, int recursionDepth) {
if (recursionDepth > RECURSION_DEPTH_LIMIT) {
throw new JSONException("JSONObject has reached recursion depth limit of " + RECURSION_DEPTH_LIMIT);
protected JSONObject(Map<?, ?> m, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) {
throw new JSONException("JSONObject has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth());
}
if (m == null) {
this.map = new HashMap<String, Object>();
Expand All @@ -299,7 +302,7 @@ protected JSONObject(Map<?, ?> m, int recursionDepth) {
final Object value = e.getValue();
if (value != null) {
testValidity(value);
this.map.put(String.valueOf(e.getKey()), wrap(value, recursionDepth + 1));
this.map.put(String.valueOf(e.getKey()), wrap(value, recursionDepth + 1, jsonParserConfiguration));
}
}
}
Expand Down Expand Up @@ -2578,15 +2581,15 @@ public static Object wrap(Object object) {
return wrap(object, null);
}

public static Object wrap(Object object, int recursionDepth) {
return wrap(object, null, recursionDepth);
public static Object wrap(Object object, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
return wrap(object, null, recursionDepth, jsonParserConfiguration);
}

private static Object wrap(Object object, Set<Object> objectsRecord) {
return wrap(object, objectsRecord, 0);
return wrap(object, objectsRecord, 0, new JSONParserConfiguration());
}

private static Object wrap(Object object, Set<Object> objectsRecord, int recursionDepth) {
private static Object wrap(Object object, Set<Object> objectsRecord, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
try {
if (NULL.equals(object)) {
return NULL;
Expand All @@ -2604,14 +2607,14 @@ private static Object wrap(Object object, Set<Object> objectsRecord, int recursi

if (object instanceof Collection) {
Collection<?> coll = (Collection<?>) object;
return new JSONArray(coll, recursionDepth);
return new JSONArray(coll, recursionDepth, jsonParserConfiguration);
}
if (object.getClass().isArray()) {
return new JSONArray(object);
}
if (object instanceof Map) {
Map<?, ?> map = (Map<?, ?>) object;
return new JSONObject(map, recursionDepth);
return new JSONObject(map, recursionDepth, jsonParserConfiguration);
}
Package objectPackage = object.getClass().getPackage();
String objectPackageName = objectPackage != null ? objectPackage
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/org/json/JSONParserConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.json;

/**
* Configuration object for the JSON parser. The configuration is immutable.
*/
public class JSONParserConfiguration extends ParserConfiguration {

/**
* We can override the default maximum nesting depth if needed.
*/
public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH;

/**
* Configuration with the default values.
*/
public JSONParserConfiguration() {
this.maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH;
}

public JSONParserConfiguration(int maxNestingDepth) {
this.maxNestingDepth = maxNestingDepth;
}

@Override
protected JSONParserConfiguration clone() {
return new JSONParserConfiguration(DEFAULT_MAXIMUM_NESTING_DEPTH);
}

}
16 changes: 9 additions & 7 deletions src/test/java/org/json/junit/JSONArrayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONParserConfiguration;
import org.json.JSONPointerException;
import org.json.JSONString;
import org.json.JSONTokener;
Expand Down Expand Up @@ -1440,15 +1441,15 @@ public void testRecursiveDepthArray() {
}

@Test
public void testRecursiveDepthAtPosition999Object() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(999);
public void testRecursiveDepthAtPositionDefaultObject() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(JSONParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
new JSONArray().put(0, map);
}

@Test
public void testRecursiveDepthAtPosition1000Object() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(1000);
new JSONArray().put(0, map);
new JSONArray().put(0, map, new JSONParserConfiguration(1000));
}

@Test(expected = JSONException.class)
Expand All @@ -1465,15 +1466,16 @@ public void testRecursiveDepthArrayLimitedMaps() {
}

@Test
public void testRecursiveDepthArrayFor999Levels() {
ArrayList<Object> array = buildNestedArray(999);
new JSONArray(array);
public void testRecursiveDepthArrayForDefaultLevels() {
ArrayList<Object> array = buildNestedArray(JSONParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
new JSONArray(array, new JSONParserConfiguration());
}

@Test
public void testRecursiveDepthArrayFor1000Levels() {
ArrayList<Object> array = buildNestedArray(1000);
new JSONArray(array);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration(1000);
new JSONArray(array, parserConfiguration);
}

@Test(expected = JSONException.class)
Expand Down
8 changes: 5 additions & 3 deletions src/test/java/org/json/junit/JSONObjectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONPointerException;
import org.json.JSONParserConfiguration;
import org.json.JSONString;
import org.json.JSONTokener;
import org.json.XML;
Expand Down Expand Up @@ -3737,16 +3738,17 @@ public void testCircularReferenceMultipleLevel() {
}

@Test
public void issue743SerializationMapWith999Objects() {
HashMap<String, Object> map = buildNestedMap(999);
public void issue743SerializationMapWith512Objects() {
HashMap<String, Object> map = buildNestedMap(JSONParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
JSONObject object = new JSONObject(map);
String jsonString = object.toString();
}

@Test
public void issue743SerializationMapWith1000Objects() {
HashMap<String, Object> map = buildNestedMap(1000);
JSONObject object = new JSONObject(map);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration(1000);
JSONObject object = new JSONObject(map, parserConfiguration);
String jsonString = object.toString();
}

Expand Down

0 comments on commit d493fee

Please sign in to comment.