From d1327c2da312d1670ecf9b9170cd36641e2522dd Mon Sep 17 00:00:00 2001 From: Robert Lichtenberger Date: Wed, 19 Mar 2025 07:59:57 +0100 Subject: [PATCH] Allow to configure Java null handling. --- src/main/java/org/json/JSONObject.java | 2 +- .../org/json/JSONParserConfiguration.java | 31 ++++++++++++++ .../java/org/json/junit/JSONObjectTest.java | 40 +++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java index d50fff7..73835d4 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.isJavaNullAsJsonNull()) { 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..58d080f 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 ignore null values when converting java maps to JSONObject or not. + */ + private boolean javaNullAsJsonNull; /** * Configuration with the default values. @@ -67,6 +72,21 @@ public class JSONParserConfiguration extends ParserConfiguration { 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 javaNullAsJsonNull define, if the parser should ignore null values in Java maps + * @return The existing configuration will not be modified. A new configuration is returned. + */ + public JSONParserConfiguration withJavaNullAsJsonNull(final boolean javaNullAsJsonNull) { + JSONParserConfiguration clone = this.clone(); + clone.javaNullAsJsonNull = javaNullAsJsonNull; + + return clone; + } /** * Sets the strict mode configuration for the JSON parser with default true value @@ -106,6 +126,17 @@ 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 ignore + * that map entry or write a JSON entry with a null value. + * + * @return The javaNullAsJsonNull configuration value. + */ + public boolean isJavaNullAsJsonNull() { + return this.javaNullAsJsonNull; + } + /** * 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/JSONObjectTest.java b/src/test/java/org/json/junit/JSONObjectTest.java index 4c3413f..5762ea7 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().withJavaNullAsJsonNull(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().withJavaNullAsJsonNull(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