From d0ea807884728782b2bc1722ca4a79e56e58e245 Mon Sep 17 00:00:00 2001 From: stleary Date: Sat, 8 Dec 2018 14:51:01 -0600 Subject: [PATCH] xml parser config tests --- README.md | 37 +- .../java/org/json/junit/JunitTestSuite.java | 3 +- .../org/json/junit/XMLConfigurationTest.java | 930 ++++++++++++++++++ 3 files changed, 933 insertions(+), 37 deletions(-) create mode 100755 src/test/java/org/json/junit/XMLConfigurationTest.java diff --git a/README.md b/README.md index 1ca5c64..e6e61b5 100644 --- a/README.md +++ b/README.md @@ -89,43 +89,8 @@ Execution failed for task ':compileJava'. > invalid flag: -parameters ``` -A unit test has the following stages: -| Test phase |Description | -|----|----| -| No test | No test specifically for this class has been written, or the class contains no executable code. | -| In progress | Unit tests have been started for this class. | -| Coverage > 90% | Initial goal of 90% coverage has been reached. Test quality may be questionable | -| Reasonable test cases | 90% coverage. Functionality and behavior has been confirmed | -| Checked against previous unit tests | Historical unit tests have been checked in case something important was missed | -| Completed | The unit test is completed | - - -| Test file name | Coverage | Comments | -| ------------- | ------------- | ---- | -| Total coverage | 90.6% | | | -| | | | -| CDL.java | 98.8% | Reasonable test cases. | -| Cookie.java | 98.9% | Reasonable test cases. | -| CookieList.java |96.5% | Reasonable test cases. | -| HTTP.java | 98.8%| Coverage > 90% | -| HTTPTokener.java |93.2% | No test | -| JSONArray.java |88.3% | Reasonable test cases. Need new tests for newer API functions | -| JSONException.java | 100% | No test | -| JSONML.java | 84.4%| In progress | -| JSONObject | 96.7% | Reasonable test cases | -| JSONObject.Null | 77.8% | No test | -| JSONPointer | 96.3% | Reasonable test cases | -| JSONPointerException | 100% | No test | -| JSONString.java | | No test | -| JSONStringer.java | 93.8%| Coverage > 90% | -| JSONTokener.java | 87.5% | In progress | -| JSONWriter.java | 89.15% | No test | -| Property.java | 95.8% | Coverage > 90% | -| XML.java | 77.3% | In progress | -| XMLTokener.java| 82.4%| No test | - -| Files used in test | +| Resource files used in test | | ------------- | | EnumTest.java | | MyBean.java | diff --git a/src/test/java/org/json/junit/JunitTestSuite.java b/src/test/java/org/json/junit/JunitTestSuite.java index 9c9a325..68b5acb 100644 --- a/src/test/java/org/json/junit/JunitTestSuite.java +++ b/src/test/java/org/json/junit/JunitTestSuite.java @@ -18,7 +18,8 @@ import org.junit.runners.Suite; EnumTest.class, JSONPointerTest.class, JSONStringTest.class, - JSONTokenerTest.class + JSONTokenerTest.class, + XMLConfigurationTest.class }) public class JunitTestSuite { } diff --git a/src/test/java/org/json/junit/XMLConfigurationTest.java b/src/test/java/org/json/junit/XMLConfigurationTest.java new file mode 100755 index 0000000..a2d0b85 --- /dev/null +++ b/src/test/java/org/json/junit/XMLConfigurationTest.java @@ -0,0 +1,930 @@ +package org.json.junit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.XML; +import org.json.XMLParserConfiguration; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + + +/** + * Tests for JSON-Java XML.java with XMLParserConfiguration.java + */ +public class XMLConfigurationTest { + /** + * JUnit supports temporary files and folders that are cleaned up after the test. + * https://garygregory.wordpress.com/2010/01/20/junit-tip-use-rules-to-manage-temporary-files-and-folders/ + */ + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + /** + * JSONObject from a null XML string. + * Expects a NullPointerException + */ + @Test(expected=NullPointerException.class) + public void shouldHandleNullXML() { + String xmlStr = null; + JSONObject jsonObject = + XML.toJSONObject(xmlStr, XMLParserConfiguration.KEEP_STRINGS); + assertTrue("jsonObject should be empty", jsonObject.isEmpty()); + } + + /** + * Empty JSONObject from an empty XML string. + */ + @Test + public void shouldHandleEmptyXML() { + + String xmlStr = ""; + JSONObject jsonObject = + XML.toJSONObject(xmlStr, XMLParserConfiguration.KEEP_STRINGS); + assertTrue("jsonObject should be empty", jsonObject.isEmpty()); + } + + /** + * Empty JSONObject from a non-XML string. + */ + @Test + public void shouldHandleNonXML() { + String xmlStr = "{ \"this is\": \"not xml\"}"; + JSONObject jsonObject = + XML.toJSONObject(xmlStr, XMLParserConfiguration.KEEP_STRINGS); + assertTrue("xml string should be empty", jsonObject.isEmpty()); + } + + /** + * Invalid XML string (tag contains a frontslash). + * Expects a JSONException + */ + @Test + public void shouldHandleInvalidSlashInTag() { + String xmlStr = + "\n"+ + "\n"+ + "
\n"+ + " \n"+ + " abc street\n"+ + "
\n"+ + "
"; + try { + XML.toJSONObject(xmlStr, XMLParserConfiguration.KEEP_STRINGS); + fail("Expecting a JSONException"); + } catch (JSONException e) { + assertEquals("Expecting an exception message", + "Misshaped tag at 176 [character 14 line 4]", + e.getMessage()); + } + } + + /** + * Invalid XML string ('!' char in tag) + * Expects a JSONException + */ + @Test + public void shouldHandleInvalidBangInTag() { + String xmlStr = + "\n"+ + "\n"+ + "
\n"+ + " \n"+ + " \n"+ + "
\n"+ + "
"; + try { + XML.toJSONObject(xmlStr, XMLParserConfiguration.KEEP_STRINGS); + fail("Expecting a JSONException"); + } catch (JSONException e) { + assertEquals("Expecting an exception message", + "Misshaped meta tag at 214 [character 12 line 7]", + e.getMessage()); + } + } + + /** + * Invalid XML string ('!' char and no closing tag brace) + * Expects a JSONException + */ + @Test + public void shouldHandleInvalidBangNoCloseInTag() { + String xmlStr = + "\n"+ + "\n"+ + "
\n"+ + " \n"+ + " \n"+ + ""; + try { + XML.toJSONObject(xmlStr, XMLParserConfiguration.KEEP_STRINGS); + fail("Expecting a JSONException"); + } catch (JSONException e) { + assertEquals("Expecting an exception message", + "Misshaped meta tag at 213 [character 12 line 7]", + e.getMessage()); + } + } + + /** + * Invalid XML string (no end brace for tag) + * Expects JSONException + */ + @Test + public void shouldHandleNoCloseStartTag() { + String xmlStr = + "\n"+ + "\n"+ + "
\n"+ + " \n"+ + " \n"+ + ""; + try { + XML.toJSONObject(xmlStr, XMLParserConfiguration.KEEP_STRINGS); + fail("Expecting a JSONException"); + } catch (JSONException e) { + assertEquals("Expecting an exception message", + "Misplaced '<' at 193 [character 4 line 6]", + e.getMessage()); + } + } + + /** + * Invalid XML string (partial CDATA chars in tag name) + * Expects JSONException + */ + @Test + public void shouldHandleInvalidCDATABangInTag() { + String xmlStr = + "\n"+ + "\n"+ + "
\n"+ + " Joe Tester\n"+ + " \n"+ + "
\n"+ + "
"; + try { + XMLParserConfiguration config = + new XMLParserConfiguration("altContent"); + XML.toJSONObject(xmlStr, config); + fail("Expecting a JSONException"); + } catch (JSONException e) { + assertEquals("Expecting an exception message", + "Expected 'CDATA[' at 204 [character 11 line 5]", + e.getMessage()); + } + } + + /** + * Null JSONObject in XML.toString() + */ + @Test + public void shouldHandleNullJSONXML() { + JSONObject jsonObject= null; + String actualXml = XML.toString(jsonObject, null, + XMLParserConfiguration.KEEP_STRINGS); + assertEquals("generated XML does not equal expected XML","\"null\"",actualXml); + } + + /** + * Empty JSONObject in XML.toString() + */ + @Test + public void shouldHandleEmptyJSONXML() { + JSONObject jsonObject= new JSONObject(); + String xmlStr = XML.toString(jsonObject, null, + XMLParserConfiguration.KEEP_STRINGS); + assertTrue("xml string should be empty", xmlStr.isEmpty()); + } + + /** + * No SML start tag. The ending tag ends up being treated as content. + */ + @Test + public void shouldHandleNoStartTag() { + String xmlStr = + "\n"+ + "\n"+ + "
\n"+ + " \n"+ + " >\n"+ + "
\n"+ + "
"; + String expectedStr = + "{\"addresses\":{\"address\":{\"name\":\"\",\"nocontent\":\"\",\""+ + "content\":\">\"},\"xsi:noNamespaceSchemaLocation\":\"test.xsd\",\""+ + "xmlns:xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"}}"; + JSONObject jsonObject = XML.toJSONObject(xmlStr, + XMLParserConfiguration.KEEP_STRINGS); + JSONObject expectedJsonObject = new JSONObject(expectedStr); + Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject); + } + + /** + * Valid XML to JSONObject + */ + @Test + public void shouldHandleSimpleXML() { + String xmlStr = + "\n"+ + "\n"+ + "
\n"+ + " Joe Tester\n"+ + " [CDATA[Baker street 5]\n"+ + " \n"+ + " true\n"+ + " false\n"+ + " null\n"+ + " 42\n"+ + " -23\n"+ + " -23.45\n"+ + " -23x.45\n"+ + " 1, 2, 3, 4.1, 5.2\n"+ + "
\n"+ + "
"; + + String expectedStr = + "{\"addresses\":{\"address\":{\"street\":\"[CDATA[Baker street 5]\","+ + "\"name\":\"Joe Tester\",\"NothingHere\":\"\",TrueValue:true,\n"+ + "\"FalseValue\":false,\"NullValue\":null,\"PositiveValue\":42,\n"+ + "\"NegativeValue\":-23,\"DoubleValue\":-23.45,\"Nan\":-23x.45,\n"+ + "\"ArrayOfNum\":\"1, 2, 3, 4.1, 5.2\"\n"+ + "},\"xsi:noNamespaceSchemaLocation\":"+ + "\"test.xsd\",\"xmlns:xsi\":\"http://www.w3.org/2001/"+ + "XMLSchema-instance\"}}"; + + XMLParserConfiguration config = + new XMLParserConfiguration("altContent"); + compareStringToJSONObject(xmlStr, expectedStr, config); + compareReaderToJSONObject(xmlStr, expectedStr, config); + compareFileToJSONObject(xmlStr, expectedStr); + } + + /** + * Valid XML with comments to JSONObject + */ + @Test + public void shouldHandleCommentsInXML() { + + String xmlStr = + "\n"+ + "\n"+ + "\n"+ + "
\n"+ + " comment ]]>\n"+ + " Joe Tester\n"+ + " \n"+ + " Baker street 5\n"+ + "
\n"+ + "
"; + XMLParserConfiguration config = + new XMLParserConfiguration("altContent"); + JSONObject jsonObject = XML.toJSONObject(xmlStr, config); + String expectedStr = "{\"addresses\":{\"address\":{\"street\":\"Baker "+ + "street 5\",\"name\":\"Joe Tester\",\"altContent\":\" this is -- "+ + " comment \"}}}"; + JSONObject expectedJsonObject = new JSONObject(expectedStr); + Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject); + } + + /** + * Valid XML to XML.toString() + */ + @Test + public void shouldHandleToString() { + String xmlStr = + "\n"+ + "\n"+ + "
\n"+ + " [CDATA[Joe & T > e < s " t ' er]]\n"+ + " Baker street 5\n"+ + " 1, 2, 3, 4.1, 5.2\n"+ + "
\n"+ + "
"; + + String expectedStr = + "{\"addresses\":{\"address\":{\"street\":\"Baker street 5\","+ + "\"name\":\"[CDATA[Joe & T > e < s \\\" t \\\' er]]\","+ + "\"ArrayOfNum\":\"1, 2, 3, 4.1, 5.2\"\n"+ + "},\"xsi:noNamespaceSchemaLocation\":"+ + "\"test.xsd\",\"xmlns:xsi\":\"http://www.w3.org/2001/"+ + "XMLSchema-instance\"}}"; + + JSONObject jsonObject = XML.toJSONObject(xmlStr, + XMLParserConfiguration.KEEP_STRINGS); + String xmlToStr = XML.toString(jsonObject, null, + XMLParserConfiguration.KEEP_STRINGS); + JSONObject finalJsonObject = XML.toJSONObject(xmlToStr, + XMLParserConfiguration.KEEP_STRINGS); + JSONObject expectedJsonObject = new JSONObject(expectedStr); + Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject); + Util.compareActualVsExpectedJsonObjects(finalJsonObject,expectedJsonObject); + } + + /** + * Converting a JSON doc containing '>' content to JSONObject, then + * XML.toString() should result in valid XML. + */ + @Test + public void shouldHandleContentNoArraytoString() { + String expectedStr = + "{\"addresses\":{\"address\":{\"name\":\"\",\"nocontent\":\"\",\""+ + "altContent\":\">\"},\"xsi:noNamespaceSchemaLocation\":\"test.xsd\",\""+ + "xmlns:xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"}}"; + JSONObject expectedJsonObject = new JSONObject(expectedStr); + XMLParserConfiguration config = new XMLParserConfiguration("altContent"); + String finalStr = XML.toString(expectedJsonObject, null, config); + String expectedFinalStr = "
>"+ + "
test.xsdhttp://www.w3.org/2001/XMLSche"+ + "ma-instance
"; + assertTrue("Should handle expectedFinal: ["+expectedStr+"] final: ["+ + finalStr+"]", expectedFinalStr.equals(finalStr)); + } + + /** + * Converting a JSON doc containing a 'content' array to JSONObject, then + * XML.toString() should result in valid XML. + * TODO: This is probably an error in how the 'content' keyword is used. + */ + @Test + public void shouldHandleContentArraytoString() { + String expectedStr = + "{\"addresses\":{\"address\":{\"name\":\"\",\"nocontent\":\"\",\""+ + "altContent\":[1, 2, 3]},\"xsi:noNamespaceSchemaLocation\":\"test.xsd\",\""+ + "xmlns:xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"}}"; + JSONObject expectedJsonObject = new JSONObject(expectedStr); + XMLParserConfiguration config = new XMLParserConfiguration("altContent"); + String finalStr = XML.toString(expectedJsonObject, null, config); + String expectedFinalStr = "
"+ + "1\n2\n3"+ + "
test.xsdhttp://www.w3.org/2001/XMLSche"+ + "ma-instance
"; + assertTrue("Should handle expectedFinal: ["+expectedStr+"] final: ["+ + finalStr+"]", expectedFinalStr.equals(finalStr)); + } + + /** + * Converting a JSON doc containing a named array to JSONObject, then + * XML.toString() should result in valid XML. + */ + @Test + public void shouldHandleArraytoString() { + String expectedStr = + "{\"addresses\":{\"address\":{\"name\":\"\",\"nocontent\":\"\","+ + "\"something\":[1, 2, 3]},\"xsi:noNamespaceSchemaLocation\":\"test.xsd\",\""+ + "xmlns:xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"}}"; + JSONObject expectedJsonObject = new JSONObject(expectedStr); + String finalStr = XML.toString(expectedJsonObject, null, + XMLParserConfiguration.KEEP_STRINGS); + String expectedFinalStr = "
"+ + "123"+ + "
test.xsdhttp://www.w3.org/2001/XMLSche"+ + "ma-instance
"; + assertTrue("Should handle expectedFinal: ["+expectedStr+"] final: ["+ + finalStr+"]", expectedFinalStr.equals(finalStr)); + } + + /** + * Tests that the XML output for empty arrays is consistent. + */ + @Test + public void shouldHandleEmptyArray(){ + final JSONObject jo1 = new JSONObject(); + jo1.put("array",new Object[]{}); + final JSONObject jo2 = new JSONObject(); + jo2.put("array",new JSONArray()); + + final String expected = ""; + String output1 = XML.toString(jo1, "jo", + XMLParserConfiguration.KEEP_STRINGS); + assertEquals("Expected an empty root tag", expected, output1); + String output2 = XML.toString(jo2, "jo", + XMLParserConfiguration.KEEP_STRINGS); + assertEquals("Expected an empty root tag", expected, output2); + } + + /** + * Tests that the XML output for arrays is consistent when an internal array is empty. + */ + @Test + public void shouldHandleEmptyMultiArray(){ + final JSONObject jo1 = new JSONObject(); + jo1.put("arr",new Object[]{"One", new String[]{}, "Four"}); + final JSONObject jo2 = new JSONObject(); + jo2.put("arr",new JSONArray(new Object[]{"One", new JSONArray(new String[]{}), "Four"})); + + final String expected = "OneFour"; + String output1 = XML.toString(jo1, "jo", + XMLParserConfiguration.KEEP_STRINGS); + assertEquals("Expected a matching array", expected, output1); + String output2 = XML.toString(jo2, "jo", + XMLParserConfiguration.KEEP_STRINGS); + + assertEquals("Expected a matching array", expected, output2); + } + + /** + * Tests that the XML output for arrays is consistent when arrays are not empty. + */ + @Test + public void shouldHandleNonEmptyArray(){ + final JSONObject jo1 = new JSONObject(); + jo1.put("arr",new String[]{"One", "Two", "Three"}); + final JSONObject jo2 = new JSONObject(); + jo2.put("arr",new JSONArray(new String[]{"One", "Two", "Three"})); + + final String expected = "OneTwoThree"; + String output1 = XML.toString(jo1, "jo", + XMLParserConfiguration.KEEP_STRINGS); + assertEquals("Expected a non empty root tag", expected, output1); + String output2 = XML.toString(jo2, "jo", + XMLParserConfiguration.KEEP_STRINGS); + assertEquals("Expected a non empty root tag", expected, output2); + } + + /** + * Tests that the XML output for arrays is consistent when arrays are not empty and contain internal arrays. + */ + @Test + public void shouldHandleMultiArray(){ + final JSONObject jo1 = new JSONObject(); + jo1.put("arr",new Object[]{"One", new String[]{"Two", "Three"}, "Four"}); + final JSONObject jo2 = new JSONObject(); + jo2.put("arr",new JSONArray(new Object[]{"One", new JSONArray(new String[]{"Two", "Three"}), "Four"})); + + final String expected = "OneTwoThreeFour"; + String output1 = XML.toString(jo1, "jo", + XMLParserConfiguration.KEEP_STRINGS); + assertEquals("Expected a matching array", expected, output1); + String output2 = XML.toString(jo2, "jo", + XMLParserConfiguration.KEEP_STRINGS); + assertEquals("Expected a matching array", expected, output2); + } + + /** + * Converting a JSON doc containing a named array of nested arrays to + * JSONObject, then XML.toString() should result in valid XML. + */ + @Test + public void shouldHandleNestedArraytoString() { + String xmlStr = + "{\"addresses\":{\"address\":{\"name\":\"\",\"nocontent\":\"\","+ + "\"outer\":[[1], [2], [3]]},\"xsi:noNamespaceSchemaLocation\":\"test.xsd\",\""+ + "xmlns:xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"}}"; + JSONObject jsonObject = new JSONObject(xmlStr); + String finalStr = XML.toString(jsonObject, null, + XMLParserConfiguration.ORIGINAL); + JSONObject finalJsonObject = XML.toJSONObject(finalStr); + String expectedStr = "
"+ + "12"+ + "3"+ + "
test.xsdhttp://www.w3.org/2001/XMLSche"+ + "ma-instance
"; + JSONObject expectedJsonObject = XML.toJSONObject(expectedStr, + XMLParserConfiguration.ORIGINAL); + Util.compareActualVsExpectedJsonObjects(finalJsonObject,expectedJsonObject); + } + + + /** + * Possible bug: + * Illegal node-names must be converted to legal XML-node-names. + * The given example shows 2 nodes which are valid for JSON, but not for XML. + * Therefore illegal arguments should be converted to e.g. an underscore (_). + */ + @Test + public void shouldHandleIllegalJSONNodeNames() + { + JSONObject inputJSON = new JSONObject(); + inputJSON.append("123IllegalNode", "someValue1"); + inputJSON.append("Illegal@node", "someValue2"); + + String result = XML.toString(inputJSON, null, + XMLParserConfiguration.KEEP_STRINGS); + + /* + * This is invalid XML. Names should not begin with digits or contain + * certain values, including '@'. One possible solution is to replace + * illegal chars with '_', in which case the expected output would be: + * <___IllegalNode>someValue1someValue2 + */ + String expected = "<123IllegalNode>someValue1someValue2"; + + assertEquals(expected, result); + } + + /** + * JSONObject with NULL value, to XML.toString() + */ + @Test + public void shouldHandleNullNodeValue() + { + JSONObject inputJSON = new JSONObject(); + inputJSON.put("nullValue", JSONObject.NULL); + // This is a possible preferred result + // String expectedXML = ""; + /** + * This is the current behavior. JSONObject.NULL is emitted as + * the string, "null". + */ + String actualXML = "null"; + String resultXML = XML.toString(inputJSON, null, + XMLParserConfiguration.KEEP_STRINGS); + assertEquals(actualXML, resultXML); + } + + /** + * Investigate exactly how the "content" keyword works + */ + @Test + public void contentOperations() { + /* + * When a standalone 0) then return]]>"; + JSONObject jsonObject = XML.toJSONObject(xmlStr, + XMLParserConfiguration.KEEP_STRINGS); + assertTrue("1. 3 items", 3 == jsonObject.length()); + assertTrue("1. empty tag1", "".equals(jsonObject.get("tag1"))); + assertTrue("1. empty tag2", "".equals(jsonObject.get("tag2"))); + assertTrue("1. content found", "if (a < b && a > 0) then return".equals(jsonObject.get("content"))); + + // multiple consecutive standalone cdatas are accumulated into an array + xmlStr = " 0) then return]]>"; + jsonObject = XML.toJSONObject(xmlStr, + new XMLParserConfiguration(true, "altContent")); + assertTrue("2. 3 items", 3 == jsonObject.length()); + assertTrue("2. empty tag1", "".equals(jsonObject.get("tag1"))); + assertTrue("2. empty tag2", "".equals(jsonObject.get("tag2"))); + assertTrue("2. content array found", jsonObject.get("altContent") instanceof JSONArray); + JSONArray jsonArray = jsonObject.getJSONArray("altContent"); + assertTrue("2. array size", jsonArray.length() == 2); + assertTrue("2. content array entry 0", "if (a < b && a > 0) then return".equals(jsonArray.get(0))); + assertTrue("2. content array entry 1", "here is another cdata".equals(jsonArray.get(1))); + + /* + * text content is accumulated in a "content" inside a local JSONObject. + * If there is only one instance, it is saved in the context (a different JSONObject + * from the calling code. and the content element is discarded. + */ + xmlStr = "value 1"; + jsonObject = XML.toJSONObject(xmlStr, + new XMLParserConfiguration(true, "altContent")); + assertTrue("3. 2 items", 1 == jsonObject.length()); + assertTrue("3. value tag1", "value 1".equals(jsonObject.get("tag1"))); + + /* + * array-style text content (multiple tags with the same name) is + * accumulated in a local JSONObject with key="content" and value=JSONArray, + * saved in the context, and then the local JSONObject is discarded. + */ + xmlStr = "value 12true"; + jsonObject = XML.toJSONObject(xmlStr, + new XMLParserConfiguration(true, "altContent")); + assertTrue("4. 1 item", 1 == jsonObject.length()); + assertTrue("4. content array found", jsonObject.get("tag1") instanceof JSONArray); + jsonArray = jsonObject.getJSONArray("tag1"); + assertTrue("4. array size", jsonArray.length() == 3); + assertTrue("4. content array entry 0", "value 1".equals(jsonArray.get(0))); + assertTrue("4. content array entry 1", jsonArray.getInt(1) == 2); + assertTrue("4. content array entry 2", jsonArray.getBoolean(2) == true); + + /* + * Complex content is accumulated in a "content" field. For example, an element + * may contain a mix of child elements and text. Each text segment is + * accumulated to content. + */ + xmlStr = "val1val2"; + jsonObject = XML.toJSONObject(xmlStr, + new XMLParserConfiguration(true, "altContent")); + assertTrue("5. 1 item", 1 == jsonObject.length()); + assertTrue("5. jsonObject found", jsonObject.get("tag1") + instanceof JSONObject); + jsonObject = jsonObject.getJSONObject("tag1"); + assertTrue("5. 2 contained items", 2 == jsonObject.length()); + assertTrue("5. contained tag", "".equals(jsonObject.get("tag2"))); + assertTrue("5. contained content jsonArray found", + jsonObject.get("altContent") instanceof JSONArray); + jsonArray = jsonObject.getJSONArray("altContent"); + assertTrue("5. array size", jsonArray.length() == 2); + assertTrue("5. content array entry 0", "val1".equals(jsonArray.get(0))); + assertTrue("5. content array entry 1", "val2".equals(jsonArray.get(1))); + + /* + * If there is only 1 complex text content, then it is accumulated in a + * "content" field as a string. + */ + xmlStr = "val1"; + jsonObject = XML.toJSONObject(xmlStr, + new XMLParserConfiguration(true, "altContent")); + assertTrue("6. 1 item", 1 == jsonObject.length()); + assertTrue("6. jsonObject found", jsonObject.get("tag1") instanceof JSONObject); + jsonObject = jsonObject.getJSONObject("tag1"); + assertTrue("6. contained content found", + "val1".equals(jsonObject.get("altContent"))); + assertTrue("6. contained tag2", "".equals(jsonObject.get("tag2"))); + + /* + * In this corner case, the content sibling happens to have key=content + * We end up with an array within an array, and no content element. + * This is probably a bug. + */ + xmlStr = "val1"; + jsonObject = XML.toJSONObject(xmlStr, + new XMLParserConfiguration(true, "altContent")); + assertTrue("7. 1 item", 1 == jsonObject.length()); + assertTrue("7. jsonArray found", + jsonObject.get("tag1") instanceof JSONArray); + jsonArray = jsonObject.getJSONArray("tag1"); + assertTrue("array size 1", jsonArray.length() == 1); + assertTrue("7. contained array found", jsonArray.get(0) + instanceof JSONArray); + jsonArray = jsonArray.getJSONArray(0); + assertTrue("7. inner array size 2", jsonArray.length() == 2); + assertTrue("7. inner array item 0", "val1".equals(jsonArray.get(0))); + assertTrue("7. inner array item 1", "".equals(jsonArray.get(1))); + + /* + * Confirm behavior of original issue + */ + String jsonStr = + "{"+ + "\"Profile\": {"+ + "\"list\": {"+ + "\"history\": {"+ + "\"entries\": ["+ + "{"+ + "\"deviceId\": \"id\","+ + "\"altContent\": {"+ + "\"material\": ["+ + "{"+ + "\"stuff\": false"+ + "}"+ + "]"+ + "}"+ + "}"+ + "]"+ + "}"+ + "}"+ + "}"+ + "}"; + jsonObject = new JSONObject(jsonStr); + xmlStr = XML.toString(jsonObject, null, + new XMLParserConfiguration(true, "altContent")); + /* + * This is the created XML. Looks like content was mistaken for + * complex (child node + text) XML. + * + * + * + * + * id + * {"material":[{"stuff":false}]} + * + * + * + * + */ + assertTrue("nothing to test here, see comment on created XML, above", true); + } + + /** + * JSON string lost leading zero and converted "True" to true. + */ + @Test + public void testToJSONArray_jsonOutput() { + final String originalXml = "011000True"; + final String expectedJsonString = "{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",1,\"00\",0],\"title\":true}}"; + final JSONObject actualJsonOutput = XML.toJSONObject(originalXml, + new XMLParserConfiguration(false)); + assertEquals(expectedJsonString, actualJsonOutput.toString()); + } + + /** + * JSON string cannot be reverted to original xml. + */ + @Test + public void testToJSONArray_reversibility() { + final String originalXml = "011000True"; + XMLParserConfiguration config = new XMLParserConfiguration(false); + final String revertedXml = + XML.toString(XML.toJSONObject(originalXml, config), + null, config); + assertNotEquals(revertedXml, originalXml); + } + + /** + * test passes when using the new method toJsonArray. + */ + @Test + public void testToJsonXML() { + final String originalXml = "011000True"; + final String expectedJsonString = "{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",\"1\",\"00\",\"0\"],\"title\":\"True\"}}"; + + final JSONObject json = XML.toJSONObject(originalXml, + new XMLParserConfiguration(true)); + assertEquals(expectedJsonString, json.toString()); + + final String reverseXml = XML.toString(json); + // this reversal isn't exactly the same. use JSONML for an exact reversal + final String expectedReverseXml = "01011000True"; + + assertEquals(expectedReverseXml, reverseXml); + } + + /** + * test to validate certain conditions of XML unescaping. + */ + @Test + public void testUnescape() { + assertEquals("{\"xml\":\"Can cope <;\"}", + XML.toJSONObject("Can cope <; ", + XMLParserConfiguration.KEEP_STRINGS).toString()); + assertEquals("Can cope <; ", XML.unescape("Can cope <; ")); + + assertEquals("{\"xml\":\"Can cope & ;\"}", + XML.toJSONObject("Can cope & ; ", + XMLParserConfiguration.KEEP_STRINGS).toString()); + assertEquals("Can cope & ; ", XML.unescape("Can cope & ; ")); + + assertEquals("{\"xml\":\"Can cope &;\"}", + XML.toJSONObject("Can cope &; ", + XMLParserConfiguration.KEEP_STRINGS).toString()); + assertEquals("Can cope &; ", XML.unescape("Can cope &; ")); + + // unicode entity + assertEquals("{\"xml\":\"Can cope 4;\"}", + XML.toJSONObject("Can cope 4; ", + XMLParserConfiguration.KEEP_STRINGS).toString()); + assertEquals("Can cope 4; ", XML.unescape("Can cope 4; ")); + + // double escaped + assertEquals("{\"xml\":\"Can cope <\"}", + XML.toJSONObject("Can cope &lt; ", + XMLParserConfiguration.KEEP_STRINGS).toString()); + assertEquals("Can cope < ", XML.unescape("Can cope &lt; ")); + + assertEquals("{\"xml\":\"Can cope 4\"}", + XML.toJSONObject("Can cope &#x34; ", + XMLParserConfiguration.KEEP_STRINGS).toString()); + assertEquals("Can cope 4 ", XML.unescape("Can cope &#x34; ")); + + } + + /** + * Confirm XMLParserConfiguration functionality + */ + @Test + public void testConfig() { + /** + * 1st param is whether to keep the raw string, or call + * XML.stringToValue(), which may convert the token to + * boolean, null, or number. + * 2nd param is what JSON name to use for strings that are + * evaluated as xml content data in complex objects, e.g. + * + * value + * content data + * + */ + + String xmlStr = + "\n"+ + "\n"+ + "
\n"+ + " content 1\n"+ + " Sherlock Holmes\n"+ + " content 2\n"+ + " Baker street 5\n"+ + " content 3\n"+ + " 1\n"+ + "
\n"+ + "
"; + + // keep strings, use the altContent tag + XMLParserConfiguration config = + new XMLParserConfiguration(true, "altContent"); + JSONObject jsonObject = XML.toJSONObject(xmlStr, config); + // num is parsed as a string + assertEquals(jsonObject.getJSONObject("addresses"). + getJSONObject("address").getString("num"), "1"); + // complex content is collected in an 'altContent' array + JSONArray jsonArray = jsonObject.getJSONObject("addresses"). + getJSONObject("address").getJSONArray("altContent"); + String expectedStr = "[\"content 1\", \"content 2\", \"content 3\"]"; + JSONArray expectedJsonArray = new JSONArray(expectedStr); + Util.compareActualVsExpectedJsonArrays(jsonArray, expectedJsonArray); + + // keepstrings only + jsonObject = XML.toJSONObject(xmlStr, + XMLParserConfiguration.KEEP_STRINGS); + // num is parsed as a string + assertEquals(jsonObject.getJSONObject("addresses"). + getJSONObject("address").getString("num"), "1"); + // complex content is collected in an 'content' array + jsonArray = jsonObject.getJSONObject("addresses"). + getJSONObject("address").getJSONArray("content"); + expectedJsonArray = new JSONArray(expectedStr); + Util.compareActualVsExpectedJsonArrays(jsonArray, expectedJsonArray); + + // use alternate content name + config = new XMLParserConfiguration("altContent"); + jsonObject = XML.toJSONObject(xmlStr, config); + // num is parsed as a number + assertEquals(jsonObject.getJSONObject("addresses"). + getJSONObject("address").getInt("num"), 1); + // complex content is collected in an 'altContent' array + jsonArray = jsonObject.getJSONObject("addresses"). + getJSONObject("address").getJSONArray("altContent"); + expectedJsonArray = new JSONArray(expectedStr); + Util.compareActualVsExpectedJsonArrays(jsonArray, expectedJsonArray); + + } + + + /** + * Convenience method, given an input string and expected result, + * convert to JSONObject and compare actual to expected result. + * @param xmlStr the string to parse + * @param expectedStr the expected JSON string + * @param config provides more flexible XML parsing + * flexible XML parsing. + */ + private void compareStringToJSONObject(String xmlStr, String expectedStr, + XMLParserConfiguration config) { + JSONObject expectedJsonObject = new JSONObject(expectedStr); + JSONObject jsonObject = XML.toJSONObject(xmlStr, config); + Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject); + } + + /** + * Convenience method, given an input string and expected result, + * convert to JSONObject via reader and compare actual to expected result. + * @param xmlStr the string to parse + * @param expectedStr the expected JSON string + * @param config provides more flexible XML parsing + */ + private void compareReaderToJSONObject(String xmlStr, String expectedStr, + XMLParserConfiguration config) { + /* + * Commenting out this method until the JSON-java code is updated + * to support XML.toJSONObject(reader) + JSONObject expectedJsonObject = new JSONObject(expectedStr); + Reader reader = new StringReader(xmlStr); + JSONObject jsonObject = XML.toJSONObject(reader); + Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject); + */ + } + + /** + * Convenience method, given an input string and expected result, convert to + * JSONObject via file and compare actual to expected result. + * + * @param xmlStr + * the string to parse + * @param expectedStr + * the expected JSON string + * @throws IOException + */ + private void compareFileToJSONObject(String xmlStr, String expectedStr) { + /* + * Commenting out this method until the JSON-java code is updated + * to support XML.toJSONObject(reader) + try { + JSONObject expectedJsonObject = new JSONObject(expectedStr); + File tempFile = testFolder.newFile("fileToJSONObject.xml"); + FileWriter fileWriter = new FileWriter(tempFile); + fileWriter.write(xmlStr); + fileWriter.close(); + Reader reader = new FileReader(tempFile); + JSONObject jsonObject = XML.toJSONObject(reader); + Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject); + } catch (IOException e) { + assertTrue("file writer error: " +e.getMessage(), false); + } + */ + } +} \ No newline at end of file