From e356739a2fa672d4d46a6d10fe49da66cb5b8856 Mon Sep 17 00:00:00 2001 From: Zach Date: Fri, 19 Nov 2021 19:42:00 -0600 Subject: [PATCH 1/6] Added forceList configuration to XMLParserConfiguration --- .../java/org/json/XMLParserConfiguration.java | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/json/XMLParserConfiguration.java b/src/main/java/org/json/XMLParserConfiguration.java index af3093bde..a1fd63eb7 100644 --- a/src/main/java/org/json/XMLParserConfiguration.java +++ b/src/main/java/org/json/XMLParserConfiguration.java @@ -25,7 +25,9 @@ of this software and associated documentation files (the "Software"), to deal import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** @@ -66,6 +68,12 @@ public class XMLParserConfiguration { */ private Map> xsiTypeMap; + /** + * When parsing the XML into JSON, specifies the tags whose values should be converted + * to arrays + */ + private Set forceList; + /** * Default parser configuration. Does not keep strings (tries to implicitly convert * values), and the CDATA Tag Name is "content". @@ -75,6 +83,7 @@ public XMLParserConfiguration () { this.cDataTagName = "content"; this.convertNilAttributeToNull = false; this.xsiTypeMap = Collections.emptyMap(); + this.forceList = Collections.emptySet(); } /** @@ -151,13 +160,15 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN * false to parse values with attribute xsi:nil="true" as {"xsi:nil":true}. * @param xsiTypeMap new HashMap>() to parse values with attribute * xsi:type="integer" as integer, xsi:type="string" as string + * @param forceList new HashSet() to parse the provided tags' values as arrays */ private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, - final boolean convertNilAttributeToNull, final Map> xsiTypeMap ) { + final boolean convertNilAttributeToNull, final Map> xsiTypeMap, final Set forceList ) { this.keepStrings = keepStrings; this.cDataTagName = cDataTagName; this.convertNilAttributeToNull = convertNilAttributeToNull; this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap); + this.forceList = Collections.unmodifiableSet(forceList); } /** @@ -174,7 +185,8 @@ protected XMLParserConfiguration clone() { this.keepStrings, this.cDataTagName, this.convertNilAttributeToNull, - this.xsiTypeMap + this.xsiTypeMap, + this.forceList ); } @@ -283,4 +295,26 @@ public XMLParserConfiguration withXsiTypeMap(final Map} to parse the provided tags' values as arrays + * @return forceList unmodifiable configuration set. + */ + public Set getForceList() { + return this.forceList; + } + + /** + * When parsing the XML into JSON, specifies that tags that will be converted to arrays + * in this configuration {@code Set} to parse the provided tags' values as arrays + * @param forceList {@code new HashSet()} to parse the provided tags' values as arrays + * @return The existing configuration will not be modified. A new configuration is returned. + */ + public XMLParserConfiguration withForceList(final Set forceList) { + XMLParserConfiguration newConfig = this.clone(); + Set cloneForceList = new HashSet(forceList); + newConfig.forceList = Collections.unmodifiableSet(cloneForceList); + return newConfig; + } } From fafaeb7aa6f89055c74d93c7dacca14959ec10d6 Mon Sep 17 00:00:00 2001 From: Zach Date: Fri, 19 Nov 2021 20:32:36 -0600 Subject: [PATCH 2/6] Added simple test case --- .../org/json/junit/XMLConfigurationTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/java/org/json/junit/XMLConfigurationTest.java b/src/test/java/org/json/junit/XMLConfigurationTest.java index 28b20ddfd..1ff5949ae 100755 --- a/src/test/java/org/json/junit/XMLConfigurationTest.java +++ b/src/test/java/org/json/junit/XMLConfigurationTest.java @@ -35,6 +35,8 @@ of this software and associated documentation files (the "Software"), to deal import java.io.IOException; import java.io.Reader; import java.io.StringReader; +import java.util.HashSet; +import java.util.Set; import org.json.JSONArray; import org.json.JSONException; @@ -903,6 +905,34 @@ public void testConfig() { Util.compareActualVsExpectedJsonArrays(jsonArray, expectedJsonArray); } + + /** + * Confirm XMLParserConfiguration functionality + */ + @Test + public void testSimpleForceList() { + + String xmlStr = + "\n"+ + "\n"+ + "
\n"+ + " Sherlock Holmes\n"+ + "
\n"+ + "
"; + + String expectedStr = "{\"addresses\":[{\"address\":{\"name\":\"Sherlock Holmes\"}}]}"; + + Set forceList = new HashSet(); + forceList.add("addresses"); + + XMLParserConfiguration config = + new XMLParserConfiguration() + .withForceList(forceList); + JSONObject jsonObject = XML.toJSONObject(xmlStr, config); + JSONObject expetedJsonObject = new JSONObject(expectedStr); + + Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); + } /** From a0f90b776d4660083e8844e276f80ff92bce8d5d Mon Sep 17 00:00:00 2001 From: Zach Date: Fri, 19 Nov 2021 20:46:15 -0600 Subject: [PATCH 3/6] Passed simple test --- src/main/java/org/json/XML.java | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index b7f0c8b2c..45384d33d 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -413,14 +413,26 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP } else if (token == LT) { // Nested element if (parse(x, jsonObject, tagName, config)) { - if (jsonObject.length() == 0) { - context.accumulate(tagName, ""); - } else if (jsonObject.length() == 1 - && jsonObject.opt(config.getcDataTagName()) != null) { - context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); + if (config.getForceList().contains(tagName)) { + if (jsonObject.length() == 0) { + context.append(tagName, ""); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + context.append(tagName, jsonObject.opt(config.getcDataTagName())); + } else { + context.append(tagName, jsonObject); + } } else { - context.accumulate(tagName, jsonObject); + if (jsonObject.length() == 0) { + context.accumulate(tagName, ""); + } else if (jsonObject.length() == 1 + && jsonObject.opt(config.getcDataTagName()) != null) { + context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); + } else { + context.accumulate(tagName, jsonObject); + } } + return false; } } From 3f9b53fee4cec631e0d8ecf36ce3588e33ee7ee9 Mon Sep 17 00:00:00 2001 From: Zach Date: Fri, 19 Nov 2021 20:55:10 -0600 Subject: [PATCH 4/6] longer forcelist tests --- .../org/json/junit/XMLConfigurationTest.java | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/json/junit/XMLConfigurationTest.java b/src/test/java/org/json/junit/XMLConfigurationTest.java index 1ff5949ae..47c588c71 100755 --- a/src/test/java/org/json/junit/XMLConfigurationTest.java +++ b/src/test/java/org/json/junit/XMLConfigurationTest.java @@ -907,11 +907,10 @@ public void testConfig() { } /** - * Confirm XMLParserConfiguration functionality + * Test forceList parameter */ @Test public void testSimpleForceList() { - String xmlStr = "\n"+ "\n"+ @@ -933,6 +932,48 @@ public void testSimpleForceList() { Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); } + @Test + public void testLongForceList() { + String xmlStr = + ""+ + ""+ + "host1"+ + "Linux"+ + ""+ + ""+ + "em0"+ + "10.0.0.1"+ + ""+ + ""+ + ""+ + ""; + + String expectedStr = + "{"+ + "\"servers\": ["+ + "{"+ + "\"server\": {"+ + "\"name\": \"host1\","+ + "\"os\": \"Linux\","+ + "\"interfaces\": ["+ + "{"+ + "\"interface\": {"+ + "\"name\": \"em0\","+ + "\"ip_address\": \"10.0.0.1\""+ + "}}]}}]}"; + + Set forceList = new HashSet(); + forceList.add("servers"); + forceList.add("interfaces"); + + XMLParserConfiguration config = + new XMLParserConfiguration() + .withForceList(forceList); + JSONObject jsonObject = XML.toJSONObject(xmlStr, config); + JSONObject expetedJsonObject = new JSONObject(expectedStr); + + Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); + } /** From e638955034b4c95849fe3f9ef144b4f8ecaea663 Mon Sep 17 00:00:00 2001 From: Zach Date: Fri, 19 Nov 2021 21:05:05 -0600 Subject: [PATCH 5/6] Pass test case for empty tag with forceList --- src/main/java/org/json/XML.java | 2 +- .../org/json/junit/XMLConfigurationTest.java | 47 +++++++++++++------ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 45384d33d..d78ae1f30 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -415,7 +415,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP if (parse(x, jsonObject, tagName, config)) { if (config.getForceList().contains(tagName)) { if (jsonObject.length() == 0) { - context.append(tagName, ""); + context.put(tagName, new JSONArray()); } else if (jsonObject.length() == 1 && jsonObject.opt(config.getcDataTagName()) != null) { context.append(tagName, jsonObject.opt(config.getcDataTagName())); diff --git a/src/test/java/org/json/junit/XMLConfigurationTest.java b/src/test/java/org/json/junit/XMLConfigurationTest.java index 47c588c71..3008024f9 100755 --- a/src/test/java/org/json/junit/XMLConfigurationTest.java +++ b/src/test/java/org/json/junit/XMLConfigurationTest.java @@ -919,7 +919,8 @@ public void testSimpleForceList() { " \n"+ ""; - String expectedStr = "{\"addresses\":[{\"address\":{\"name\":\"Sherlock Holmes\"}}]}"; + String expectedStr = + "{\"addresses\":[{\"address\":{\"name\":\"Sherlock Holmes\"}}]}"; Set forceList = new HashSet(); forceList.add("addresses"); @@ -949,18 +950,18 @@ public void testLongForceList() { ""; String expectedStr = - "{"+ - "\"servers\": ["+ - "{"+ - "\"server\": {"+ - "\"name\": \"host1\","+ - "\"os\": \"Linux\","+ - "\"interfaces\": ["+ - "{"+ - "\"interface\": {"+ - "\"name\": \"em0\","+ - "\"ip_address\": \"10.0.0.1\""+ - "}}]}}]}"; + "{"+ + "\"servers\": ["+ + "{"+ + "\"server\": {"+ + "\"name\": \"host1\","+ + "\"os\": \"Linux\","+ + "\"interfaces\": ["+ + "{"+ + "\"interface\": {"+ + "\"name\": \"em0\","+ + "\"ip_address\": \"10.0.0.1\""+ + "}}]}}]}"; Set forceList = new HashSet(); forceList.add("servers"); @@ -974,7 +975,25 @@ public void testLongForceList() { Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); } - + @Test + public void testEmptyForceList() { + String xmlStr = + ""; + + String expectedStr = + "{\"addresses\":[]}"; + + Set forceList = new HashSet(); + forceList.add("addresses"); + + XMLParserConfiguration config = + new XMLParserConfiguration() + .withForceList(forceList); + JSONObject jsonObject = XML.toJSONObject(xmlStr, config); + JSONObject expetedJsonObject = new JSONObject(expectedStr); + + Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); + } /** * Convenience method, given an input string and expected result, From 5dd78bc0b96eceb77f9ba113e2e5f7867053ee7e Mon Sep 17 00:00:00 2001 From: Zach Date: Fri, 19 Nov 2021 21:22:38 -0600 Subject: [PATCH 6/6] Test case passed with tags with multiple entries set forceList --- src/main/java/org/json/XML.java | 22 ++++-- .../org/json/junit/XMLConfigurationTest.java | 77 +++++++++++++++++++ 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index d78ae1f30..9b2ba8939 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -380,12 +380,23 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP if (x.nextToken() != GT) { throw x.syntaxError("Misshaped tag"); } - if (nilAttributeFound) { - context.accumulate(tagName, JSONObject.NULL); - } else if (jsonObject.length() > 0) { - context.accumulate(tagName, jsonObject); + if (config.getForceList().contains(tagName)) { + // Force the value to be an array + if (nilAttributeFound) { + context.append(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.append(tagName, jsonObject); + } else { + context.put(tagName, new JSONArray()); + } } else { - context.accumulate(tagName, ""); + if (nilAttributeFound) { + context.accumulate(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.accumulate(tagName, jsonObject); + } else { + context.accumulate(tagName, ""); + } } return false; @@ -414,6 +425,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP // Nested element if (parse(x, jsonObject, tagName, config)) { if (config.getForceList().contains(tagName)) { + // Force the value to be an array if (jsonObject.length() == 0) { context.put(tagName, new JSONArray()); } else if (jsonObject.length() == 1 diff --git a/src/test/java/org/json/junit/XMLConfigurationTest.java b/src/test/java/org/json/junit/XMLConfigurationTest.java index 3008024f9..2ab06be05 100755 --- a/src/test/java/org/json/junit/XMLConfigurationTest.java +++ b/src/test/java/org/json/junit/XMLConfigurationTest.java @@ -976,6 +976,45 @@ public void testLongForceList() { Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); } @Test + public void testMultipleTagForceList() { + String xmlStr = + "\n"+ + "
\n"+ + " Sherlock Holmes\n"+ + " John H. Watson\n"+ + "
\n"+ + "
"; + + String expectedStr = + "{"+ + "\"addresses\":["+ + "{"+ + "\"address\":["+ + "{"+ + "\"name\":["+ + "\"Sherlock Holmes\","+ + "\"John H. Watson\""+ + "]"+ + "}"+ + "]"+ + "}"+ + "]"+ + "}"; + + Set forceList = new HashSet(); + forceList.add("addresses"); + forceList.add("address"); + forceList.add("name"); + + XMLParserConfiguration config = + new XMLParserConfiguration() + .withForceList(forceList); + JSONObject jsonObject = XML.toJSONObject(xmlStr, config); + JSONObject expetedJsonObject = new JSONObject(expectedStr); + + Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); + } + @Test public void testEmptyForceList() { String xmlStr = ""; @@ -994,6 +1033,44 @@ public void testEmptyForceList() { Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); } + @Test + public void testContentForceList() { + String xmlStr = + "Baker Street"; + + String expectedStr = + "{\"addresses\":[\"Baker Street\"]}"; + + Set forceList = new HashSet(); + forceList.add("addresses"); + + XMLParserConfiguration config = + new XMLParserConfiguration() + .withForceList(forceList); + JSONObject jsonObject = XML.toJSONObject(xmlStr, config); + JSONObject expetedJsonObject = new JSONObject(expectedStr); + + Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); + } + @Test + public void testEmptyTagForceList() { + String xmlStr = + ""; + + String expectedStr = + "{\"addresses\":[]}"; + + Set forceList = new HashSet(); + forceList.add("addresses"); + + XMLParserConfiguration config = + new XMLParserConfiguration() + .withForceList(forceList); + JSONObject jsonObject = XML.toJSONObject(xmlStr, config); + JSONObject expetedJsonObject = new JSONObject(expectedStr); + + Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); + } /** * Convenience method, given an input string and expected result,