Adding JSONParserConfiguration for configuring the depth of nested maps

This commit is contained in:
sk02241994 2023-12-22 15:44:33 +05:30
parent dcac3bc18e
commit abea194120
5 changed files with 115 additions and 34 deletions

View File

@ -149,18 +149,22 @@ public class JSONArray implements Iterable<Object> {
* A Collection. * A Collection.
*/ */
public JSONArray(Collection<?> collection) { public JSONArray(Collection<?> collection) {
this(collection, 0); this(collection, 0, new JSONParserConfiguration());
} }
protected JSONArray(Collection<?> collection, int recursionDepth) { public JSONArray(Collection<?> collection, JSONParserConfiguration jsonParserConfiguration) {
if (recursionDepth > JSONObject.RECURSION_DEPTH_LIMIT) { this(collection, 0, jsonParserConfiguration);
throw new JSONException("JSONArray has reached recursion depth limit of " + JSONObject.RECURSION_DEPTH_LIMIT); }
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) { if (collection == null) {
this.myArrayList = new ArrayList<Object>(); this.myArrayList = new ArrayList<Object>();
} else { } else {
this.myArrayList = new ArrayList<Object>(collection.size()); this.myArrayList = new ArrayList<Object>(collection.size());
this.addAll(collection, true, recursionDepth); this.addAll(collection, true, recursionDepth, jsonParserConfiguration);
} }
} }
@ -1345,7 +1349,27 @@ public class JSONArray implements Iterable<Object> {
* If a key in the map is <code>null</code> * If a key in the map is <code>null</code>
*/ */
public JSONArray put(int index, Map<?, ?> value) throws JSONException { 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; return this;
} }
@ -1790,11 +1814,11 @@ public class JSONArray implements Iterable<Object> {
* variable to keep the count of how nested the object creation is happening. * 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()); this.myArrayList.ensureCapacity(this.myArrayList.size() + collection.size());
if (wrap) { if (wrap) {
for (Object o: collection){ for (Object o: collection){
this.put(JSONObject.wrap(o, recursionDepth + 1)); this.put(JSONObject.wrap(o, recursionDepth + 1, jsonParserConfiguration));
} }
} else { } else {
for (Object o: collection){ for (Object o: collection){
@ -1823,7 +1847,14 @@ public class JSONArray implements Iterable<Object> {
} }
} }
} }
/**
* Add an array's elements to the JSONArray.
*
* @param array
* @param wrap
* @throws JSONException
*/
private void addAll(Object array, boolean wrap) throws JSONException { private void addAll(Object array, boolean wrap) throws JSONException {
this.addAll(array, wrap, 0); this.addAll(array, wrap, 0);
} }
@ -1836,23 +1867,37 @@ public class JSONArray implements Iterable<Object> {
* JSONArray, Collection, or Iterable, an exception will be * JSONArray, Collection, or Iterable, an exception will be
* thrown. * thrown.
* @param wrap * @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 true} to call {@link JSONObject#wrap(Object)} for each item,
* {@code false} to add the items directly * {@code false} to add the items directly
* @param recursionDepth * @param recursionDepth
* Variable to keep the count of how nested the object creation is happening. * 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 * @throws JSONException
* If not an array or if an array value is non-finite number. * If not an array or if an array value is non-finite number.
* @throws NullPointerException * @throws NullPointerException
* Thrown if the array parameter is null. * 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()) { if (array.getClass().isArray()) {
int length = Array.getLength(array); int length = Array.getLength(array);
this.myArrayList.ensureCapacity(this.myArrayList.size() + length); this.myArrayList.ensureCapacity(this.myArrayList.size() + length);
if (wrap) { if (wrap) {
for (int i = 0; i < length; i += 1) { 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 { } else {
for (int i = 0; i < length; i += 1) { for (int i = 0; i < length; i += 1) {

View File

@ -147,7 +147,6 @@ public class JSONObject {
* The map where the JSONObject's properties are kept. * The map where the JSONObject's properties are kept.
*/ */
private final Map<String, Object> map; private final Map<String, Object> map;
public static final int RECURSION_DEPTH_LIMIT = 1000;
public Class<? extends Map> getMapType() { public Class<? extends Map> getMapType() {
return map.getClass(); return map.getClass();
@ -277,16 +276,20 @@ public class JSONObject {
* If a key in the map is <code>null</code> * If a key in the map is <code>null</code>
*/ */
public JSONObject(Map<?, ?> m) { 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. * Construct a JSONObject from a map with recursion depth.
* *
*/ */
protected JSONObject(Map<?, ?> m, int recursionDepth) { protected JSONObject(Map<?, ?> m, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
if (recursionDepth > RECURSION_DEPTH_LIMIT) { if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) {
throw new JSONException("JSONObject has reached recursion depth limit of " + RECURSION_DEPTH_LIMIT); throw new JSONException("JSONObject has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth());
} }
if (m == null) { if (m == null) {
this.map = new HashMap<String, Object>(); this.map = new HashMap<String, Object>();
@ -299,7 +302,7 @@ public class JSONObject {
final Object value = e.getValue(); final Object value = e.getValue();
if (value != null) { if (value != null) {
testValidity(value); 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));
} }
} }
} }
@ -2578,15 +2581,15 @@ public class JSONObject {
return wrap(object, null); return wrap(object, null);
} }
public static Object wrap(Object object, int recursionDepth) { public static Object wrap(Object object, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
return wrap(object, null, recursionDepth); return wrap(object, null, recursionDepth, jsonParserConfiguration);
} }
private static Object wrap(Object object, Set<Object> objectsRecord) { 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 { try {
if (NULL.equals(object)) { if (NULL.equals(object)) {
return NULL; return NULL;
@ -2604,14 +2607,14 @@ public class JSONObject {
if (object instanceof Collection) { if (object instanceof Collection) {
Collection<?> coll = (Collection<?>) object; Collection<?> coll = (Collection<?>) object;
return new JSONArray(coll, recursionDepth); return new JSONArray(coll, recursionDepth, jsonParserConfiguration);
} }
if (object.getClass().isArray()) { if (object.getClass().isArray()) {
return new JSONArray(object); return new JSONArray(object);
} }
if (object instanceof Map) { if (object instanceof Map) {
Map<?, ?> map = (Map<?, ?>) object; Map<?, ?> map = (Map<?, ?>) object;
return new JSONObject(map, recursionDepth); return new JSONObject(map, recursionDepth, jsonParserConfiguration);
} }
Package objectPackage = object.getClass().getPackage(); Package objectPackage = object.getClass().getPackage();
String objectPackageName = objectPackage != null ? objectPackage String objectPackageName = objectPackage != null ? objectPackage

View File

@ -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);
}
}

View File

@ -28,6 +28,7 @@ import java.util.Map;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONParserConfiguration;
import org.json.JSONPointerException; import org.json.JSONPointerException;
import org.json.JSONString; import org.json.JSONString;
import org.json.JSONTokener; import org.json.JSONTokener;
@ -1440,15 +1441,15 @@ public class JSONArrayTest {
} }
@Test @Test
public void testRecursiveDepthAtPosition999Object() { public void testRecursiveDepthAtPositionDefaultObject() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(999); HashMap<String, Object> map = JSONObjectTest.buildNestedMap(JSONParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
new JSONArray().put(0, map); new JSONArray().put(0, map);
} }
@Test @Test
public void testRecursiveDepthAtPosition1000Object() { public void testRecursiveDepthAtPosition1000Object() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(1000); HashMap<String, Object> map = JSONObjectTest.buildNestedMap(1000);
new JSONArray().put(0, map); new JSONArray().put(0, map, new JSONParserConfiguration(1000));
} }
@Test(expected = JSONException.class) @Test(expected = JSONException.class)
@ -1465,15 +1466,16 @@ public class JSONArrayTest {
} }
@Test @Test
public void testRecursiveDepthArrayFor999Levels() { public void testRecursiveDepthArrayForDefaultLevels() {
ArrayList<Object> array = buildNestedArray(999); ArrayList<Object> array = buildNestedArray(JSONParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
new JSONArray(array); new JSONArray(array, new JSONParserConfiguration());
} }
@Test @Test
public void testRecursiveDepthArrayFor1000Levels() { public void testRecursiveDepthArrayFor1000Levels() {
ArrayList<Object> array = buildNestedArray(1000); ArrayList<Object> array = buildNestedArray(1000);
new JSONArray(array); JSONParserConfiguration parserConfiguration = new JSONParserConfiguration(1000);
new JSONArray(array, parserConfiguration);
} }
@Test(expected = JSONException.class) @Test(expected = JSONException.class)

View File

@ -32,6 +32,7 @@ import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONPointerException; import org.json.JSONPointerException;
import org.json.JSONParserConfiguration;
import org.json.JSONString; import org.json.JSONString;
import org.json.JSONTokener; import org.json.JSONTokener;
import org.json.XML; import org.json.XML;
@ -3737,8 +3738,8 @@ public class JSONObjectTest {
} }
@Test @Test
public void issue743SerializationMapWith999Objects() { public void issue743SerializationMapWith512Objects() {
HashMap<String, Object> map = buildNestedMap(999); HashMap<String, Object> map = buildNestedMap(JSONParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
JSONObject object = new JSONObject(map); JSONObject object = new JSONObject(map);
String jsonString = object.toString(); String jsonString = object.toString();
} }
@ -3746,7 +3747,8 @@ public class JSONObjectTest {
@Test @Test
public void issue743SerializationMapWith1000Objects() { public void issue743SerializationMapWith1000Objects() {
HashMap<String, Object> map = buildNestedMap(1000); 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(); String jsonString = object.toString();
} }