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>someValue1123IllegalNode>someValue2";
+
+ 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 = "011000- True
";
+ 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 = "011000- True
";
+ 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 = "011000- True
";
+ 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 = "- 01
011000True";
+
+ 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 < ",
+ XMLParserConfiguration.KEEP_STRINGS).toString());
+ assertEquals("Can cope < ", XML.unescape("Can cope < "));
+
+ assertEquals("{\"xml\":\"Can cope 4\"}",
+ XML.toJSONObject("Can cope 4 ",
+ XMLParserConfiguration.KEEP_STRINGS).toString());
+ assertEquals("Can cope 4 ", XML.unescape("Can cope 4 "));
+
+ }
+
+ /**
+ * 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