mirror of
https://github.com/stleary/JSON-java.git
synced 2025-08-02 19:15:27 -04:00
Use the built-in System.lineSeparator() instead of implementing it ourselves with System.getProperty("line.separator") in order to clean up the code and make it easier to maintain.
1321 lines
56 KiB
Java
1321 lines
56 KiB
Java
package org.json.junit;
|
|
|
|
/*
|
|
Public Domain.
|
|
*/
|
|
|
|
import static org.junit.Assert.assertEquals;
|
|
import static org.junit.Assert.assertFalse;
|
|
import static org.junit.Assert.assertNotEquals;
|
|
import static org.junit.Assert.assertTrue;
|
|
import static org.junit.Assert.fail;
|
|
|
|
import java.io.File;
|
|
import java.io.FileReader;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.Reader;
|
|
import java.io.StringReader;
|
|
import java.nio.charset.Charset;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
import org.json.*;
|
|
import org.junit.Rule;
|
|
import org.junit.Test;
|
|
import org.junit.rules.TemporaryFolder;
|
|
|
|
|
|
/**
|
|
* Tests for JSON-Java XML.java
|
|
* Note: noSpace() will be tested by JSONMLTest
|
|
*/
|
|
public class XMLTest {
|
|
/**
|
|
* 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);
|
|
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);
|
|
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);
|
|
assertTrue("xml string should be empty", jsonObject.isEmpty());
|
|
}
|
|
|
|
/**
|
|
* Invalid XML string (tag contains a frontslash).
|
|
* Expects a JSONException
|
|
*/
|
|
@Test
|
|
public void shouldHandleInvalidSlashInTag() {
|
|
String xmlStr =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
|
|
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
|
|
" <address>\n"+
|
|
" <name/x>\n"+
|
|
" <street>abc street</street>\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
try {
|
|
XML.toJSONObject(xmlStr);
|
|
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 =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
|
|
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
|
|
" <address>\n"+
|
|
" <name/>\n"+
|
|
" <!>\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
try {
|
|
XML.toJSONObject(xmlStr);
|
|
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 =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
|
|
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
|
|
" <address>\n"+
|
|
" <name/>\n"+
|
|
" <!\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
try {
|
|
XML.toJSONObject(xmlStr);
|
|
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 =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
|
|
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
|
|
" <address>\n"+
|
|
" <name/>\n"+
|
|
" <abc\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
try {
|
|
XML.toJSONObject(xmlStr);
|
|
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 =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
|
|
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
|
|
" <address>\n"+
|
|
" <name>Joe Tester</name>\n"+
|
|
" <![[]>\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
try {
|
|
XML.toJSONObject(xmlStr);
|
|
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);
|
|
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);
|
|
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 =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
|
|
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
|
|
" <address>\n"+
|
|
" <name/>\n"+
|
|
" <nocontent/>>\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Valid XML to JSONObject
|
|
*/
|
|
@Test
|
|
public void shouldHandleSimpleXML() {
|
|
String xmlStr =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
|
|
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
|
|
" <address>\n"+
|
|
" <name>Joe Tester</name>\n"+
|
|
" <street>[CDATA[Baker street 5]</street>\n"+
|
|
" <NothingHere/>\n"+
|
|
" <TrueValue>true</TrueValue>\n"+
|
|
" <FalseValue>false</FalseValue>\n"+
|
|
" <NullValue>null</NullValue>\n"+
|
|
" <PositiveValue>42</PositiveValue>\n"+
|
|
" <NegativeValue>-23</NegativeValue>\n"+
|
|
" <DoubleValue>-23.45</DoubleValue>\n"+
|
|
" <Nan>-23x.45</Nan>\n"+
|
|
" <ArrayOfNum>1, 2, 3, 4.1, 5.2</ArrayOfNum>\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
|
|
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\"}}";
|
|
|
|
compareStringToJSONObject(xmlStr, expectedStr);
|
|
compareReaderToJSONObject(xmlStr, expectedStr);
|
|
compareFileToJSONObject(xmlStr, expectedStr);
|
|
}
|
|
|
|
/**
|
|
* Tests to verify that supported escapes in XML are converted to actual values.
|
|
*/
|
|
@Test
|
|
public void testXmlEscapeToJson(){
|
|
String xmlStr =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<root>"+
|
|
"<rawQuote>\"</rawQuote>"+
|
|
"<euro>A €33</euro>"+
|
|
"<euroX>A €22€</euroX>"+
|
|
"<unknown>some text ©</unknown>"+
|
|
"<known>" " & ' < ></known>"+
|
|
"<high>𝄢 𐅥</high>" +
|
|
"</root>";
|
|
String expectedStr =
|
|
"{\"root\":{" +
|
|
"\"rawQuote\":\"\\\"\"," +
|
|
"\"euro\":\"A €33\"," +
|
|
"\"euroX\":\"A €22€\"," +
|
|
"\"unknown\":\"some text ©\"," +
|
|
"\"known\":\"\\\" \\\" & ' < >\"," +
|
|
"\"high\":\"𝄢 𐅥\""+
|
|
"}}";
|
|
|
|
compareStringToJSONObject(xmlStr, expectedStr);
|
|
compareReaderToJSONObject(xmlStr, expectedStr);
|
|
compareFileToJSONObject(xmlStr, expectedStr);
|
|
}
|
|
|
|
/**
|
|
* Tests that control characters are escaped.
|
|
*/
|
|
@Test
|
|
public void testJsonToXmlEscape(){
|
|
final String jsonSrc = "{\"amount\":\"10,00 €\","
|
|
+ "\"description\":\"Ação Válida\u0085\","
|
|
+ "\"xmlEntities\":\"\\\" ' & < >\""
|
|
+ "}";
|
|
JSONObject json = new JSONObject(jsonSrc);
|
|
String xml = XML.toString(json);
|
|
//test control character not existing
|
|
assertFalse("Escaping \u0085 failed. Found in XML output.", xml.contains("\u0085"));
|
|
assertTrue("Escaping \u0085 failed. Entity not found in XML output.", xml.contains("…"));
|
|
// test normal unicode existing
|
|
assertTrue("Escaping € failed. Not found in XML output.", xml.contains("€"));
|
|
assertTrue("Escaping ç failed. Not found in XML output.", xml.contains("ç"));
|
|
assertTrue("Escaping ã failed. Not found in XML output.", xml.contains("ã"));
|
|
assertTrue("Escaping á failed. Not found in XML output.", xml.contains("á"));
|
|
// test XML Entities converted
|
|
assertTrue("Escaping \" failed. Not found in XML output.", xml.contains("""));
|
|
assertTrue("Escaping ' failed. Not found in XML output.", xml.contains("'"));
|
|
assertTrue("Escaping & failed. Not found in XML output.", xml.contains("&"));
|
|
assertTrue("Escaping < failed. Not found in XML output.", xml.contains("<"));
|
|
assertTrue("Escaping > failed. Not found in XML output.", xml.contains(">"));
|
|
}
|
|
|
|
/**
|
|
* Valid XML with comments to JSONObject
|
|
*/
|
|
@Test
|
|
public void shouldHandleCommentsInXML() {
|
|
|
|
String xmlStr =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<!-- this is a comment -->\n"+
|
|
"<addresses>\n"+
|
|
" <address>\n"+
|
|
" <![CDATA[ this is -- <another> comment ]]>\n"+
|
|
" <name>Joe Tester</name>\n"+
|
|
" <!-- this is a - multi line \n"+
|
|
" comment -->\n"+
|
|
" <street>Baker street 5</street>\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
JSONObject jsonObject = XML.toJSONObject(xmlStr);
|
|
String expectedStr = "{\"addresses\":{\"address\":{\"street\":\"Baker "+
|
|
"street 5\",\"name\":\"Joe Tester\",\"content\":\" this is -- "+
|
|
"<another> comment \"}}}";
|
|
JSONObject expectedJsonObject = new JSONObject(expectedStr);
|
|
Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject);
|
|
}
|
|
|
|
/**
|
|
* Valid XML to XML.toString()
|
|
*/
|
|
@Test
|
|
public void shouldHandleToString() {
|
|
String xmlStr =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
|
|
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
|
|
" <address>\n"+
|
|
" <name>[CDATA[Joe & T > e < s " t ' er]]</name>\n"+
|
|
" <street>Baker street 5</street>\n"+
|
|
" <ArrayOfNum>1, 2, 3, 4.1, 5.2</ArrayOfNum>\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Converting a JSON doc containing '>' content to JSONObject, then
|
|
* XML.toString() should result in valid XML.
|
|
*/
|
|
@Test
|
|
public void shouldHandleContentNoArraytoString() {
|
|
String expectedStr = "{\"addresses\":{\"content\":\">\"}}";
|
|
JSONObject expectedJsonObject = new JSONObject(expectedStr);
|
|
String finalStr = XML.toString(expectedJsonObject);
|
|
String expectedFinalStr = "<addresses>></addresses>";
|
|
assertEquals("Should handle expectedFinal: ["+expectedStr+"] final: ["+
|
|
finalStr+"]", expectedFinalStr, 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\":{" +
|
|
"\"content\":[1, 2, 3]}}";
|
|
JSONObject expectedJsonObject = new JSONObject(expectedStr);
|
|
String finalStr = XML.toString(expectedJsonObject);
|
|
String expectedFinalStr = "<addresses>"+
|
|
"1\n2\n3</addresses>";
|
|
assertEquals("Should handle expectedFinal: ["+expectedStr+"] final: ["+
|
|
finalStr+"]", expectedFinalStr, 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\":{"+
|
|
"\"something\":[1, 2, 3]}}";
|
|
JSONObject expectedJsonObject = new JSONObject(expectedStr);
|
|
String finalStr = XML.toString(expectedJsonObject);
|
|
String expectedFinalStr = "<addresses>"+
|
|
"<something>1</something><something>2</something><something>3</something>"+
|
|
"</addresses>";
|
|
assertEquals("Should handle expectedFinal: ["+expectedStr+"] final: ["+
|
|
finalStr+"]", expectedFinalStr, 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 = "<jo></jo>";
|
|
String output1 = XML.toString(jo1,"jo");
|
|
assertEquals("Expected an empty root tag", expected, output1);
|
|
String output2 = XML.toString(jo2,"jo");
|
|
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 = "<jo><arr>One</arr><arr></arr><arr>Four</arr></jo>";
|
|
String output1 = XML.toString(jo1,"jo");
|
|
assertEquals("Expected a matching array", expected, output1);
|
|
String output2 = XML.toString(jo2,"jo");
|
|
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 = "<jo><arr>One</arr><arr>Two</arr><arr>Three</arr></jo>";
|
|
String output1 = XML.toString(jo1,"jo");
|
|
assertEquals("Expected a non empty root tag", expected, output1);
|
|
String output2 = XML.toString(jo2,"jo");
|
|
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 = "<jo><arr>One</arr><arr><array>Two</array><array>Three</array></arr><arr>Four</arr></jo>";
|
|
String output1 = XML.toString(jo1,"jo");
|
|
assertEquals("Expected a matching array", expected, output1);
|
|
String output2 = XML.toString(jo2,"jo");
|
|
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);
|
|
JSONObject finalJsonObject = XML.toJSONObject(finalStr);
|
|
String expectedStr = "<addresses><address><name/><nocontent/>"+
|
|
"<outer><array>1</array></outer><outer><array>2</array>"+
|
|
"</outer><outer><array>3</array></outer>"+
|
|
"</address><xsi:noNamespaceSchemaLocation>test.xsd</xsi:noName"+
|
|
"spaceSchemaLocation><xmlns:xsi>http://www.w3.org/2001/XMLSche"+
|
|
"ma-instance</xmlns:xsi></addresses>";
|
|
JSONObject expectedJsonObject = XML.toJSONObject(expectedStr);
|
|
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);
|
|
|
|
/*
|
|
* 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>someValue1</___IllegalNode><Illegal_node>someValue2</Illegal_node>
|
|
*/
|
|
String expected = "<123IllegalNode>someValue1</123IllegalNode><Illegal@node>someValue2</Illegal@node>";
|
|
|
|
assertEquals("length",expected.length(), result.length());
|
|
assertTrue("123IllegalNode",result.contains("<123IllegalNode>someValue1</123IllegalNode>"));
|
|
assertTrue("Illegal@node",result.contains("<Illegal@node>someValue2</Illegal@node>"));
|
|
}
|
|
|
|
/**
|
|
* 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 = "<nullValue/>";
|
|
/**
|
|
* This is the current behavior. JSONObject.NULL is emitted as
|
|
* the string, "null".
|
|
*/
|
|
String actualXML = "<nullValue>null</nullValue>";
|
|
String resultXML = XML.toString(inputJSON);
|
|
assertEquals(actualXML, resultXML);
|
|
}
|
|
|
|
/**
|
|
* Investigate exactly how the "content" keyword works
|
|
*/
|
|
@Test
|
|
public void contentOperations() {
|
|
/*
|
|
* When a standalone <!CDATA[...]] structure is found while parsing XML into a
|
|
* JSONObject, the contents are placed in a string value with key="content".
|
|
*/
|
|
String xmlStr = "<tag1></tag1><![CDATA[if (a < b && a > 0) then return]]><tag2></tag2>";
|
|
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 = "<tag1></tag1><![CDATA[if (a < b && a > 0) then return]]><tag2></tag2><![CDATA[here is another cdata]]>";
|
|
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 = "<tag1>value 1</tag1>";
|
|
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 = "<tag1>value 1</tag1><tag1>2</tag1><tag1>true</tag1>";
|
|
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 = "<tag1>val1<tag2/>val2</tag1>";
|
|
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 = "<tag1>val1<tag2/></tag1>";
|
|
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 = "<tag1>val1<content/></tag1>";
|
|
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.
|
|
* <Profile>
|
|
* <list>
|
|
* <history>
|
|
* <entries>
|
|
* <deviceId>id</deviceId>
|
|
* {"material":[{"stuff":false}]}
|
|
* </entries>
|
|
* </history>
|
|
* </list>
|
|
* </Profile>
|
|
*/
|
|
assertTrue("nothing to test here, see comment on created XML, above", true);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
private void compareStringToJSONObject(String xmlStr, String expectedStr) {
|
|
JSONObject jsonObject = XML.toJSONObject(xmlStr);
|
|
JSONObject expectedJsonObject = new JSONObject(expectedStr);
|
|
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
|
|
*/
|
|
private void compareReaderToJSONObject(String xmlStr, String expectedStr) {
|
|
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) {
|
|
try {
|
|
JSONObject expectedJsonObject = new JSONObject(expectedStr);
|
|
File tempFile = this.testFolder.newFile("fileToJSONObject.xml");
|
|
FileWriter fileWriter = new FileWriter(tempFile);
|
|
try {
|
|
fileWriter.write(xmlStr);
|
|
} finally {
|
|
fileWriter.close();
|
|
}
|
|
|
|
Reader reader = new FileReader(tempFile);
|
|
try {
|
|
JSONObject jsonObject = XML.toJSONObject(reader);
|
|
Util.compareActualVsExpectedJsonObjects(jsonObject,expectedJsonObject);
|
|
} finally {
|
|
reader.close();
|
|
}
|
|
} catch (IOException e) {
|
|
fail("Error: " +e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* JSON string lost leading zero and converted "True" to true.
|
|
*/
|
|
@Test
|
|
public void testToJSONArray_jsonOutput() {
|
|
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><item id=\"01\"/><title>True</title></root>";
|
|
final JSONObject expectedJson = new JSONObject("{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",1,\"00\",0],\"title\":true}}");
|
|
final JSONObject actualJsonOutput = XML.toJSONObject(originalXml, false);
|
|
|
|
Util.compareActualVsExpectedJsonObjects(actualJsonOutput,expectedJson);
|
|
}
|
|
|
|
/**
|
|
* JSON string cannot be reverted to original xml.
|
|
*/
|
|
@Test
|
|
public void testToJSONArray_reversibility() {
|
|
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><item id=\"01\"/><title>True</title></root>";
|
|
final String revertedXml = XML.toString(XML.toJSONObject(originalXml, false));
|
|
|
|
assertNotEquals(revertedXml, originalXml);
|
|
}
|
|
|
|
/**
|
|
* test passes when using the new method toJsonArray.
|
|
*/
|
|
@Test
|
|
public void testToJsonXML() {
|
|
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><item id=\"01\"/><title>True</title></root>";
|
|
final JSONObject expected = new JSONObject("{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",\"1\",\"00\",\"0\"],\"title\":\"True\"}}");
|
|
|
|
final JSONObject actual = XML.toJSONObject(originalXml,true);
|
|
|
|
Util.compareActualVsExpectedJsonObjects(actual, expected);
|
|
|
|
final String reverseXml = XML.toString(actual);
|
|
// this reversal isn't exactly the same. use JSONML for an exact reversal
|
|
// the order of the elements may be differnet as well.
|
|
final String expectedReverseXml = "<root><item><id>01</id></item><id>01</id><id>1</id><id>00</id><id>0</id><title>True</title></root>";
|
|
|
|
assertEquals("length",expectedReverseXml.length(), reverseXml.length());
|
|
assertTrue("array contents", reverseXml.contains("<id>01</id><id>1</id><id>00</id><id>0</id>"));
|
|
assertTrue("item contents", reverseXml.contains("<item><id>01</id></item>"));
|
|
assertTrue("title contents", reverseXml.contains("<title>True</title>"));
|
|
}
|
|
|
|
/**
|
|
* test to validate certain conditions of XML unescaping.
|
|
*/
|
|
@Test
|
|
public void testUnescape() {
|
|
assertEquals("{\"xml\":\"Can cope <;\"}",
|
|
XML.toJSONObject("<xml>Can cope <; </xml>").toString());
|
|
assertEquals("Can cope <; ", XML.unescape("Can cope <; "));
|
|
|
|
assertEquals("{\"xml\":\"Can cope & ;\"}",
|
|
XML.toJSONObject("<xml>Can cope & ; </xml>").toString());
|
|
assertEquals("Can cope & ; ", XML.unescape("Can cope & ; "));
|
|
|
|
assertEquals("{\"xml\":\"Can cope &;\"}",
|
|
XML.toJSONObject("<xml>Can cope &; </xml>").toString());
|
|
assertEquals("Can cope &; ", XML.unescape("Can cope &; "));
|
|
|
|
// unicode entity
|
|
assertEquals("{\"xml\":\"Can cope 4;\"}",
|
|
XML.toJSONObject("<xml>Can cope 4; </xml>").toString());
|
|
assertEquals("Can cope 4; ", XML.unescape("Can cope 4; "));
|
|
|
|
// double escaped
|
|
assertEquals("{\"xml\":\"Can cope <\"}",
|
|
XML.toJSONObject("<xml>Can cope &lt; </xml>").toString());
|
|
assertEquals("Can cope < ", XML.unescape("Can cope &lt; "));
|
|
|
|
assertEquals("{\"xml\":\"Can cope 4\"}",
|
|
XML.toJSONObject("<xml>Can cope &#x34; </xml>").toString());
|
|
assertEquals("Can cope 4 ", XML.unescape("Can cope &#x34; "));
|
|
|
|
}
|
|
|
|
/**
|
|
* test passes when xsi:nil="true" converting to null (JSON specification-like nil conversion enabled)
|
|
*/
|
|
@Test
|
|
public void testToJsonWithNullWhenNilConversionEnabled() {
|
|
final String originalXml = "<root><id xsi:nil=\"true\"/></root>";
|
|
final String expectedJsonString = "{\"root\":{\"id\":null}}";
|
|
|
|
final JSONObject json = XML.toJSONObject(originalXml,
|
|
new XMLParserConfiguration()
|
|
.withKeepStrings(false)
|
|
.withcDataTagName("content")
|
|
.withConvertNilAttributeToNull(true));
|
|
assertEquals(expectedJsonString, json.toString());
|
|
}
|
|
|
|
/**
|
|
* test passes when xsi:nil="true" not converting to null (JSON specification-like nil conversion disabled)
|
|
*/
|
|
@Test
|
|
public void testToJsonWithNullWhenNilConversionDisabled() {
|
|
final String originalXml = "<root><id xsi:nil=\"true\"/></root>";
|
|
final String expectedJsonString = "{\"root\":{\"id\":{\"xsi:nil\":true}}}";
|
|
|
|
final JSONObject json = XML.toJSONObject(originalXml, new XMLParserConfiguration());
|
|
assertEquals(expectedJsonString, json.toString());
|
|
}
|
|
|
|
/**
|
|
* Tests to verify that supported escapes in XML are converted to actual values.
|
|
*/
|
|
@Test
|
|
public void testIssue537CaseSensitiveHexEscapeMinimal(){
|
|
String xmlStr =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<root>Neutrophils.Hypersegmented | Bld-Ser-Plas</root>";
|
|
String expectedStr =
|
|
"{\"root\":\"Neutrophils.Hypersegmented | Bld-Ser-Plas\"}";
|
|
JSONObject xmlJSONObj = XML.toJSONObject(xmlStr, true);
|
|
JSONObject expected = new JSONObject(expectedStr);
|
|
Util.compareActualVsExpectedJsonObjects(xmlJSONObj, expected);
|
|
}
|
|
|
|
/**
|
|
* Tests to verify that supported escapes in XML are converted to actual values.
|
|
*/
|
|
@Test
|
|
public void testIssue537CaseSensitiveHexEscapeFullFile(){
|
|
try {
|
|
InputStream xmlStream = null;
|
|
try {
|
|
xmlStream = XMLTest.class.getClassLoader().getResourceAsStream("Issue537.xml");
|
|
Reader xmlReader = new InputStreamReader(xmlStream, Charset.forName("UTF-8"));
|
|
JSONObject actual = XML.toJSONObject(xmlReader, true);
|
|
InputStream jsonStream = null;
|
|
try {
|
|
jsonStream = XMLTest.class.getClassLoader().getResourceAsStream("Issue537.json");
|
|
final JSONObject expected = new JSONObject(new JSONTokener(jsonStream));
|
|
Util.compareActualVsExpectedJsonObjects(actual,expected);
|
|
} finally {
|
|
if (jsonStream != null) {
|
|
jsonStream.close();
|
|
}
|
|
}
|
|
} finally {
|
|
if (xmlStream != null) {
|
|
xmlStream.close();
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
fail("file writer error: " +e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests to verify that supported escapes in XML are converted to actual values.
|
|
*/
|
|
@Test
|
|
public void testIssue537CaseSensitiveHexUnEscapeDirect(){
|
|
String origStr =
|
|
"Neutrophils.Hypersegmented | Bld-Ser-Plas";
|
|
String expectedStr =
|
|
"Neutrophils.Hypersegmented | Bld-Ser-Plas";
|
|
String actualStr = XML.unescape(origStr);
|
|
|
|
assertEquals("Case insensitive Entity unescape", expectedStr, actualStr);
|
|
}
|
|
|
|
/**
|
|
* test passes when xsi:type="java.lang.String" not converting to string
|
|
*/
|
|
@Test
|
|
public void testToJsonWithTypeWhenTypeConversionDisabled() {
|
|
String originalXml = "<root><id xsi:type=\"string\">1234</id></root>";
|
|
String expectedJsonString = "{\"root\":{\"id\":{\"xsi:type\":\"string\",\"content\":1234}}}";
|
|
JSONObject expectedJson = new JSONObject(expectedJsonString);
|
|
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration());
|
|
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
|
|
}
|
|
|
|
/**
|
|
* test passes when xsi:type="java.lang.String" converting to String
|
|
*/
|
|
@Test
|
|
public void testToJsonWithTypeWhenTypeConversionEnabled() {
|
|
String originalXml = "<root><id1 xsi:type=\"string\">1234</id1>"
|
|
+ "<id2 xsi:type=\"integer\">1234</id2></root>";
|
|
String expectedJsonString = "{\"root\":{\"id2\":1234,\"id1\":\"1234\"}}";
|
|
JSONObject expectedJson = new JSONObject(expectedJsonString);
|
|
Map<String, XMLXsiTypeConverter<?>> xsiTypeMap = new HashMap<String, XMLXsiTypeConverter<?>>();
|
|
xsiTypeMap.put("string", new XMLXsiTypeConverter<String>() {
|
|
@Override public String convert(final String value) {
|
|
return value;
|
|
}
|
|
});
|
|
xsiTypeMap.put("integer", new XMLXsiTypeConverter<Integer>() {
|
|
@Override public Integer convert(final String value) {
|
|
return Integer.valueOf(value);
|
|
}
|
|
});
|
|
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withXsiTypeMap(xsiTypeMap));
|
|
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
|
|
}
|
|
|
|
@Test
|
|
public void testToJsonWithXSITypeWhenTypeConversionEnabled() {
|
|
String originalXml = "<root><asString xsi:type=\"string\">12345</asString><asInt "
|
|
+ "xsi:type=\"integer\">54321</asInt></root>";
|
|
String expectedJsonString = "{\"root\":{\"asString\":\"12345\",\"asInt\":54321}}";
|
|
JSONObject expectedJson = new JSONObject(expectedJsonString);
|
|
Map<String, XMLXsiTypeConverter<?>> xsiTypeMap = new HashMap<String, XMLXsiTypeConverter<?>>();
|
|
xsiTypeMap.put("string", new XMLXsiTypeConverter<String>() {
|
|
@Override public String convert(final String value) {
|
|
return value;
|
|
}
|
|
});
|
|
xsiTypeMap.put("integer", new XMLXsiTypeConverter<Integer>() {
|
|
@Override public Integer convert(final String value) {
|
|
return Integer.valueOf(value);
|
|
}
|
|
});
|
|
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withXsiTypeMap(xsiTypeMap));
|
|
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
|
|
}
|
|
|
|
@Test
|
|
public void testToJsonWithXSITypeWhenTypeConversionNotEnabledOnOne() {
|
|
String originalXml = "<root><asString xsi:type=\"string\">12345</asString><asInt>54321</asInt></root>";
|
|
String expectedJsonString = "{\"root\":{\"asString\":\"12345\",\"asInt\":54321}}";
|
|
JSONObject expectedJson = new JSONObject(expectedJsonString);
|
|
Map<String, XMLXsiTypeConverter<?>> xsiTypeMap = new HashMap<String, XMLXsiTypeConverter<?>>();
|
|
xsiTypeMap.put("string", new XMLXsiTypeConverter<String>() {
|
|
@Override public String convert(final String value) {
|
|
return value;
|
|
}
|
|
});
|
|
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withXsiTypeMap(xsiTypeMap));
|
|
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
|
|
}
|
|
|
|
@Test
|
|
public void testXSITypeMapNotModifiable() {
|
|
Map<String, XMLXsiTypeConverter<?>> xsiTypeMap = new HashMap<String, XMLXsiTypeConverter<?>>();
|
|
XMLParserConfiguration config = new XMLParserConfiguration().withXsiTypeMap(xsiTypeMap);
|
|
xsiTypeMap.put("string", new XMLXsiTypeConverter<String>() {
|
|
@Override public String convert(final String value) {
|
|
return value;
|
|
}
|
|
});
|
|
assertEquals("Config Conversion Map size is expected to be 0", 0, config.getXsiTypeMap().size());
|
|
|
|
try {
|
|
config.getXsiTypeMap().put("boolean", new XMLXsiTypeConverter<Boolean>() {
|
|
@Override public Boolean convert(final String value) {
|
|
return Boolean.valueOf(value);
|
|
}
|
|
});
|
|
fail("Expected to be unable to modify the config");
|
|
} catch (Exception ignored) { }
|
|
}
|
|
|
|
@Test
|
|
public void testIndentComplicatedJsonObject(){
|
|
String str = "{\n" +
|
|
" \"success\": true,\n" +
|
|
" \"error\": null,\n" +
|
|
" \"response\": [\n" +
|
|
" {\n" +
|
|
" \"timestamp\": 1664917200,\n" +
|
|
" \"dateTimeISO\": \"2022-10-05T00:00:00+03:00\",\n" +
|
|
" \"loc\": {\n" +
|
|
" \"lat\": 39.91987,\n" +
|
|
" \"long\": 32.85427\n" +
|
|
" },\n" +
|
|
" \"place\": {\n" +
|
|
" \"name\": \"ankara\",\n" +
|
|
" \"state\": \"an\",\n" +
|
|
" \"country\": \"tr\"\n" +
|
|
" },\n" +
|
|
" \"profile\": {\n" +
|
|
" \"tz\": \"Europe/Istanbul\"\n" +
|
|
" },\n" +
|
|
" \"sun\": {\n" +
|
|
" \"rise\": 1664941721,\n" +
|
|
" \"riseISO\": \"2022-10-05T06:48:41+03:00\",\n" +
|
|
" \"set\": 1664983521,\n" +
|
|
" \"setISO\": \"2022-10-05T18:25:21+03:00\",\n" +
|
|
" \"transit\": 1664962621,\n" +
|
|
" \"transitISO\": \"2022-10-05T12:37:01+03:00\",\n" +
|
|
" \"midnightSun\": false,\n" +
|
|
" \"polarNight\": false,\n" +
|
|
" \"twilight\": {\n" +
|
|
" \"civilBegin\": 1664940106,\n" +
|
|
" \"civilBeginISO\": \"2022-10-05T06:21:46+03:00\",\n" +
|
|
" \"civilEnd\": 1664985136,\n" +
|
|
" \"civilEndISO\": \"2022-10-05T18:52:16+03:00\",\n" +
|
|
" \"nauticalBegin\": 1664938227,\n" +
|
|
" \"nauticalBeginISO\": \"2022-10-05T05:50:27+03:00\",\n" +
|
|
" \"nauticalEnd\": 1664987015,\n" +
|
|
" \"nauticalEndISO\": \"2022-10-05T19:23:35+03:00\",\n" +
|
|
" \"astronomicalBegin\": 1664936337,\n" +
|
|
" \"astronomicalBeginISO\": \"2022-10-05T05:18:57+03:00\",\n" +
|
|
" \"astronomicalEnd\": 1664988905,\n" +
|
|
" \"astronomicalEndISO\": \"2022-10-05T19:55:05+03:00\"\n" +
|
|
" }\n" +
|
|
" },\n" +
|
|
" \"moon\": {\n" +
|
|
" \"rise\": 1664976480,\n" +
|
|
" \"riseISO\": \"2022-10-05T16:28:00+03:00\",\n" +
|
|
" \"set\": 1664921520,\n" +
|
|
" \"setISO\": \"2022-10-05T01:12:00+03:00\",\n" +
|
|
" \"transit\": 1664994240,\n" +
|
|
" \"transitISO\": \"2022-10-05T21:24:00+03:00\",\n" +
|
|
" \"underfoot\": 1664949360,\n" +
|
|
" \"underfootISO\": \"2022-10-05T08:56:00+03:00\",\n" +
|
|
" \"phase\": {\n" +
|
|
" \"phase\": 0.3186,\n" +
|
|
" \"name\": \"waxing gibbous\",\n" +
|
|
" \"illum\": 71,\n" +
|
|
" \"age\": 9.41,\n" +
|
|
" \"angle\": 0.55\n" +
|
|
" }\n" +
|
|
" }\n" +
|
|
" }\n" +
|
|
" ]\n" +
|
|
"}" ;
|
|
JSONObject jsonObject = new JSONObject(str);
|
|
String actualIndentedXmlString = XML.toString(jsonObject, 1);
|
|
JSONObject actualJsonObject = XML.toJSONObject(actualIndentedXmlString);
|
|
String expected = "<success>true</success>\n" +
|
|
"<response>\n" +
|
|
" <dateTimeISO>2022-10-05T00:00:00+03:00</dateTimeISO>\n" +
|
|
" <loc>\n" +
|
|
" <lat>39.91987</lat>\n" +
|
|
" <long>32.85427</long>\n" +
|
|
" </loc>\n" +
|
|
" <moon>\n" +
|
|
" <phase>\n" +
|
|
" <phase>0.3186</phase>\n" +
|
|
" <name>waxing gibbous</name>\n" +
|
|
" <angle>0.55</angle>\n" +
|
|
" <illum>71</illum>\n" +
|
|
" <age>9.41</age>\n" +
|
|
" </phase>\n" +
|
|
" <setISO>2022-10-05T01:12:00+03:00</setISO>\n" +
|
|
" <underfoot>1664949360</underfoot>\n" +
|
|
" <set>1664921520</set>\n" +
|
|
" <transit>1664994240</transit>\n" +
|
|
" <transitISO>2022-10-05T21:24:00+03:00</transitISO>\n" +
|
|
" <riseISO>2022-10-05T16:28:00+03:00</riseISO>\n" +
|
|
" <rise>1664976480</rise>\n" +
|
|
" <underfootISO>2022-10-05T08:56:00+03:00</underfootISO>\n" +
|
|
" </moon>\n" +
|
|
" <profile>\n" +
|
|
" <tz>Europe/Istanbul</tz>\n" +
|
|
" </profile>\n" +
|
|
" <place>\n" +
|
|
" <country>tr</country>\n" +
|
|
" <name>ankara</name>\n" +
|
|
" <state>an</state>\n" +
|
|
" </place>\n" +
|
|
" <sun>\n" +
|
|
" <setISO>2022-10-05T18:25:21+03:00</setISO>\n" +
|
|
" <midnightSun>false</midnightSun>\n" +
|
|
" <set>1664983521</set>\n" +
|
|
" <transit>1664962621</transit>\n" +
|
|
" <polarNight>false</polarNight>\n" +
|
|
" <transitISO>2022-10-05T12:37:01+03:00</transitISO>\n" +
|
|
" <riseISO>2022-10-05T06:48:41+03:00</riseISO>\n" +
|
|
" <rise>1664941721</rise>\n" +
|
|
" <twilight>\n" +
|
|
" <civilEnd>1664985136</civilEnd>\n" +
|
|
" <astronomicalBegin>1664936337</astronomicalBegin>\n" +
|
|
" <astronomicalEnd>1664988905</astronomicalEnd>\n" +
|
|
" <astronomicalBeginISO>2022-10-05T05:18:57+03:00</astronomicalBeginISO>\n" +
|
|
" <civilBegin>1664940106</civilBegin>\n" +
|
|
" <nauticalEndISO>2022-10-05T19:23:35+03:00</nauticalEndISO>\n" +
|
|
" <astronomicalEndISO>2022-10-05T19:55:05+03:00</astronomicalEndISO>\n" +
|
|
" <nauticalBegin>1664938227</nauticalBegin>\n" +
|
|
" <nauticalEnd>1664987015</nauticalEnd>\n" +
|
|
" <nauticalBeginISO>2022-10-05T05:50:27+03:00</nauticalBeginISO>\n" +
|
|
" <civilBeginISO>2022-10-05T06:21:46+03:00</civilBeginISO>\n" +
|
|
" <civilEndISO>2022-10-05T18:52:16+03:00</civilEndISO>\n" +
|
|
" </twilight>\n" +
|
|
" </sun>\n" +
|
|
" <timestamp>1664917200</timestamp>\n" +
|
|
"</response>\n" +
|
|
"<error>null</error>\n";
|
|
JSONObject expectedJsonObject = XML.toJSONObject(expected);
|
|
assertTrue(expectedJsonObject.similar(actualJsonObject));
|
|
|
|
|
|
}
|
|
@Test
|
|
public void testIndentSimpleJsonObject(){
|
|
String str = "{ \"employee\": { \n" +
|
|
" \"name\": \"sonoo\", \n" +
|
|
" \"salary\": 56000, \n" +
|
|
" \"married\": true \n" +
|
|
" }}";
|
|
JSONObject jsonObject = new JSONObject(str);
|
|
String actual = XML.toString(jsonObject, "Test", 2);
|
|
JSONObject actualJsonObject = XML.toJSONObject(actual);
|
|
String expected = "<Test>\n" +
|
|
" <employee>\n" +
|
|
" <name>sonoo</name>\n" +
|
|
" <salary>56000</salary>\n" +
|
|
" <married>true</married>\n" +
|
|
" </employee>\n" +
|
|
"</Test>\n";
|
|
JSONObject expectedJsonObject = XML.toJSONObject(expected);
|
|
assertTrue(expectedJsonObject.similar(actualJsonObject));
|
|
}
|
|
|
|
@Test
|
|
public void testIndentSimpleJsonArray(){
|
|
String str = "[ \n" +
|
|
" {\"name\":\"Ram\", \"email\":\"Ram@gmail.com\"}, \n" +
|
|
" {\"name\":\"Bob\", \"email\":\"bob32@gmail.com\"} \n" +
|
|
"] ";
|
|
JSONArray jsonObject = new JSONArray(str);
|
|
String actual = XML.toString(jsonObject, 2);
|
|
JSONObject actualJsonObject = XML.toJSONObject(actual);
|
|
String expected = "<array>\n" +
|
|
" <name>Ram</name>\n" +
|
|
" <email>Ram@gmail.com</email>\n" +
|
|
"</array>\n" +
|
|
"<array>\n" +
|
|
" <name>Bob</name>\n" +
|
|
" <email>bob32@gmail.com</email>\n" +
|
|
"</array>\n";
|
|
JSONObject expectedJsonObject = XML.toJSONObject(expected);
|
|
assertTrue(expectedJsonObject.similar(actualJsonObject));
|
|
|
|
|
|
}
|
|
|
|
@Test
|
|
public void testIndentComplicatedJsonObjectWithArrayAndWithConfig(){
|
|
try {
|
|
InputStream jsonStream = null;
|
|
try {
|
|
jsonStream = XMLTest.class.getClassLoader().getResourceAsStream("Issue593.json");
|
|
final JSONObject object = new JSONObject(new JSONTokener(jsonStream));
|
|
String actualString = XML.toString(object, null, XMLParserConfiguration.KEEP_STRINGS,2);
|
|
InputStream xmlStream = null;
|
|
try {
|
|
xmlStream = XMLTest.class.getClassLoader().getResourceAsStream("Issue593.xml");
|
|
int bufferSize = 1024;
|
|
char[] buffer = new char[bufferSize];
|
|
StringBuilder expected = new StringBuilder();
|
|
Reader in = new InputStreamReader(xmlStream, "UTF-8");
|
|
for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) {
|
|
expected.append(buffer, 0, numRead);
|
|
}
|
|
assertEquals(expected.toString().replaceAll("\\n|\\r\\n", System.lineSeparator()),
|
|
actualString.replaceAll("\\n|\\r\\n", System.lineSeparator()));
|
|
} finally {
|
|
if (xmlStream != null) {
|
|
xmlStream.close();
|
|
}
|
|
}
|
|
} finally {
|
|
if (jsonStream != null) {
|
|
jsonStream.close();
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
fail("file writer error: " +e.getMessage());
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMaxNestingDepthOf42IsRespected() {
|
|
final String wayTooLongMalformedXML = new String(new char[6000]).replace("\0", "<a>");
|
|
|
|
final int maxNestingDepth = 42;
|
|
|
|
try {
|
|
XML.toJSONObject(wayTooLongMalformedXML, XMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
|
|
|
|
fail("Expecting a JSONException");
|
|
} catch (JSONException e) {
|
|
assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">",
|
|
e.getMessage().startsWith("Maximum nesting depth of " + maxNestingDepth));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMaxNestingDepthIsRespectedWithValidXML() {
|
|
final String perfectlyFineXML = "<Test>\n" +
|
|
" <employee>\n" +
|
|
" <name>sonoo</name>\n" +
|
|
" <salary>56000</salary>\n" +
|
|
" <married>true</married>\n" +
|
|
" </employee>\n" +
|
|
"</Test>\n";
|
|
|
|
final int maxNestingDepth = 1;
|
|
|
|
try {
|
|
XML.toJSONObject(perfectlyFineXML, XMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
|
|
|
|
fail("Expecting a JSONException");
|
|
} catch (JSONException e) {
|
|
assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">",
|
|
e.getMessage().startsWith("Maximum nesting depth of " + maxNestingDepth));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMaxNestingDepthWithValidFittingXML() {
|
|
final String perfectlyFineXML = "<Test>\n" +
|
|
" <employee>\n" +
|
|
" <name>sonoo</name>\n" +
|
|
" <salary>56000</salary>\n" +
|
|
" <married>true</married>\n" +
|
|
" </employee>\n" +
|
|
"</Test>\n";
|
|
|
|
final int maxNestingDepth = 3;
|
|
|
|
try {
|
|
XML.toJSONObject(perfectlyFineXML, XMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
|
|
} catch (JSONException e) {
|
|
e.printStackTrace();
|
|
fail("XML document should be parsed as its maximum depth fits the maxNestingDepth " +
|
|
"parameter of the XMLParserConfiguration used");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|