Skip to content

Commit d4c5136

Browse files
authored
Merge pull request #961 from effad/master
Option to store null value in JSONObject when parsing a map
2 parents 7751b39 + fd0cca3 commit d4c5136

File tree

5 files changed

+95
-1
lines changed

5 files changed

+95
-1
lines changed

src/main/java/org/json/JSONObject.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ private JSONObject(Map<?, ?> m, int recursionDepth, JSONParserConfiguration json
332332
throw new NullPointerException("Null key.");
333333
}
334334
final Object value = e.getValue();
335-
if (value != null) {
335+
if (value != null || jsonParserConfiguration.isUseNativeNulls()) {
336336
testValidity(value);
337337
this.map.put(String.valueOf(e.getKey()), wrap(value, recursionDepth + 1, jsonParserConfiguration));
338338
}

src/main/java/org/json/JSONParserConfiguration.java

+33
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ public class JSONParserConfiguration extends ParserConfiguration {
88
* Used to indicate whether to overwrite duplicate key or not.
99
*/
1010
private boolean overwriteDuplicateKey;
11+
12+
/**
13+
* Used to indicate whether to convert java null values to JSONObject.NULL or ignoring the entry when converting java maps.
14+
*/
15+
private boolean useNativeNulls;
1116

1217
/**
1318
* Configuration with the default values.
@@ -32,6 +37,7 @@ protected JSONParserConfiguration clone() {
3237
clone.strictMode = strictMode;
3338
clone.maxNestingDepth = maxNestingDepth;
3439
clone.keepStrings = keepStrings;
40+
clone.useNativeNulls = useNativeNulls;
3541
return clone;
3642
}
3743

@@ -67,6 +73,21 @@ public JSONParserConfiguration withOverwriteDuplicateKey(final boolean overwrite
6773

6874
return clone;
6975
}
76+
77+
/**
78+
* Controls the parser's behavior when meeting Java null values while converting maps.
79+
* If set to true, the parser will put a JSONObject.NULL into the resulting JSONObject.
80+
* Or the map entry will be ignored.
81+
*
82+
* @param useNativeNulls defines if the parser should convert null values in Java maps
83+
* @return The existing configuration will not be modified. A new configuration is returned.
84+
*/
85+
public JSONParserConfiguration withUseNativeNulls(final boolean useNativeNulls) {
86+
JSONParserConfiguration clone = this.clone();
87+
clone.useNativeNulls = useNativeNulls;
88+
89+
return clone;
90+
}
7091

7192
/**
7293
* Sets the strict mode configuration for the JSON parser with default true value
@@ -106,6 +127,18 @@ public JSONParserConfiguration withStrictMode(final boolean mode) {
106127
public boolean isOverwriteDuplicateKey() {
107128
return this.overwriteDuplicateKey;
108129
}
130+
131+
/**
132+
* The parser's behavior when meeting a null value in a java map, controls whether the parser should
133+
* write a JSON entry with a null value (<code>isUseNativeNulls() == true</code>)
134+
* or ignore that map entry (<code>isUseNativeNulls() == false</code>).
135+
*
136+
* @return The <code>useNativeNulls</code> configuration value.
137+
*/
138+
public boolean isUseNativeNulls() {
139+
return this.useNativeNulls;
140+
}
141+
109142

110143
/**
111144
* The parser throws an Exception when strict mode is true and tries to parse invalid JSON characters.

src/test/java/org/json/junit/JSONArrayTest.java

+13
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,19 @@ public void verifyConstructor() {
228228
Util.checkJSONArrayMaps(jaRaw);
229229
Util.checkJSONArrayMaps(jaInt);
230230
}
231+
232+
@Test
233+
public void jsonArrayByListWithNestedNullValue() {
234+
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
235+
Map<String, Object> sub = new HashMap<String, Object>();
236+
sub.put("nullKey", null);
237+
list.add(sub);
238+
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withUseNativeNulls(true);
239+
JSONArray jsonArray = new JSONArray(list, parserConfiguration);
240+
JSONObject subObject = jsonArray.getJSONObject(0);
241+
assertTrue(subObject.has("nullKey"));
242+
assertEquals(JSONObject.NULL, subObject.get("nullKey"));
243+
}
231244

232245
/**
233246
* Tests consecutive calls to putAll with array and collection.

src/test/java/org/json/junit/JSONObjectTest.java

+40
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,46 @@ public void jsonObjectByMapWithNullValue() {
616616
assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(jsonObject.query("/doubleKey")));
617617
Util.checkJSONObjectMaps(jsonObject);
618618
}
619+
620+
@Test
621+
public void jsonObjectByMapWithNullValueAndParserConfiguration() {
622+
Map<String, Object> map = new HashMap<String, Object>();
623+
map.put("nullKey", null);
624+
625+
// by default, null values are ignored
626+
JSONObject obj1 = new JSONObject(map);
627+
assertTrue("expected null value to be ignored by default", obj1.isEmpty());
628+
629+
// if configured, null values are written as such into the JSONObject.
630+
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withUseNativeNulls(true);
631+
JSONObject obj2 = new JSONObject(map, parserConfiguration);
632+
assertFalse("expected null value to accepted when configured", obj2.isEmpty());
633+
assertTrue(obj2.has("nullKey"));
634+
assertEquals(JSONObject.NULL, obj2.get("nullKey"));
635+
}
636+
637+
@Test
638+
public void jsonObjectByMapWithNestedNullValueAndParserConfiguration() {
639+
Map<String, Object> map = new HashMap<String, Object>();
640+
Map<String, Object> nestedMap = new HashMap<String, Object>();
641+
nestedMap.put("nullKey", null);
642+
map.put("nestedMap", nestedMap);
643+
List<Map<String, Object>> nestedList = new ArrayList<Map<String,Object>>();
644+
nestedList.add(nestedMap);
645+
map.put("nestedList", nestedList);
646+
647+
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withUseNativeNulls(true);
648+
JSONObject jsonObject = new JSONObject(map, parserConfiguration);
649+
650+
JSONObject nestedObject = jsonObject.getJSONObject("nestedMap");
651+
assertTrue(nestedObject.has("nullKey"));
652+
assertEquals(JSONObject.NULL, nestedObject.get("nullKey"));
653+
654+
JSONArray nestedArray = jsonObject.getJSONArray("nestedList");
655+
assertEquals(1, nestedArray.length());
656+
assertTrue(nestedArray.getJSONObject(0).has("nullKey"));
657+
assertEquals(JSONObject.NULL, nestedArray.getJSONObject(0).get("nullKey"));
658+
}
619659

620660
/**
621661
* JSONObject built from a bean. In this case all but one of the

src/test/java/org/json/junit/JSONParserConfigurationTest.java

+8
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ public void maxNestingDepthIsCloned(){
5353

5454
assertTrue(jsonParserConfiguration.isKeepStrings());
5555
}
56+
57+
@Test
58+
public void useNativeNullsIsCloned() {
59+
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration()
60+
.withUseNativeNulls(true)
61+
.withStrictMode(true);
62+
assertTrue(jsonParserConfiguration.isUseNativeNulls());
63+
}
5664

5765
@Test
5866
public void verifyDuplicateKeyThenMaxDepth() {

0 commit comments

Comments
 (0)