This commit is contained in:
marilynel 2025-03-23 10:27:57 -07:00
commit 45ec164faa
5 changed files with 95 additions and 1 deletions

View File

@ -332,7 +332,7 @@ public class JSONObject {
throw new NullPointerException("Null key."); throw new NullPointerException("Null key.");
} }
final Object value = e.getValue(); final Object value = e.getValue();
if (value != null) { if (value != null || jsonParserConfiguration.isUseNativeNulls()) {
testValidity(value); testValidity(value);
this.map.put(String.valueOf(e.getKey()), wrap(value, recursionDepth + 1, jsonParserConfiguration)); this.map.put(String.valueOf(e.getKey()), wrap(value, recursionDepth + 1, jsonParserConfiguration));
} }

View File

@ -9,6 +9,11 @@ public class JSONParserConfiguration extends ParserConfiguration {
*/ */
private boolean overwriteDuplicateKey; private boolean overwriteDuplicateKey;
/**
* Used to indicate whether to convert java null values to JSONObject.NULL or ignoring the entry when converting java maps.
*/
private boolean useNativeNulls;
/** /**
* Configuration with the default values. * Configuration with the default values.
*/ */
@ -32,6 +37,7 @@ public class JSONParserConfiguration extends ParserConfiguration {
clone.strictMode = strictMode; clone.strictMode = strictMode;
clone.maxNestingDepth = maxNestingDepth; clone.maxNestingDepth = maxNestingDepth;
clone.keepStrings = keepStrings; clone.keepStrings = keepStrings;
clone.useNativeNulls = useNativeNulls;
return clone; return clone;
} }
@ -68,6 +74,21 @@ public class JSONParserConfiguration extends ParserConfiguration {
return clone; return clone;
} }
/**
* Controls the parser's behavior when meeting Java null values while converting maps.
* If set to true, the parser will put a JSONObject.NULL into the resulting JSONObject.
* Or the map entry will be ignored.
*
* @param useNativeNulls defines if the parser should convert null values in Java maps
* @return The existing configuration will not be modified. A new configuration is returned.
*/
public JSONParserConfiguration withUseNativeNulls(final boolean useNativeNulls) {
JSONParserConfiguration clone = this.clone();
clone.useNativeNulls = useNativeNulls;
return clone;
}
/** /**
* Sets the strict mode configuration for the JSON parser with default true value * Sets the strict mode configuration for the JSON parser with default true value
* <p> * <p>
@ -107,6 +128,18 @@ public class JSONParserConfiguration extends ParserConfiguration {
return this.overwriteDuplicateKey; return this.overwriteDuplicateKey;
} }
/**
* The parser's behavior when meeting a null value in a java map, controls whether the parser should
* write a JSON entry with a null value (<code>isUseNativeNulls() == true</code>)
* or ignore that map entry (<code>isUseNativeNulls() == false</code>).
*
* @return The <code>useNativeNulls</code> configuration value.
*/
public boolean isUseNativeNulls() {
return this.useNativeNulls;
}
/** /**
* The parser throws an Exception when strict mode is true and tries to parse invalid JSON characters. * The parser throws an Exception when strict mode is true and tries to parse invalid JSON characters.
* Otherwise, the parser is more relaxed and might tolerate some invalid characters. * Otherwise, the parser is more relaxed and might tolerate some invalid characters.

View File

@ -229,6 +229,19 @@ public class JSONArrayTest {
Util.checkJSONArrayMaps(jaInt); Util.checkJSONArrayMaps(jaInt);
} }
@Test
public void jsonArrayByListWithNestedNullValue() {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Map<String, Object> sub = new HashMap<String, Object>();
sub.put("nullKey", null);
list.add(sub);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withUseNativeNulls(true);
JSONArray jsonArray = new JSONArray(list, parserConfiguration);
JSONObject subObject = jsonArray.getJSONObject(0);
assertTrue(subObject.has("nullKey"));
assertEquals(JSONObject.NULL, subObject.get("nullKey"));
}
/** /**
* Tests consecutive calls to putAll with array and collection. * Tests consecutive calls to putAll with array and collection.
*/ */

View File

@ -617,6 +617,46 @@ public class JSONObjectTest {
Util.checkJSONObjectMaps(jsonObject); Util.checkJSONObjectMaps(jsonObject);
} }
@Test
public void jsonObjectByMapWithNullValueAndParserConfiguration() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("nullKey", null);
// by default, null values are ignored
JSONObject obj1 = new JSONObject(map);
assertTrue("expected null value to be ignored by default", obj1.isEmpty());
// if configured, null values are written as such into the JSONObject.
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withUseNativeNulls(true);
JSONObject obj2 = new JSONObject(map, parserConfiguration);
assertFalse("expected null value to accepted when configured", obj2.isEmpty());
assertTrue(obj2.has("nullKey"));
assertEquals(JSONObject.NULL, obj2.get("nullKey"));
}
@Test
public void jsonObjectByMapWithNestedNullValueAndParserConfiguration() {
Map<String, Object> map = new HashMap<String, Object>();
Map<String, Object> nestedMap = new HashMap<String, Object>();
nestedMap.put("nullKey", null);
map.put("nestedMap", nestedMap);
List<Map<String, Object>> nestedList = new ArrayList<Map<String,Object>>();
nestedList.add(nestedMap);
map.put("nestedList", nestedList);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withUseNativeNulls(true);
JSONObject jsonObject = new JSONObject(map, parserConfiguration);
JSONObject nestedObject = jsonObject.getJSONObject("nestedMap");
assertTrue(nestedObject.has("nullKey"));
assertEquals(JSONObject.NULL, nestedObject.get("nullKey"));
JSONArray nestedArray = jsonObject.getJSONArray("nestedList");
assertEquals(1, nestedArray.length());
assertTrue(nestedArray.getJSONObject(0).has("nullKey"));
assertEquals(JSONObject.NULL, nestedArray.getJSONObject(0).get("nullKey"));
}
/** /**
* JSONObject built from a bean. In this case all but one of the * JSONObject built from a bean. In this case all but one of the
* bean getters return valid JSON types * bean getters return valid JSON types

View File

@ -54,6 +54,14 @@ public class JSONParserConfigurationTest {
assertTrue(jsonParserConfiguration.isKeepStrings()); assertTrue(jsonParserConfiguration.isKeepStrings());
} }
@Test
public void useNativeNullsIsCloned() {
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration()
.withUseNativeNulls(true)
.withStrictMode(true);
assertTrue(jsonParserConfiguration.isUseNativeNulls());
}
@Test @Test
public void verifyDuplicateKeyThenMaxDepth() { public void verifyDuplicateKeyThenMaxDepth() {
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration() JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration()