package org.json.junit; import static org.junit.Assert.*; import org.json.*; import org.junit.Test; /** * Tests for JSON-Java XML.java * Note: noSpace() will be tested by JSONMLTest */ public class XMLTest { @Test(expected=NullPointerException.class) public void shouldHandleNullXML() { String xmlStr = null; JSONObject jsonObject = XML.toJSONObject(xmlStr); assertTrue("jsonObject should be empty", jsonObject.length() == 0); } @Test public void shouldHandleEmptyXML() { String xmlStr = ""; JSONObject jsonObject = XML.toJSONObject(xmlStr); assertTrue("jsonObject should be empty", jsonObject.length() == 0); } @Test public void shouldHandleNonXML() { String xmlStr = "{ \"this is\": \"not xml\"}"; JSONObject jsonObject = XML.toJSONObject(xmlStr); assertTrue("xml string should be empty", jsonObject.length() == 0); } @Test(expected=JSONException.class) public void shouldHandleInvalidSlashInTag() { String xmlStr = "\n"+ "\n"+ "
\n"+ " \n"+ " abc street\n"+ "
\n"+ "
"; XML.toJSONObject(xmlStr); } @Test(expected=JSONException.class) public void shouldHandleInvalidBangInTag() { String xmlStr = "\n"+ "\n"+ "
\n"+ " \n"+ " \n"+ "
\n"+ "
"; XML.toJSONObject(xmlStr); } @Test(expected=JSONException.class) public void shouldHandleInvalidBangNoCloseInTag() { String xmlStr = "\n"+ "\n"+ "
\n"+ " \n"+ " \n"+ ""; XML.toJSONObject(xmlStr); } @Test(expected=JSONException.class) public void shouldHandleNoCloseStartTag() { String xmlStr = "\n"+ "\n"+ "
\n"+ " \n"+ " \n"+ ""; XML.toJSONObject(xmlStr); } @Test(expected=JSONException.class) public void shouldHandleInvalidCDATABangInTag() { String xmlStr = "\n"+ "\n"+ "
\n"+ " Joe Tester\n"+ " \n"+ "
\n"+ "
"; XML.toJSONObject(xmlStr); } @Test(expected=NullPointerException.class) public void shouldHandleNullJSONXML() { JSONObject jsonObject= null; XML.toString(jsonObject); } @Test public void shouldHandleEmptyJSONXML() { JSONObject jsonObject= new JSONObject(); String xmlStr = XML.toString(jsonObject); assertTrue("xml string should be empty", xmlStr.length() == 0); } @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); JSONObject expectedJsonObject = new JSONObject(expectedStr); Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject); } @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\"}}"; JSONObject expectedJsonObject = new JSONObject(expectedStr); JSONObject jsonObject = XML.toJSONObject(xmlStr); Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject); } @Test public void shouldHandleCommentsInXML() { String xmlStr = "\n"+ "\n"+ "\n"+ "
\n"+ " comment ]]>\n"+ " Joe Tester\n"+ " \n"+ " Baker street 5\n"+ "
\n"+ "
"; JSONObject jsonObject = XML.toJSONObject(xmlStr); String expectedStr = "{\"addresses\":{\"address\":{\"street\":\"Baker "+ "street 5\",\"name\":\"Joe Tester\",\"content\":\" this is -- "+ " comment \"}}}"; JSONObject expectedJsonObject = new JSONObject(expectedStr); Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject); } @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); String xmlToStr = XML.toString(jsonObject); JSONObject finalJsonObject = XML.toJSONObject(xmlToStr); JSONObject expectedJsonObject = new JSONObject(expectedStr); Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject); Util.compareActualVsExpectedJsonObjects(finalJsonObject,expectedJsonObject); } @Test public void shouldHandleContentNoArraytoString() { String expectedStr = "{\"addresses\":{\"address\":{\"name\":\"\",\"nocontent\":\"\",\""+ "content\":\">\"},\"xsi:noNamespaceSchemaLocation\":\"test.xsd\",\""+ "xmlns:xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"}}"; JSONObject expectedJsonObject = new JSONObject(expectedStr); String finalStr = XML.toString(expectedJsonObject); String expectedFinalStr = "
>"+ "
test.xsdhttp://www.w3.org/2001/XMLSche"+ "ma-instance
"; assertTrue("Should handle expectedFinal: ["+expectedStr+"] final: ["+ finalStr+"]", expectedFinalStr.equals(finalStr)); } @Test public void shouldHandleContentArraytoString() { String expectedStr = "{\"addresses\":{\"address\":{\"name\":\"\",\"nocontent\":\"\",\""+ "content\":[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); String expectedFinalStr = "
"+ "1\n2\n3"+ "
test.xsdhttp://www.w3.org/2001/XMLSche"+ "ma-instance
"; assertTrue("Should handle expectedFinal: ["+expectedStr+"] final: ["+ finalStr+"]", expectedFinalStr.equals(finalStr)); } @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); String expectedFinalStr = "
"+ "123"+ "
test.xsdhttp://www.w3.org/2001/XMLSche"+ "ma-instance
"; assertTrue("Should handle expectedFinal: ["+expectedStr+"] final: ["+ finalStr+"]", expectedFinalStr.equals(finalStr)); } @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); JSONObject finalJsonObject = XML.toJSONObject(finalStr); String expectedStr = "
"+ "12"+ "3"+ "
test.xsdhttp://www.w3.org/2001/XMLSche"+ "ma-instance
"; JSONObject expectedJsonObject = XML.toJSONObject(expectedStr); Util.compareActualVsExpectedJsonObjects(finalJsonObject,expectedJsonObject); } /** * 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); /** * 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); } @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); assertEquals(actualXML, resultXML); } @Test public void contentOperations() { /** * Make sure we understand exactly how the "content" keyword works */ /** * When a standalone 0) then return]]>"; JSONObject jsonObject = XML.toJSONObject(xmlStr); 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); 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("content") instanceof JSONArray); JSONArray jsonArray = jsonObject.getJSONArray("content"); 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); 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); 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); 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("content") instanceof JSONArray); jsonArray = jsonObject.getJSONArray("content"); 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); 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("content"))); 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); 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\","+ "\"content\": {"+ "\"material\": ["+ "{"+ "\"stuff\": false"+ "}"+ "]"+ "}"+ "}"+ "]"+ "}"+ "}"+ "}"+ "}"; jsonObject = new JSONObject(jsonStr); xmlStr = XML.toString(jsonObject); /** * 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); } }