mirror of
https://github.com/stleary/JSON-java.git
synced 2025-08-02 11:05:28 -04:00
Merge pull request #857 from XIAYM-gh/master
Implemented custom duplicate key handling (#840)
This commit is contained in:
commit
712859d771
@ -15,17 +15,8 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@ -205,6 +196,21 @@ public class JSONObject {
|
||||
* duplicated key.
|
||||
*/
|
||||
public JSONObject(JSONTokener x) throws JSONException {
|
||||
this(x, new JSONParserConfiguration());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONObject from a JSONTokener with custom json parse configurations.
|
||||
*
|
||||
* @param x
|
||||
* A JSONTokener object containing the source string.
|
||||
* @param jsonParserConfiguration
|
||||
* Variable to pass parser custom configuration for json parsing.
|
||||
* @throws JSONException
|
||||
* If there is a syntax error in the source string or a
|
||||
* duplicated key.
|
||||
*/
|
||||
public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
|
||||
this();
|
||||
char c;
|
||||
String key;
|
||||
@ -234,13 +240,14 @@ public class JSONObject {
|
||||
|
||||
if (key != null) {
|
||||
// Check if key exists
|
||||
if (this.opt(key) != null) {
|
||||
// key already exists
|
||||
boolean keyExists = this.opt(key) != null;
|
||||
if (keyExists && !jsonParserConfiguration.isOverwriteDuplicateKey()) {
|
||||
throw x.syntaxError("Duplicate key \"" + key + "\"");
|
||||
}
|
||||
// Only add value if non-null
|
||||
|
||||
Object value = x.nextValue();
|
||||
if (value!=null) {
|
||||
// Only add value if non-null
|
||||
if (value != null) {
|
||||
this.put(key, value);
|
||||
}
|
||||
}
|
||||
@ -296,7 +303,6 @@ public class JSONObject {
|
||||
|
||||
/**
|
||||
* Construct a JSONObject from a map with recursion depth.
|
||||
*
|
||||
*/
|
||||
private JSONObject(Map<?, ?> m, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
|
||||
if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) {
|
||||
@ -427,7 +433,25 @@ public class JSONObject {
|
||||
* duplicated key.
|
||||
*/
|
||||
public JSONObject(String source) throws JSONException {
|
||||
this(new JSONTokener(source));
|
||||
this(source, new JSONParserConfiguration());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONObject from a source JSON text string with custom json parse configurations.
|
||||
* This is the most commonly used JSONObject constructor.
|
||||
*
|
||||
* @param source
|
||||
* A string beginning with <code>{</code> <small>(left
|
||||
* brace)</small> and ending with <code>}</code>
|
||||
* <small>(right brace)</small>.
|
||||
* @param jsonParserConfiguration
|
||||
* Variable to pass parser custom configuration for json parsing.
|
||||
* @exception JSONException
|
||||
* If there is a syntax error in the source string or a
|
||||
* duplicated key.
|
||||
*/
|
||||
public JSONObject(String source, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
|
||||
this(new JSONTokener(source), jsonParserConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,23 +4,77 @@ package org.json;
|
||||
* Configuration object for the JSON parser. The configuration is immutable.
|
||||
*/
|
||||
public class JSONParserConfiguration extends ParserConfiguration {
|
||||
/**
|
||||
* Used to indicate whether to overwrite duplicate key or not.
|
||||
*/
|
||||
private boolean overwriteDuplicateKey;
|
||||
|
||||
/**
|
||||
* Configuration with the default values.
|
||||
*/
|
||||
public JSONParserConfiguration() {
|
||||
super();
|
||||
}
|
||||
/**
|
||||
* Configuration with the default values.
|
||||
*/
|
||||
public JSONParserConfiguration() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONParserConfiguration clone() {
|
||||
return new JSONParserConfiguration();
|
||||
}
|
||||
/**
|
||||
* Configure the parser with argument overwriteDuplicateKey.
|
||||
*
|
||||
* @param overwriteDuplicateKey Indicate whether to overwrite duplicate key or not.<br>
|
||||
* If not, the JSONParser will throw a {@link JSONException}
|
||||
* when meeting duplicate keys.
|
||||
*/
|
||||
public JSONParserConfiguration(boolean overwriteDuplicateKey) {
|
||||
super();
|
||||
this.overwriteDuplicateKey = overwriteDuplicateKey;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public JSONParserConfiguration withMaxNestingDepth(final int maxNestingDepth) {
|
||||
return super.withMaxNestingDepth(maxNestingDepth);
|
||||
}
|
||||
@Override
|
||||
protected JSONParserConfiguration clone() {
|
||||
JSONParserConfiguration clone = new JSONParserConfiguration(overwriteDuplicateKey);
|
||||
clone.maxNestingDepth = maxNestingDepth;
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the maximum nesting depth that the parser will descend before throwing an exception
|
||||
* when parsing a map into JSONObject or parsing a {@link java.util.Collection} instance into
|
||||
* JSONArray. The default max nesting depth is 512, which means the parser will throw a JsonException
|
||||
* if the maximum depth is reached.
|
||||
*
|
||||
* @param maxNestingDepth the maximum nesting depth allowed to the JSON parser
|
||||
* @return The existing configuration will not be modified. A new configuration is returned.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public JSONParserConfiguration withMaxNestingDepth(final int maxNestingDepth) {
|
||||
JSONParserConfiguration clone = this.clone();
|
||||
clone.maxNestingDepth = maxNestingDepth;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the parser's behavior when meeting duplicate keys.
|
||||
* If set to false, the parser will throw a JSONException when meeting a duplicate key.
|
||||
* Or the duplicate key's value will be overwritten.
|
||||
*
|
||||
* @param overwriteDuplicateKey defines should the parser overwrite duplicate keys.
|
||||
* @return The existing configuration will not be modified. A new configuration is returned.
|
||||
*/
|
||||
public JSONParserConfiguration withOverwriteDuplicateKey(final boolean overwriteDuplicateKey) {
|
||||
JSONParserConfiguration clone = this.clone();
|
||||
clone.overwriteDuplicateKey = overwriteDuplicateKey;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* The parser's behavior when meeting duplicate keys, controls whether the parser should
|
||||
* overwrite duplicate keys or not.
|
||||
*
|
||||
* @return The <code>overwriteDuplicateKey</code> configuration value.
|
||||
*/
|
||||
public boolean isOverwriteDuplicateKey() {
|
||||
return this.overwriteDuplicateKey;
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,12 @@ public class ParserConfiguration {
|
||||
|
||||
/**
|
||||
* Specifies if values should be kept as strings (<code>true</code>), or if
|
||||
* they should try to be guessed into JSON values (numeric, boolean, string)
|
||||
* they should try to be guessed into JSON values (numeric, boolean, string).
|
||||
*/
|
||||
protected boolean keepStrings;
|
||||
|
||||
/**
|
||||
* The maximum nesting depth when parsing a document.
|
||||
* The maximum nesting depth when parsing an object.
|
||||
*/
|
||||
protected int maxNestingDepth;
|
||||
|
||||
@ -59,14 +59,14 @@ public class ParserConfiguration {
|
||||
// map should be cloned as well. If the values of the map are known to also
|
||||
// be immutable, then a shallow clone of the map is acceptable.
|
||||
return new ParserConfiguration(
|
||||
this.keepStrings,
|
||||
this.maxNestingDepth
|
||||
this.keepStrings,
|
||||
this.maxNestingDepth
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* When parsing the XML into JSONML, specifies if values should be kept as strings (<code>true</code>), or if
|
||||
* they should try to be guessed into JSON values (numeric, boolean, string)
|
||||
* they should try to be guessed into JSON values (numeric, boolean, string).
|
||||
*
|
||||
* @return The <code>keepStrings</code> configuration value.
|
||||
*/
|
||||
@ -78,22 +78,21 @@ public class ParserConfiguration {
|
||||
* When parsing the XML into JSONML, specifies if values should be kept as strings (<code>true</code>), or if
|
||||
* they should try to be guessed into JSON values (numeric, boolean, string)
|
||||
*
|
||||
* @param newVal
|
||||
* new value to use for the <code>keepStrings</code> configuration option.
|
||||
* @param <T> the type of the configuration object
|
||||
*
|
||||
* @param newVal new value to use for the <code>keepStrings</code> configuration option.
|
||||
* @param <T> the type of the configuration object
|
||||
* @return The existing configuration will not be modified. A new configuration is returned.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends ParserConfiguration> T withKeepStrings(final boolean newVal) {
|
||||
T newConfig = (T)this.clone();
|
||||
T newConfig = (T) this.clone();
|
||||
newConfig.keepStrings = newVal;
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum nesting depth that the parser will descend before throwing an exception
|
||||
* when parsing the XML into JSONML.
|
||||
* when parsing an object (e.g. Map, Collection) into JSON-related objects.
|
||||
*
|
||||
* @return the maximum nesting depth set for this configuration
|
||||
*/
|
||||
public int getMaxNestingDepth() {
|
||||
@ -102,18 +101,19 @@ public class ParserConfiguration {
|
||||
|
||||
/**
|
||||
* Defines the maximum nesting depth that the parser will descend before throwing an exception
|
||||
* when parsing the XML into JSONML. The default max nesting depth is 512, which means the parser
|
||||
* will throw a JsonException if the maximum depth is reached.
|
||||
* when parsing an object (e.g. Map, Collection) into JSON-related objects.
|
||||
* The default max nesting depth is 512, which means the parser will throw a JsonException if
|
||||
* the maximum depth is reached.
|
||||
* Using any negative value as a parameter is equivalent to setting no limit to the nesting depth,
|
||||
* which means the parses will go as deep as the maximum call stack size allows.
|
||||
*
|
||||
* @param maxNestingDepth the maximum nesting depth allowed to the XML parser
|
||||
* @param <T> the type of the configuration object
|
||||
*
|
||||
* @param <T> the type of the configuration object
|
||||
* @return The existing configuration will not be modified. A new configuration is returned.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends ParserConfiguration> T withMaxNestingDepth(int maxNestingDepth) {
|
||||
T newConfig = (T)this.clone();
|
||||
T newConfig = (T) this.clone();
|
||||
|
||||
if (maxNestingDepth > UNDEFINED_MAXIMUM_NESTING_DEPTH) {
|
||||
newConfig.maxNestingDepth = maxNestingDepth;
|
||||
|
@ -0,0 +1,45 @@
|
||||
package org.json.junit;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONParserConfiguration;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class JSONParserConfigurationTest {
|
||||
private static final String TEST_SOURCE = "{\"key\": \"value1\", \"key\": \"value2\"}";
|
||||
|
||||
@Test(expected = JSONException.class)
|
||||
public void testThrowException() {
|
||||
new JSONObject(TEST_SOURCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverwrite() {
|
||||
JSONObject jsonObject = new JSONObject(TEST_SOURCE, new JSONParserConfiguration(true));
|
||||
|
||||
assertEquals("duplicate key should be overwritten", "value2", jsonObject.getString("key"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyDuplicateKeyThenMaxDepth() {
|
||||
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration()
|
||||
.withOverwriteDuplicateKey(true)
|
||||
.withMaxNestingDepth(42);
|
||||
|
||||
assertEquals(42, jsonParserConfiguration.getMaxNestingDepth());
|
||||
assertTrue(jsonParserConfiguration.isOverwriteDuplicateKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyMaxDepthThenDuplicateKey() {
|
||||
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration()
|
||||
.withMaxNestingDepth(42)
|
||||
.withOverwriteDuplicateKey(true);
|
||||
|
||||
assertTrue(jsonParserConfiguration.isOverwriteDuplicateKey());
|
||||
assertEquals(42, jsonParserConfiguration.getMaxNestingDepth());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user