mirror of
https://github.com/stleary/JSON-java.git
synced 2026-03-20 00:00:50 -04:00
Compare commits
18 Commits
20251224
...
license-cl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff264ef647 | ||
|
|
a37aa69480 | ||
|
|
510a03ac36 | ||
|
|
538afc3d78 | ||
|
|
d092d0903c | ||
|
|
7a8da886e7 | ||
|
|
0737e04f8a | ||
|
|
592e7828d9 | ||
|
|
6c1bfbc7a5 | ||
|
|
534ce3c4d1 | ||
|
|
9d14246bee | ||
|
|
995fb840f7 | ||
|
|
e635f40238 | ||
|
|
d5e744ca90 | ||
|
|
e0c4086168 | ||
|
|
96353de304 | ||
|
|
8cbb4d5bb3 | ||
|
|
421abfdc1f |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,3 +16,6 @@ build
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
.gitmodules
|
||||
|
||||
# ignore compiled class files
|
||||
*.class
|
||||
|
||||
13
README.md
13
README.md
@@ -9,6 +9,7 @@ JSON in Java [package org.json]
|
||||
[](https://mvnrepository.com/artifact/org.json/json)
|
||||
[](https://github.com/stleary/JSON-java/actions/workflows/pipeline.yml)
|
||||
[](https://github.com/stleary/JSON-java/actions/workflows/codeql-analysis.yml)
|
||||
[](https://javadoc.io/doc/org.json/json)
|
||||
|
||||
**[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20251224/json-20251224.jar)**
|
||||
|
||||
@@ -19,6 +20,8 @@ JSON in Java [package org.json]
|
||||
|
||||
The JSON-Java package is a reference implementation that demonstrates how to parse JSON documents into Java objects and how to generate new JSON documents from the Java classes.
|
||||
|
||||
The files in this package implement JSON encoders and decoders. The package can also convert between JSON and XML, HTTP headers, Cookies, and CDL.
|
||||
|
||||
Project goals include:
|
||||
* Reliable and consistent results
|
||||
* Adherence to the JSON specification
|
||||
@@ -28,8 +31,16 @@ Project goals include:
|
||||
* Maintain backward compatibility
|
||||
* Designed and tested to use on Java versions 1.6 - 25
|
||||
|
||||
# License Clarification
|
||||
This project is in the public domain. This means:
|
||||
* You can use this code for any purpose, including commercial projects
|
||||
* No attribution or credit is required
|
||||
* You can modify, distribute, and sublicense freely
|
||||
* There are no conditions or restrictions whatsoever
|
||||
|
||||
We recognize this can create uncertainty for some corporate legal departments accustomed to standard licenses like MIT or Apache 2.0.
|
||||
If your organization requires a named license for compliance purposes, public domain is functionally equivalent to the Unlicense or CC0 1.0, both of which have been reviewed and accepted by organizations including the Open Source Initiative and Creative Commons. You may reference either when explaining this project's terms to your legal team.
|
||||
|
||||
The files in this package implement JSON encoders and decoders. The package can also convert between JSON and XML, HTTP headers, Cookies, and CDL.
|
||||
|
||||
# If you would like to contribute to this project
|
||||
|
||||
|
||||
@@ -22,6 +22,33 @@ public class JSONML {
|
||||
public JSONML() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely cast parse result to JSONArray with proper type checking.
|
||||
* @param result The result from parse() method
|
||||
* @return JSONArray if result is a JSONArray
|
||||
* @throws JSONException if result is not a JSONArray
|
||||
*/
|
||||
private static JSONArray toJSONArraySafe(Object result) throws JSONException {
|
||||
if (result instanceof JSONArray) {
|
||||
return (JSONArray) result;
|
||||
}
|
||||
throw new JSONException("Expected JSONArray but got " +
|
||||
(result == null ? "null" : result.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely cast parse result to JSONObject with proper type checking.
|
||||
* @param result The result from parse() method
|
||||
* @return JSONObject if result is a JSONObject
|
||||
* @throws JSONException if result is not a JSONObject
|
||||
*/
|
||||
private static JSONObject toJSONObjectSafe(Object result) throws JSONException {
|
||||
if (result instanceof JSONObject) {
|
||||
return (JSONObject) result;
|
||||
}
|
||||
throw new JSONException("Expected JSONObject but got " +
|
||||
(result == null ? "null" : result.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse XML values and store them in a JSONArray.
|
||||
@@ -276,7 +303,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONArray
|
||||
*/
|
||||
public static JSONArray toJSONArray(String string) throws JSONException {
|
||||
return (JSONArray)parse(new XMLTokener(string), true, null, JSONMLParserConfiguration.ORIGINAL, 0);
|
||||
return toJSONArraySafe(parse(new XMLTokener(string), true, null, JSONMLParserConfiguration.ORIGINAL, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -298,7 +325,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONArray
|
||||
*/
|
||||
public static JSONArray toJSONArray(String string, boolean keepStrings) throws JSONException {
|
||||
return (JSONArray)parse(new XMLTokener(string), true, null, keepStrings, 0);
|
||||
return toJSONArraySafe(parse(new XMLTokener(string), true, null, keepStrings, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -323,7 +350,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONArray
|
||||
*/
|
||||
public static JSONArray toJSONArray(String string, JSONMLParserConfiguration config) throws JSONException {
|
||||
return (JSONArray)parse(new XMLTokener(string), true, null, config, 0);
|
||||
return toJSONArraySafe(parse(new XMLTokener(string), true, null, config, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -347,7 +374,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONArray
|
||||
*/
|
||||
public static JSONArray toJSONArray(XMLTokener x, JSONMLParserConfiguration config) throws JSONException {
|
||||
return (JSONArray)parse(x, true, null, config, 0);
|
||||
return toJSONArraySafe(parse(x, true, null, config, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -369,7 +396,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONArray
|
||||
*/
|
||||
public static JSONArray toJSONArray(XMLTokener x, boolean keepStrings) throws JSONException {
|
||||
return (JSONArray)parse(x, true, null, keepStrings, 0);
|
||||
return toJSONArraySafe(parse(x, true, null, keepStrings, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -386,7 +413,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONArray
|
||||
*/
|
||||
public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
|
||||
return (JSONArray)parse(x, true, null, false, 0);
|
||||
return toJSONArraySafe(parse(x, true, null, false, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -404,7 +431,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONObject
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
return (JSONObject)parse(new XMLTokener(string), false, null, false, 0);
|
||||
return toJSONObjectSafe(parse(new XMLTokener(string), false, null, false, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -424,7 +451,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONObject
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
|
||||
return (JSONObject)parse(new XMLTokener(string), false, null, keepStrings, 0);
|
||||
return toJSONObjectSafe(parse(new XMLTokener(string), false, null, keepStrings, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -446,7 +473,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONObject
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string, JSONMLParserConfiguration config) throws JSONException {
|
||||
return (JSONObject)parse(new XMLTokener(string), false, null, config, 0);
|
||||
return toJSONObjectSafe(parse(new XMLTokener(string), false, null, config, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -464,7 +491,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONObject
|
||||
*/
|
||||
public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
|
||||
return (JSONObject)parse(x, false, null, false, 0);
|
||||
return toJSONObjectSafe(parse(x, false, null, false, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -484,7 +511,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONObject
|
||||
*/
|
||||
public static JSONObject toJSONObject(XMLTokener x, boolean keepStrings) throws JSONException {
|
||||
return (JSONObject)parse(x, false, null, keepStrings, 0);
|
||||
return toJSONObjectSafe(parse(x, false, null, keepStrings, 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -506,7 +533,7 @@ public class JSONML {
|
||||
* @throws JSONException Thrown on error converting to a JSONObject
|
||||
*/
|
||||
public static JSONObject toJSONObject(XMLTokener x, JSONMLParserConfiguration config) throws JSONException {
|
||||
return (JSONObject)parse(x, false, null, config, 0);
|
||||
return toJSONObjectSafe(parse(x, false, null, config, 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.io.StringReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* This provides static methods to convert an XML text into a JSONObject, and to
|
||||
@@ -80,7 +81,7 @@ public class XML {
|
||||
public Iterator<Integer> iterator() {
|
||||
return new Iterator<Integer>() {
|
||||
private int nextIndex = 0;
|
||||
private int length = string.length();
|
||||
private final int length = string.length();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
@@ -89,6 +90,9 @@ public class XML {
|
||||
|
||||
@Override
|
||||
public Integer next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
int result = string.codePointAt(this.nextIndex);
|
||||
this.nextIndex += Character.charCount(result);
|
||||
return result;
|
||||
@@ -387,8 +391,13 @@ public class XML {
|
||||
context.append(tagName, JSONObject.NULL);
|
||||
} else if (jsonObject.length() > 0) {
|
||||
context.append(tagName, jsonObject);
|
||||
} else {
|
||||
} else if(context.isEmpty()) { //avoids resetting the array in case of an empty tag in the middle or end
|
||||
context.put(tagName, new JSONArray());
|
||||
if (jsonObject.isEmpty()){
|
||||
context.append(tagName, "");
|
||||
}
|
||||
} else {
|
||||
context.append(tagName, "");
|
||||
}
|
||||
} else {
|
||||
if (nilAttributeFound) {
|
||||
@@ -447,7 +456,11 @@ public class XML {
|
||||
if (config.getForceList().contains(tagName)) {
|
||||
// Force the value to be an array
|
||||
if (jsonObject.length() == 0) {
|
||||
//avoids resetting the array in case of an empty element in the middle or end
|
||||
if(context.isEmpty()) {
|
||||
context.put(tagName, new JSONArray());
|
||||
}
|
||||
context.append(tagName, "");
|
||||
} else if (jsonObject.length() == 1
|
||||
&& jsonObject.opt(config.getcDataTagName()) != null) {
|
||||
context.append(tagName, jsonObject.opt(config.getcDataTagName()));
|
||||
|
||||
@@ -151,23 +151,22 @@ public class XMLTokener extends JSONTokener {
|
||||
/**
|
||||
* Unescape an XML entity encoding;
|
||||
* @param e entity (only the actual entity value, not the preceding & or ending ;
|
||||
* @return
|
||||
* @return the unescaped entity string
|
||||
* @throws JSONException if the entity is malformed
|
||||
*/
|
||||
static String unescapeEntity(String e) {
|
||||
static String unescapeEntity(String e) throws JSONException {
|
||||
// validate
|
||||
if (e == null || e.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
// if our entity is an encoded unicode point, parse it.
|
||||
if (e.charAt(0) == '#') {
|
||||
int cp;
|
||||
if (e.charAt(1) == 'x' || e.charAt(1) == 'X') {
|
||||
// hex encoded unicode
|
||||
cp = Integer.parseInt(e.substring(2), 16);
|
||||
} else {
|
||||
// decimal encoded unicode
|
||||
cp = Integer.parseInt(e.substring(1));
|
||||
if (e.length() < 2) {
|
||||
throw new JSONException("Invalid numeric character reference: &#;");
|
||||
}
|
||||
int cp = (e.charAt(1) == 'x' || e.charAt(1) == 'X')
|
||||
? parseHexEntity(e)
|
||||
: parseDecimalEntity(e);
|
||||
return new String(new int[] {cp}, 0, 1);
|
||||
}
|
||||
Character knownEntity = entity.get(e);
|
||||
@@ -178,6 +177,82 @@ public class XMLTokener extends JSONTokener {
|
||||
return knownEntity.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a hexadecimal numeric character reference (e.g., "઼").
|
||||
* @param e entity string starting with '#' (e.g., "#x1F4A9")
|
||||
* @return the Unicode code point
|
||||
* @throws JSONException if the format is invalid
|
||||
*/
|
||||
private static int parseHexEntity(String e) throws JSONException {
|
||||
// hex encoded unicode - need at least one hex digit after #x
|
||||
if (e.length() < 3) {
|
||||
throw new JSONException("Invalid hex character reference: missing hex digits in &#" + e.substring(1) + ";");
|
||||
}
|
||||
String hex = e.substring(2);
|
||||
if (!isValidHex(hex)) {
|
||||
throw new JSONException("Invalid hex character reference: &#" + e.substring(1) + ";");
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(hex, 16);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new JSONException("Invalid hex character reference: &#" + e.substring(1) + ";", nfe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a decimal numeric character reference (e.g., "{").
|
||||
* @param e entity string starting with '#' (e.g., "#123")
|
||||
* @return the Unicode code point
|
||||
* @throws JSONException if the format is invalid
|
||||
*/
|
||||
private static int parseDecimalEntity(String e) throws JSONException {
|
||||
String decimal = e.substring(1);
|
||||
if (!isValidDecimal(decimal)) {
|
||||
throw new JSONException("Invalid decimal character reference: &#" + decimal + ";");
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(decimal);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new JSONException("Invalid decimal character reference: &#" + decimal + ";", nfe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string contains only valid hexadecimal digits.
|
||||
* @param s the string to check
|
||||
* @return true if s is non-empty and contains only hex digits (0-9, a-f, A-F)
|
||||
*/
|
||||
private static boolean isValidHex(String s) {
|
||||
if (s == null || s.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string contains only valid decimal digits.
|
||||
* @param s the string to check
|
||||
* @return true if s is non-empty and contains only digits (0-9)
|
||||
*/
|
||||
private static boolean isValidDecimal(String s) {
|
||||
if (s == null || s.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c < '0' || c > '9') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <pre>{@code
|
||||
|
||||
@@ -986,4 +986,70 @@ public class JSONMLTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that malformed XML causing type mismatch throws JSONException.
|
||||
* Previously threw ClassCastException when parse() returned String instead of JSONArray.
|
||||
* Related to issue #1034
|
||||
*/
|
||||
@Test(expected = JSONException.class)
|
||||
public void testMalformedXMLThrowsJSONExceptionNotClassCast() {
|
||||
// This malformed XML causes parse() to return wrong type
|
||||
byte[] data = {0x3c, 0x0a, 0x2f, (byte)0xff, (byte)0xff, (byte)0xff,
|
||||
(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff,
|
||||
(byte)0xff, 0x3e, 0x42};
|
||||
String xmlStr = new String(data);
|
||||
JSONML.toJSONArray(xmlStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that type mismatch in toJSONObject throws JSONException.
|
||||
* Validates safe type casting in toJSONObject methods.
|
||||
*/
|
||||
@Test
|
||||
public void testToJSONObjectTypeMismatch() {
|
||||
// Create XML that would cause parse() to return wrong type
|
||||
String xmlStr = "<\n/\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff>B";
|
||||
try {
|
||||
JSONML.toJSONObject(xmlStr);
|
||||
fail("Expected JSONException for type mismatch");
|
||||
} catch (ClassCastException e) {
|
||||
fail("Should throw JSONException, not ClassCastException");
|
||||
} catch (JSONException e) {
|
||||
// Expected - verify it's about type mismatch
|
||||
assertTrue("Exception message should mention type error",
|
||||
e.getMessage().contains("Expected") || e.getMessage().contains("got"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that valid XML still works correctly after the fix.
|
||||
* Ensures the type checking doesn't break normal operation.
|
||||
*/
|
||||
@Test
|
||||
public void testValidXMLStillWorks() {
|
||||
String xmlStr = "<root><item>value</item></root>";
|
||||
try {
|
||||
JSONArray jsonArray = JSONML.toJSONArray(xmlStr);
|
||||
assertNotNull("JSONArray should not be null", jsonArray);
|
||||
assertEquals("root", jsonArray.getString(0));
|
||||
} catch (Exception e) {
|
||||
fail("Valid XML should not throw exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that valid XML to JSONObject still works correctly.
|
||||
*/
|
||||
@Test
|
||||
public void testValidXMLToJSONObjectStillWorks() {
|
||||
String xmlStr = "<root attr=\"value\"><item>content</item></root>";
|
||||
try {
|
||||
JSONObject jsonObject = JSONML.toJSONObject(xmlStr);
|
||||
assertNotNull("JSONObject should not be null", jsonObject);
|
||||
assertEquals("root", jsonObject.getString("tagName"));
|
||||
} catch (Exception e) {
|
||||
fail("Valid XML should not throw exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ public class JSONObjectLocaleTest {
|
||||
|
||||
MyLocaleBean myLocaleBean = new MyLocaleBean();
|
||||
|
||||
// save and restore the current default locale, to avoid any side effects on other executions in the same JVM
|
||||
Locale defaultLocale = Locale.getDefault();
|
||||
try {
|
||||
/**
|
||||
* This is just the control case which happens when the locale.ROOT
|
||||
* lowercasing behavior is the same as the current locale.
|
||||
@@ -56,5 +59,8 @@ public class JSONObjectLocaleTest {
|
||||
assertEquals("expected size 2, found: " +jsontr.length(), 2, jsontr.length());
|
||||
assertEquals("expected jsontr[i] == beanI", "beanI", jsontr.getString("i"));
|
||||
assertEquals("expected jsontr[id] == beanId", "beanId", jsontr.getString("id"));
|
||||
} finally {
|
||||
Locale.setDefault(defaultLocale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3117,12 +3117,13 @@ public class JSONObjectTest {
|
||||
|
||||
// test a more complex object
|
||||
writer = new StringWriter();
|
||||
try {
|
||||
new JSONObject()
|
||||
|
||||
JSONObject object = new JSONObject()
|
||||
.put("somethingElse", "a value")
|
||||
.put("someKey", new JSONArray()
|
||||
.put(new JSONObject().put("key1", new BrokenToString())))
|
||||
.write(writer).toString();
|
||||
.put(new JSONObject().put("key1", new BrokenToString())));
|
||||
try {
|
||||
object.write(writer).toString();
|
||||
fail("Expected an exception, got a String value");
|
||||
} catch (JSONException e) {
|
||||
assertEquals("Unable to write JSONObject value for key: someKey", e.getMessage());
|
||||
@@ -3136,14 +3137,15 @@ public class JSONObjectTest {
|
||||
|
||||
// test a more slightly complex object
|
||||
writer = new StringWriter();
|
||||
try {
|
||||
new JSONObject()
|
||||
|
||||
object = new JSONObject()
|
||||
.put("somethingElse", "a value")
|
||||
.put("someKey", new JSONArray()
|
||||
.put(new JSONObject().put("key1", new BrokenToString()))
|
||||
.put(12345)
|
||||
)
|
||||
.write(writer).toString();
|
||||
);
|
||||
try {
|
||||
object.write(writer).toString();
|
||||
fail("Expected an exception, got a String value");
|
||||
} catch (JSONException e) {
|
||||
assertEquals("Unable to write JSONObject value for key: someKey", e.getMessage());
|
||||
|
||||
@@ -1092,7 +1092,7 @@ public class XMLConfigurationTest {
|
||||
"<addresses></addresses>";
|
||||
|
||||
String expectedStr =
|
||||
"{\"addresses\":[]}";
|
||||
"{\"addresses\":[\"\"]}";
|
||||
|
||||
Set<String> forceList = new HashSet<String>();
|
||||
forceList.add("addresses");
|
||||
@@ -1130,7 +1130,7 @@ public class XMLConfigurationTest {
|
||||
"<addresses />";
|
||||
|
||||
String expectedStr =
|
||||
"{\"addresses\":[]}";
|
||||
"{\"addresses\":[\"\"]}";
|
||||
|
||||
Set<String> forceList = new HashSet<String>();
|
||||
forceList.add("addresses");
|
||||
@@ -1144,6 +1144,157 @@ public class XMLConfigurationTest {
|
||||
Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceListWithLastElementAsEmptyTag(){
|
||||
final String originalXml = "<root><id>1</id><id/></root>";
|
||||
final String expectedJsonString = "{\"root\":{\"id\":[1,\"\"]}}";
|
||||
|
||||
HashSet<String> forceListCandidates = new HashSet<>();
|
||||
forceListCandidates.add("id");
|
||||
final JSONObject json = XML.toJSONObject(originalXml,
|
||||
new XMLParserConfiguration()
|
||||
.withKeepStrings(false)
|
||||
.withcDataTagName("content")
|
||||
.withForceList(forceListCandidates)
|
||||
.withConvertNilAttributeToNull(true));
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceListWithFirstElementAsEmptyTag(){
|
||||
final String originalXml = "<root><id/><id>1</id></root>";
|
||||
final String expectedJsonString = "{\"root\":{\"id\":[\"\",1]}}";
|
||||
|
||||
HashSet<String> forceListCandidates = new HashSet<>();
|
||||
forceListCandidates.add("id");
|
||||
final JSONObject json = XML.toJSONObject(originalXml,
|
||||
new XMLParserConfiguration()
|
||||
.withKeepStrings(false)
|
||||
.withcDataTagName("content")
|
||||
.withForceList(forceListCandidates)
|
||||
.withConvertNilAttributeToNull(true));
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceListWithMiddleElementAsEmptyTag(){
|
||||
final String originalXml = "<root><id>1</id><id/><id>2</id></root>";
|
||||
final String expectedJsonString = "{\"root\":{\"id\":[1,\"\",2]}}";
|
||||
|
||||
HashSet<String> forceListCandidates = new HashSet<>();
|
||||
forceListCandidates.add("id");
|
||||
final JSONObject json = XML.toJSONObject(originalXml,
|
||||
new XMLParserConfiguration()
|
||||
.withKeepStrings(false)
|
||||
.withcDataTagName("content")
|
||||
.withForceList(forceListCandidates)
|
||||
.withConvertNilAttributeToNull(true));
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceListWithLastElementAsEmpty(){
|
||||
final String originalXml = "<root><id>1</id><id></id></root>";
|
||||
final String expectedJsonString = "{\"root\":{\"id\":[1,\"\"]}}";
|
||||
HashSet<String> forceListCandidates = new HashSet<>();
|
||||
forceListCandidates.add("id");
|
||||
final JSONObject json = XML.toJSONObject(originalXml,
|
||||
new XMLParserConfiguration()
|
||||
.withKeepStrings(false)
|
||||
.withForceList(forceListCandidates)
|
||||
.withConvertNilAttributeToNull(true));
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceListWithFirstElementAsEmpty(){
|
||||
final String originalXml = "<root><id></id><id>1</id></root>";
|
||||
final String expectedJsonString = "{\"root\":{\"id\":[\"\",1]}}";
|
||||
|
||||
HashSet<String> forceListCandidates = new HashSet<>();
|
||||
forceListCandidates.add("id");
|
||||
final JSONObject json = XML.toJSONObject(originalXml,
|
||||
new XMLParserConfiguration()
|
||||
.withKeepStrings(false)
|
||||
.withForceList(forceListCandidates)
|
||||
.withConvertNilAttributeToNull(true));
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceListWithMiddleElementAsEmpty(){
|
||||
final String originalXml = "<root><id>1</id><id></id><id>2</id></root>";
|
||||
final String expectedJsonString = "{\"root\":{\"id\":[1,\"\",2]}}";
|
||||
|
||||
HashSet<String> forceListCandidates = new HashSet<>();
|
||||
forceListCandidates.add("id");
|
||||
final JSONObject json = XML.toJSONObject(originalXml,
|
||||
new XMLParserConfiguration()
|
||||
.withKeepStrings(false)
|
||||
.withForceList(forceListCandidates)
|
||||
.withConvertNilAttributeToNull(true));
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceListEmptyAndEmptyTagsMixed(){
|
||||
final String originalXml = "<root><id></id><id/><id>1</id><id/><id></id><id>2</id></root>";
|
||||
final String expectedJsonString = "{\"root\":{\"id\":[\"\",\"\",1,\"\",\"\",2]}}";
|
||||
|
||||
HashSet<String> forceListCandidates = new HashSet<>();
|
||||
forceListCandidates.add("id");
|
||||
final JSONObject json = XML.toJSONObject(originalXml,
|
||||
new XMLParserConfiguration()
|
||||
.withKeepStrings(false)
|
||||
.withForceList(forceListCandidates)
|
||||
.withConvertNilAttributeToNull(true));
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceListConsistencyWithDefault() {
|
||||
final String originalXml = "<root><id>0</id><id>1</id><id/><id></id></root>";
|
||||
final String expectedJsonString = "{\"root\":{\"id\":[0,1,\"\",\"\"]}}";
|
||||
|
||||
// confirm expected result of default array-of-tags processing
|
||||
JSONObject json = XML.toJSONObject(originalXml);
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
|
||||
// confirm forceList array-of-tags processing is consistent with default processing
|
||||
HashSet<String> forceListCandidates = new HashSet<>();
|
||||
forceListCandidates.add("id");
|
||||
json = XML.toJSONObject(originalXml,
|
||||
new XMLParserConfiguration()
|
||||
.withForceList(forceListCandidates));
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceListInitializesAnArrayWithAnEmptyElement(){
|
||||
final String originalXml = "<root><id></id></root>";
|
||||
final String expectedJsonString = "{\"root\":{\"id\":[\"\"]}}";
|
||||
|
||||
HashSet<String> forceListCandidates = new HashSet<>();
|
||||
forceListCandidates.add("id");
|
||||
JSONObject json = XML.toJSONObject(originalXml,
|
||||
new XMLParserConfiguration()
|
||||
.withForceList(forceListCandidates));
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceListInitializesAnArrayWithAnEmptyTag(){
|
||||
final String originalXml = "<root><id/></root>";
|
||||
final String expectedJsonString = "{\"root\":{\"id\":[\"\"]}}";
|
||||
|
||||
HashSet<String> forceListCandidates = new HashSet<>();
|
||||
forceListCandidates.add("id");
|
||||
JSONObject json = XML.toJSONObject(originalXml,
|
||||
new XMLParserConfiguration()
|
||||
.withForceList(forceListCandidates));
|
||||
assertEquals(expectedJsonString, json.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxNestingDepthIsSet() {
|
||||
XMLParserConfiguration xmlParserConfiguration = XMLParserConfiguration.ORIGINAL;
|
||||
|
||||
@@ -1426,6 +1426,81 @@ public class XMLTest {
|
||||
assertEquals(jsonObject3.getJSONObject("color").getString("value"), "008E97");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that empty numeric character reference &#; throws JSONException.
|
||||
* Previously threw StringIndexOutOfBoundsException.
|
||||
* Related to issue #1035
|
||||
*/
|
||||
@Test(expected = JSONException.class)
|
||||
public void testEmptyNumericEntityThrowsJSONException() {
|
||||
String xmlStr = "<a>&#;</a>";
|
||||
XML.toJSONObject(xmlStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that malformed decimal entity &#txx; throws JSONException.
|
||||
* Previously threw NumberFormatException.
|
||||
* Related to issue #1036
|
||||
*/
|
||||
@Test(expected = JSONException.class)
|
||||
public void testInvalidDecimalEntityThrowsJSONException() {
|
||||
String xmlStr = "<a>&#txx;</a>";
|
||||
XML.toJSONObject(xmlStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that empty hex entity &#x; throws JSONException.
|
||||
* Validates proper input validation for hex entities.
|
||||
*/
|
||||
@Test(expected = JSONException.class)
|
||||
public void testEmptyHexEntityThrowsJSONException() {
|
||||
String xmlStr = "<a>&#x;</a>";
|
||||
XML.toJSONObject(xmlStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that invalid hex entity &#xGGG; throws JSONException.
|
||||
* Validates hex digit validation.
|
||||
*/
|
||||
@Test(expected = JSONException.class)
|
||||
public void testInvalidHexEntityThrowsJSONException() {
|
||||
String xmlStr = "<a>&#xGGG;</a>";
|
||||
XML.toJSONObject(xmlStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that valid decimal numeric entity A works correctly.
|
||||
* Should decode to character 'A'.
|
||||
*/
|
||||
@Test
|
||||
public void testValidDecimalEntity() {
|
||||
String xmlStr = "<a>A</a>";
|
||||
JSONObject jsonObject = XML.toJSONObject(xmlStr);
|
||||
assertEquals("A", jsonObject.getString("a"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that valid hex numeric entity A works correctly.
|
||||
* Should decode to character 'A'.
|
||||
*/
|
||||
@Test
|
||||
public void testValidHexEntity() {
|
||||
String xmlStr = "<a>A</a>";
|
||||
JSONObject jsonObject = XML.toJSONObject(xmlStr);
|
||||
assertEquals("A", jsonObject.getString("a"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that valid uppercase hex entity A works correctly.
|
||||
* Should decode to character 'A'.
|
||||
*/
|
||||
@Test
|
||||
public void testValidUppercaseHexEntity() {
|
||||
String xmlStr = "<a>A</a>";
|
||||
JSONObject jsonObject = XML.toJSONObject(xmlStr);
|
||||
assertEquals("A", jsonObject.getString("a"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user