mirror of
https://github.com/stleary/JSON-java.git
synced 2025-08-03 03:15:32 -04:00
This reverts commit 14f71274f79ac50afb316845539f4195facc33ee, reversing changes made to 054786e300d0fc38f0cf7fc0f2db4d9b39cb6443.
1433 lines
62 KiB
Java
1433 lines
62 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 shouldCreateExplicitEndTagWithEmptyValueWhenConfigured(){
|
|
String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}";
|
|
JSONObject jsonObject = new JSONObject(jsonString);
|
|
String expectedXmlString = "<encloser><outer><innerOne></innerOne><innerTwo>two</innerTwo></outer></encloser>";
|
|
String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(true));
|
|
JSONObject actualJsonObject = XML.toJSONObject(xmlForm);
|
|
JSONObject expectedJsonObject = XML.toJSONObject(expectedXmlString);
|
|
assertTrue(expectedJsonObject.similar(actualJsonObject));
|
|
}
|
|
|
|
@Test
|
|
public void shouldNotCreateExplicitEndTagWithEmptyValueWhenNotConfigured(){
|
|
String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}";
|
|
JSONObject jsonObject = new JSONObject(jsonString);
|
|
String expectedXmlString = "<encloser><outer><innerOne/><innerTwo>two</innerTwo></outer></encloser>";
|
|
String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(false));
|
|
JSONObject actualJsonObject = XML.toJSONObject(xmlForm);
|
|
JSONObject expectedJsonObject = XML.toJSONObject(expectedXmlString);
|
|
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 = XMLTest.class.getClassLoader().getResourceAsStream("Issue593.json")) {
|
|
final JSONObject object = new JSONObject(new JSONTokener(jsonStream));
|
|
String actualString = XML.toString(object, null, XMLParserConfiguration.KEEP_STRINGS, 2);
|
|
try (InputStream 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);
|
|
}
|
|
assertTrue(XML.toJSONObject(expected.toString()).similar(XML.toJSONObject(actualString)));
|
|
}
|
|
} 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");
|
|
}
|
|
}
|
|
@Test
|
|
public void testWithWhitespaceTrimmingDisabled() {
|
|
String originalXml = "<testXml> Test Whitespace String \t </testXml>";
|
|
|
|
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(false));
|
|
String expectedJsonString = "{\"testXml\":\" Test Whitespace String \t \"}";
|
|
JSONObject expectedJson = new JSONObject(expectedJsonString);
|
|
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
|
|
}
|
|
@Test
|
|
public void testNestedWithWhitespaceTrimmingDisabled() {
|
|
String originalXml =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses>\n"+
|
|
" <address>\n"+
|
|
" <name> Sherlock Holmes </name>\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
|
|
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(false));
|
|
String expectedJsonString = "{\"addresses\":{\"address\":{\"name\":\" Sherlock Holmes \"}}}";
|
|
JSONObject expectedJson = new JSONObject(expectedJsonString);
|
|
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
|
|
}
|
|
@Test
|
|
public void shouldTrimWhitespaceDoesNotSupportTagsEqualingCDataTagName() {
|
|
// When using withShouldTrimWhitespace = true, input containing tags with same name as cDataTagName is unsupported and should not be used in conjunction
|
|
String originalXml =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses>\n"+
|
|
" <address>\n"+
|
|
" <content> Sherlock Holmes </content>\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
|
|
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(false).withcDataTagName("content"));
|
|
String expectedJsonString = "{\"addresses\":{\"address\":[[\"\\n \",\" Sherlock Holmes \",\"\\n \"]]}}";
|
|
JSONObject expectedJson = new JSONObject(expectedJsonString);
|
|
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
|
|
}
|
|
@Test
|
|
public void shouldTrimWhitespaceEnabledDropsTagsEqualingCDataTagNameButValueRemains() {
|
|
String originalXml =
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
|
|
"<addresses>\n"+
|
|
" <address>\n"+
|
|
" <content> Sherlock Holmes </content>\n"+
|
|
" </address>\n"+
|
|
"</addresses>";
|
|
|
|
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(true).withcDataTagName("content"));
|
|
String expectedJsonString = "{\"addresses\":{\"address\":\"Sherlock Holmes\"}}";
|
|
JSONObject expectedJson = new JSONObject(expectedJsonString);
|
|
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
|
|
}
|
|
@Test
|
|
public void testWithWhitespaceTrimmingEnabled() {
|
|
String originalXml = "<testXml> Test Whitespace String \t </testXml>";
|
|
|
|
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(true));
|
|
String expectedJsonString = "{\"testXml\":\"Test Whitespace String\"}";
|
|
JSONObject expectedJson = new JSONObject(expectedJsonString);
|
|
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
|
|
}
|
|
@Test
|
|
public void testWithWhitespaceTrimmingEnabledByDefault() {
|
|
String originalXml = "<testXml> Test Whitespace String \t </testXml>";
|
|
|
|
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration());
|
|
String expectedJsonString = "{\"testXml\":\"Test Whitespace String\"}";
|
|
JSONObject expectedJson = new JSONObject(expectedJsonString);
|
|
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
|
|
}
|
|
|
|
@Test
|
|
public void clarifyCurrentBehavior() {
|
|
|
|
// Behavior documented in #826
|
|
// After reverting the code, amount is stored as numeric, and phone is stored as string
|
|
String str1 =
|
|
" <datatypes>\n" +
|
|
" <telephone>0123456789</telephone>\n" +
|
|
" <amount>0.1230</amount>\n" +
|
|
" <boolean>true</boolean>\n" +
|
|
" </datatypes>";
|
|
JSONObject jsonObject1 = XML.toJSONObject(str1,
|
|
new XMLParserConfiguration().withKeepStrings(false));
|
|
assertEquals(jsonObject1.getJSONObject("datatypes").getFloat("amount"), 0.123, .1);
|
|
assertEquals(jsonObject1.getJSONObject("datatypes").getString("telephone"), "0123456789");
|
|
|
|
|
|
// Behavior documented in #852
|
|
// After reverting the code, value is still stored as a number. This is due to how XML.isDecimalNotation() works
|
|
// and is probably a bug. JSONObject has a similar problem.
|
|
String str2 = "<color> <color_type>primary</color_type> <value>008E97</value> </color>";
|
|
JSONObject jsonObject2 = XML.toJSONObject(str2);
|
|
assertEquals(jsonObject2.getJSONObject("color").getLong("value"), 0e897, .1);
|
|
|
|
// Workaround for now is to use keepStrings
|
|
JSONObject jsonObject3 = XML.toJSONObject(str2, new XMLParserConfiguration().withKeepStrings(true));
|
|
assertEquals(jsonObject3.getJSONObject("color").getString("value"), "008E97");
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|