From 987707ffd40e9b61b63bf06b75ffb1ffdec63beb Mon Sep 17 00:00:00 2001 From: Andres Almiray Date: Mon, 3 Jun 2024 13:23:00 +0100 Subject: [PATCH] fix: Honor type hints when deserializing XML to JSON with empty string values Fixes #66 --- .../java/org/kordamp/json/util/JSONUtils.java | 24 ++++++++++++++++++ .../org/kordamp/json/xml/XMLSerializer.java | 25 ++++++++----------- .../kordamp/json/xml/TestUserSubmitted.java | 16 +++++++++--- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/subprojects/json-lib-core/src/main/java/org/kordamp/json/util/JSONUtils.java b/subprojects/json-lib-core/src/main/java/org/kordamp/json/util/JSONUtils.java index 00036fc..c07f4c3 100644 --- a/subprojects/json-lib-core/src/main/java/org/kordamp/json/util/JSONUtils.java +++ b/subprojects/json-lib-core/src/main/java/org/kordamp/json/util/JSONUtils.java @@ -385,6 +385,30 @@ public static boolean isString(Object obj) { return false; } + /** + *

Determines whether a given string is null, empty, + * or only contains whitespace. If it contains anything other than + * whitespace then the string is not considered to be blank and the + * method returns false.

+ * + * @param str The string to test. + * @return true if the string is null, or + * blank. + */ + public static boolean isBlank(String str) { + if (null == str || str.length() == 0) { + return true; + } + + for (char c : str.toCharArray()) { + if (!Character.isWhitespace(c)) { + return false; + } + } + + return true; + } + /** * Tests if the String possibly represents a valid JSON String.
* Valid JSON strings are: diff --git a/subprojects/json-lib-core/src/main/java/org/kordamp/json/xml/XMLSerializer.java b/subprojects/json-lib-core/src/main/java/org/kordamp/json/xml/XMLSerializer.java index a2acbd8..15a48ff 100644 --- a/subprojects/json-lib-core/src/main/java/org/kordamp/json/xml/XMLSerializer.java +++ b/subprojects/json-lib-core/src/main/java/org/kordamp/json/xml/XMLSerializer.java @@ -59,6 +59,8 @@ import java.util.Map; import java.util.TreeMap; +import static org.kordamp.json.util.JSONUtils.isBlank; + /** * Utility class for transforming JSON to XML an back.
* When transforming JSONObject and JSONArray instances to XML, this class will @@ -560,9 +562,7 @@ public void setForcedArrayElements(Collection forcedArrayElements) { * Creates a JSON value from a XML string. * * @param xml A well-formed xml document in a String - * * @return a JSONNull, JSONObject or JSONArray - * * @throws JSONException if the conversion from XML to JSON can't be made for * I/O or format reasons. */ @@ -600,9 +600,7 @@ public JSON read(String xml) { * Creates a JSON value from a File. * * @param file - * * @return a JSONNull, JSONObject or JSONArray - * * @throws JSONException if the conversion from XML to JSON can't be made for * I/O or format reasons. */ @@ -627,9 +625,7 @@ public JSON readFromFile(File file) { * Creates a JSON value from a File. * * @param path - * * @return a JSONNull, JSONObject or JSONArray - * * @throws JSONException if the conversion from XML to JSON can't be made for * I/O or format reasons. */ @@ -643,9 +639,7 @@ public JSON readFromFile(String path) { * Creates a JSON value from an input stream. * * @param stream - * * @return a JSONNull, JSONObject or JSONArray - * * @throws JSONException if the conversion from XML to JSON can't be made for * I/O or format reasons. */ @@ -767,9 +761,7 @@ public void setKeepArrayName(boolean keepName) { * Writes a JSON value into a XML string with UTF-8 encoding.
* * @param json The JSON value to transform - * * @return a String representation of a well-formed xml document. - * * @throws JSONException if the conversion from JSON to XML can't be made for * I/O reasons. */ @@ -783,9 +775,7 @@ public String write(JSON json) { * * @param json The JSON value to transform * @param encoding The xml encoding to use - * * @return a String representation of a well-formed xml document. - * * @throws JSONException if the conversion from JSON to XML can't be made for * I/O reasons or the encoding is not supported. */ @@ -1193,7 +1183,9 @@ private Element processJSONObject(JSONObject jsonObject, Element root, Object[] names = jsonObject.names().toArray(); List unprocessed = new ArrayList<>(); - if (isSortPropertyNames()) { Arrays.sort(names); } + if (isSortPropertyNames()) { + Arrays.sort(names); + } for (Object o : names) { String name = (String) o; Object value = jsonObject.get(name); @@ -1275,7 +1267,6 @@ private Element processJSONObject(JSONObject jsonObject, Element root, * Only perform auto expansion if all children are objects. * * @param array The array to check - * * @return True if all children are objects, false otherwise. */ private boolean canAutoExpand(JSONArray array) { @@ -1557,7 +1548,11 @@ private void setValue(JSONObject jsonObject, Element element, String defaultType params = StringUtils.split(paramsAttribute.getValue(), ","); setOrAccumulate(jsonObject, key, new JSONFunction(params, text)); } else { - if (isArray(element, false)) { + Attribute typeAttr = element.getAttribute(addJsonPrefix("type")); + if (typeAttr != null && isBlank(element.getValue()) && + element.getChildCount() == 0 && element.getChildElements().size() == 0) { + setOrAccumulate(jsonObject, key, ""); + } else if (isArray(element, false)) { setOrAccumulate(jsonObject, key, processArrayElement(element, defaultType)); } else if (isObject(element, false)) { setOrAccumulate(jsonObject, key, simplifyValue(jsonObject, diff --git a/subprojects/json-lib-core/src/test/java/org/kordamp/json/xml/TestUserSubmitted.java b/subprojects/json-lib-core/src/test/java/org/kordamp/json/xml/TestUserSubmitted.java index efab5fa..0ddf2fb 100644 --- a/subprojects/json-lib-core/src/test/java/org/kordamp/json/xml/TestUserSubmitted.java +++ b/subprojects/json-lib-core/src/test/java/org/kordamp/json/xml/TestUserSubmitted.java @@ -221,16 +221,24 @@ public void testXMLWithoutArray() { Assertions.assertEquals(expected, actual); } + public void testEmptyStringWithHints() { + String testXML = ""; + + JSON expected = JSONSerializer.toJSON("{\"o\": {\"test1\": \"\"}}"); + + JSONObject actual = convertXML(testXML); + assertNotNull(actual); + Assertions.assertEquals(expected, actual); + } + private JSONObject convertXML(String testXML) { - JSON jsonElement = getSerializer().read(testXML); - return (JSONObject) jsonElement; + return (JSONObject) getSerializer().read(testXML); } private JSONObject convertXML(String testXML, String arrayName) { final XMLSerializer xmlSerializer = getSerializer(); xmlSerializer.setArrayName(arrayName); - JSON jsonElement = xmlSerializer.read(testXML); - return (JSONObject) jsonElement; + return (JSONObject) xmlSerializer.read(testXML); } private XMLSerializer getSerializer() {