diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index d50fff7..a1664f7 100644 --- a/src/main/java/org/json/JSONObject.java +++ b/src/main/java/org/json/JSONObject.java @@ -332,7 +332,7 @@ public class JSONObject { throw new NullPointerException("Null key."); } final Object value = e.getValue(); - if (value != null) { + if (value != null || jsonParserConfiguration.isUseNativeNulls()) { testValidity(value); this.map.put(String.valueOf(e.getKey()), wrap(value, recursionDepth + 1, jsonParserConfiguration)); } diff --git a/src/main/java/org/json/JSONParserConfiguration.java b/src/main/java/org/json/JSONParserConfiguration.java index 3fbfb8a..0cfa2ea 100644 --- a/src/main/java/org/json/JSONParserConfiguration.java +++ b/src/main/java/org/json/JSONParserConfiguration.java @@ -8,6 +8,11 @@ public class JSONParserConfiguration extends ParserConfiguration { * Used to indicate whether to overwrite duplicate key or not. */ 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. @@ -32,6 +37,7 @@ public class JSONParserConfiguration extends ParserConfiguration { clone.strictMode = strictMode; clone.maxNestingDepth = maxNestingDepth; clone.keepStrings = keepStrings; + clone.useNativeNulls = useNativeNulls; return clone; } @@ -67,6 +73,21 @@ public class JSONParserConfiguration extends ParserConfiguration { 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 @@ -106,6 +127,18 @@ public class JSONParserConfiguration extends ParserConfiguration { public boolean isOverwriteDuplicateKey() { 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 (isUseNativeNulls() == true) + * or ignore that map entry (isUseNativeNulls() == false). + * + * @return The useNativeNulls 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. diff --git a/src/test/java/org/json/junit/JSONArrayTest.java b/src/test/java/org/json/junit/JSONArrayTest.java index 580fe82..4296203 100644 --- a/src/test/java/org/json/junit/JSONArrayTest.java +++ b/src/test/java/org/json/junit/JSONArrayTest.java @@ -228,6 +228,19 @@ public class JSONArrayTest { Util.checkJSONArrayMaps(jaRaw); Util.checkJSONArrayMaps(jaInt); } + + @Test + public void jsonArrayByListWithNestedNullValue() { + List> list = new ArrayList>(); + Map sub = new HashMap(); + 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. diff --git a/src/test/java/org/json/junit/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index 4c3413f..9d9ef8f 100644 --- a/src/test/java/org/json/junit/JSONObjectTest.java +++ b/src/test/java/org/json/junit/JSONObjectTest.java @@ -616,6 +616,46 @@ public class JSONObjectTest { assertTrue("expected \"doubleKey\":-23.45e67", Double.valueOf("-23.45e67").equals(jsonObject.query("/doubleKey"))); Util.checkJSONObjectMaps(jsonObject); } + + @Test + public void jsonObjectByMapWithNullValueAndParserConfiguration() { + Map map = new HashMap(); + 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 map = new HashMap(); + Map nestedMap = new HashMap(); + nestedMap.put("nullKey", null); + map.put("nestedMap", nestedMap); + List> nestedList = new ArrayList>(); + 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 diff --git a/src/test/java/org/json/junit/JSONParserConfigurationTest.java b/src/test/java/org/json/junit/JSONParserConfigurationTest.java index 0ecf938..926c49f 100644 --- a/src/test/java/org/json/junit/JSONParserConfigurationTest.java +++ b/src/test/java/org/json/junit/JSONParserConfigurationTest.java @@ -53,6 +53,14 @@ public class JSONParserConfigurationTest { assertTrue(jsonParserConfiguration.isKeepStrings()); } + + @Test + public void useNativeNullsIsCloned() { + JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration() + .withUseNativeNulls(true) + .withStrictMode(true); + assertTrue(jsonParserConfiguration.isUseNativeNulls()); + } @Test public void verifyDuplicateKeyThenMaxDepth() {