mirror of
https://github.com/stleary/JSON-java.git
synced 2026-05-18 00:00:50 -04:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d749ee16ab | |||
| 649598338f | |||
| 896ce0fb74 | |||
| 1877069780 | |||
| b959027aa2 | |||
| 039f331d7d | |||
| 94e340002b | |||
| 6230128f59 | |||
| ff264ef647 | |||
| a37aa69480 | |||
| 510a03ac36 | |||
| 538afc3d78 | |||
| d092d0903c | |||
| 7a8da886e7 | |||
| 0737e04f8a | |||
| 592e7828d9 | |||
| 6c1bfbc7a5 | |||
| 534ce3c4d1 | |||
| 9d14246bee | |||
| 995fb840f7 | |||
| e635f40238 | |||
| d5e744ca90 | |||
| e0c4086168 | |||
| 96353de304 | |||
| 8cbb4d5bb3 | |||
| 421abfdc1f |
@@ -16,3 +16,6 @@ build
|
|||||||
/gradlew
|
/gradlew
|
||||||
/gradlew.bat
|
/gradlew.bat
|
||||||
.gitmodules
|
.gitmodules
|
||||||
|
|
||||||
|
# ignore compiled class files
|
||||||
|
*.class
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ JSON in Java [package org.json]
|
|||||||
[](https://mvnrepository.com/artifact/org.json/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/pipeline.yml)
|
||||||
[](https://github.com/stleary/JSON-java/actions/workflows/codeql-analysis.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)**
|
**[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 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:
|
Project goals include:
|
||||||
* Reliable and consistent results
|
* Reliable and consistent results
|
||||||
* Adherence to the JSON specification
|
* Adherence to the JSON specification
|
||||||
@@ -28,8 +31,16 @@ Project goals include:
|
|||||||
* Maintain backward compatibility
|
* Maintain backward compatibility
|
||||||
* Designed and tested to use on Java versions 1.6 - 25
|
* 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
|
# If you would like to contribute to this project
|
||||||
|
|
||||||
|
|||||||
+55
@@ -3,3 +3,58 @@
|
|||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
Please follow the instructions in the ["How are vulnerabilities and exploits handled?"](https://github.com/stleary/JSON-java/wiki/FAQ#how-are-vulnerabilities-and-exploits-handled) section in the FAQ.
|
Please follow the instructions in the ["How are vulnerabilities and exploits handled?"](https://github.com/stleary/JSON-java/wiki/FAQ#how-are-vulnerabilities-and-exploits-handled) section in the FAQ.
|
||||||
|
|
||||||
|
## Verifying Release Signatures
|
||||||
|
|
||||||
|
All releases of `org.json:json` published to Maven Central are signed with PGP. The fingerprint, keyserver location, and verification procedure below let you confirm that the artifacts you've downloaded were produced by this project and have not been modified in transit.
|
||||||
|
|
||||||
|
### Signing Key
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| --- | --- |
|
||||||
|
| **Fingerprint** | `FB35 C8D0 2B47 24DA DA23 DE0A FD11 6C19 69FC CFF3` |
|
||||||
|
| **Long key ID** | `FD116C1969FCCFF3` |
|
||||||
|
| **Keyserver** | `hkps://keyserver.ubuntu.com` |
|
||||||
|
|
||||||
|
The full 40-character fingerprint above is the canonical identifier for the key. Always pin or compare against the full fingerprint rather than the long or short key ID.
|
||||||
|
|
||||||
|
### Importing the Key
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gpg --keyserver hkps://keyserver.ubuntu.com \
|
||||||
|
--recv-keys FB35C8D02B4724DADA23DE0AFD116C1969FCCFF3
|
||||||
|
```
|
||||||
|
|
||||||
|
After importing, confirm the fingerprint matches what's published here:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gpg --fingerprint FB35C8D02B4724DADA23DE0AFD116C1969FCCFF3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verifying an Artifact
|
||||||
|
|
||||||
|
Download both the artifact and its detached signature from Maven Central. For example, for version `20251224`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -O https://repo1.maven.org/maven2/org/json/json/20251224/json-20251224.jar
|
||||||
|
curl -O https://repo1.maven.org/maven2/org/json/json/20251224/json-20251224.jar.asc
|
||||||
|
gpg --verify json-20251224.jar.asc json-20251224.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
A successful verification will report `Good signature from ...` and display the same fingerprint shown above. If GPG reports `BAD signature`, a mismatched fingerprint, or `No public key`, do not use the artifact and please open an issue.
|
||||||
|
|
||||||
|
The same procedure applies to the `.pom` and any other signed sidecars in the release directory; substitute the filename you want to verify.
|
||||||
|
|
||||||
|
### Gradle Dependency Verification
|
||||||
|
|
||||||
|
If you are using Gradle's [dependency verification](https://docs.gradle.org/current/userguide/dependency_verification.html) feature, add an entry like the following to `gradle/verification-metadata.xml`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<trusted-key id="FB35C8D02B4724DADA23DE0AFD116C1969FCCFF3" group="org.json" name="json"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
Gradle also accepts the long key ID (`FD116C1969FCCFF3`), but pinning the full fingerprint is recommended.
|
||||||
|
|
||||||
|
### Key Rotation
|
||||||
|
|
||||||
|
If the signing key is ever rotated or revoked, this document will be updated in the `master` branch with the new fingerprint, and the change will be visible in the file's commit history. Always check this file directly in the repository for the current authoritative value before trusting any third-party copy of the fingerprint.
|
||||||
@@ -22,6 +22,33 @@ public class JSONML {
|
|||||||
public 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.
|
* 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
|
* @throws JSONException Thrown on error converting to a JSONArray
|
||||||
*/
|
*/
|
||||||
public static JSONArray toJSONArray(String string) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONArray
|
||||||
*/
|
*/
|
||||||
public static JSONArray toJSONArray(String string, boolean keepStrings) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONArray
|
||||||
*/
|
*/
|
||||||
public static JSONArray toJSONArray(String string, JSONMLParserConfiguration config) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONArray
|
||||||
*/
|
*/
|
||||||
public static JSONArray toJSONArray(XMLTokener x, JSONMLParserConfiguration config) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONArray
|
||||||
*/
|
*/
|
||||||
public static JSONArray toJSONArray(XMLTokener x, boolean keepStrings) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONArray
|
||||||
*/
|
*/
|
||||||
public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONObject
|
||||||
*/
|
*/
|
||||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONObject
|
||||||
*/
|
*/
|
||||||
public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONObject
|
||||||
*/
|
*/
|
||||||
public static JSONObject toJSONObject(String string, JSONMLParserConfiguration config) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONObject
|
||||||
*/
|
*/
|
||||||
public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONObject
|
||||||
*/
|
*/
|
||||||
public static JSONObject toJSONObject(XMLTokener x, boolean keepStrings) throws JSONException {
|
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
|
* @throws JSONException Thrown on error converting to a JSONObject
|
||||||
*/
|
*/
|
||||||
public static JSONObject toJSONObject(XMLTokener x, JSONMLParserConfiguration config) throws JSONException {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3349,7 +3349,7 @@ public class JSONObject {
|
|||||||
* of the given class. It supports basic data types including {@code int}, {@code double},
|
* of the given class. It supports basic data types including {@code int}, {@code double},
|
||||||
* {@code float}, {@code long}, and {@code boolean}, as well as their boxed counterparts.
|
* {@code float}, {@code long}, and {@code boolean}, as well as their boxed counterparts.
|
||||||
* The target class must have a no-argument constructor, and its field names must match
|
* The target class must have a no-argument constructor, and its field names must match
|
||||||
* the keys in the JSON string.
|
* the keys in the JSON string. Static fields are ignored.
|
||||||
*
|
*
|
||||||
* <p><strong>Note:</strong> Only classes that are explicitly supported and registered within
|
* <p><strong>Note:</strong> Only classes that are explicitly supported and registered within
|
||||||
* the {@code JSONObject} context can be deserialized. If the provided class is not among those,
|
* the {@code JSONObject} context can be deserialized. If the provided class is not among those,
|
||||||
@@ -3366,6 +3366,9 @@ public class JSONObject {
|
|||||||
try {
|
try {
|
||||||
T obj = clazz.getDeclaredConstructor().newInstance();
|
T obj = clazz.getDeclaredConstructor().newInstance();
|
||||||
for (Field field : clazz.getDeclaredFields()) {
|
for (Field field : clazz.getDeclaredFields()) {
|
||||||
|
if (Modifier.isStatic(field.getModifiers())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
String fieldName = field.getName();
|
String fieldName = field.getName();
|
||||||
if (has(fieldName)) {
|
if (has(fieldName)) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.io.StringReader;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provides static methods to convert an XML text into a JSONObject, and to
|
* 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() {
|
public Iterator<Integer> iterator() {
|
||||||
return new Iterator<Integer>() {
|
return new Iterator<Integer>() {
|
||||||
private int nextIndex = 0;
|
private int nextIndex = 0;
|
||||||
private int length = string.length();
|
private final int length = string.length();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
@@ -89,6 +90,9 @@ public class XML {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer next() {
|
public Integer next() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
int result = string.codePointAt(this.nextIndex);
|
int result = string.codePointAt(this.nextIndex);
|
||||||
this.nextIndex += Character.charCount(result);
|
this.nextIndex += Character.charCount(result);
|
||||||
return result;
|
return result;
|
||||||
@@ -154,7 +158,7 @@ public class XML {
|
|||||||
* @param cp code point to test
|
* @param cp code point to test
|
||||||
* @return true if the code point is not valid for an XML
|
* @return true if the code point is not valid for an XML
|
||||||
*/
|
*/
|
||||||
private static boolean mustEscape(int cp) {
|
static boolean mustEscape(int cp) {
|
||||||
/* Valid range from https://www.w3.org/TR/REC-xml/#charsets
|
/* Valid range from https://www.w3.org/TR/REC-xml/#charsets
|
||||||
*
|
*
|
||||||
* #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
|
* #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
|
||||||
@@ -387,8 +391,13 @@ public class XML {
|
|||||||
context.append(tagName, JSONObject.NULL);
|
context.append(tagName, JSONObject.NULL);
|
||||||
} else if (jsonObject.length() > 0) {
|
} else if (jsonObject.length() > 0) {
|
||||||
context.append(tagName, jsonObject);
|
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());
|
context.put(tagName, new JSONArray());
|
||||||
|
if (jsonObject.isEmpty()){
|
||||||
|
context.append(tagName, "");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.append(tagName, "");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (nilAttributeFound) {
|
if (nilAttributeFound) {
|
||||||
@@ -447,7 +456,11 @@ public class XML {
|
|||||||
if (config.getForceList().contains(tagName)) {
|
if (config.getForceList().contains(tagName)) {
|
||||||
// Force the value to be an array
|
// Force the value to be an array
|
||||||
if (jsonObject.length() == 0) {
|
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.put(tagName, new JSONArray());
|
||||||
|
}
|
||||||
|
context.append(tagName, "");
|
||||||
} else if (jsonObject.length() == 1
|
} else if (jsonObject.length() == 1
|
||||||
&& jsonObject.opt(config.getcDataTagName()) != null) {
|
&& jsonObject.opt(config.getcDataTagName()) != null) {
|
||||||
context.append(tagName, jsonObject.opt(config.getcDataTagName()));
|
context.append(tagName, jsonObject.opt(config.getcDataTagName()));
|
||||||
|
|||||||
@@ -151,22 +151,24 @@ public class XMLTokener extends JSONTokener {
|
|||||||
/**
|
/**
|
||||||
* Unescape an XML entity encoding;
|
* Unescape an XML entity encoding;
|
||||||
* @param e entity (only the actual entity value, not the preceding & or ending ;
|
* @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
|
// validate
|
||||||
if (e == null || e.isEmpty()) {
|
if (e == null || e.isEmpty()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
// if our entity is an encoded unicode point, parse it.
|
// if our entity is an encoded unicode point, parse it.
|
||||||
if (e.charAt(0) == '#') {
|
if (e.charAt(0) == '#') {
|
||||||
int cp;
|
if (e.length() < 2) {
|
||||||
if (e.charAt(1) == 'x' || e.charAt(1) == 'X') {
|
throw new JSONException("Invalid numeric character reference: &#;");
|
||||||
// hex encoded unicode
|
}
|
||||||
cp = Integer.parseInt(e.substring(2), 16);
|
int cp = (e.charAt(1) == 'x' || e.charAt(1) == 'X')
|
||||||
} else {
|
? parseHexEntity(e)
|
||||||
// decimal encoded unicode
|
: parseDecimalEntity(e);
|
||||||
cp = Integer.parseInt(e.substring(1));
|
if (XML.mustEscape(cp)) {
|
||||||
|
throw new JSONException("Invalid numeric character reference: &#" + e.substring(1) + ";");
|
||||||
}
|
}
|
||||||
return new String(new int[] {cp}, 0, 1);
|
return new String(new int[] {cp}, 0, 1);
|
||||||
}
|
}
|
||||||
@@ -178,6 +180,82 @@ public class XMLTokener extends JSONTokener {
|
|||||||
return knownEntity.toString();
|
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
|
* <pre>{@code
|
||||||
|
|||||||
@@ -1502,19 +1502,23 @@ public class JSONArrayTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRecursiveDepthArrayFor1000Levels() {
|
/**
|
||||||
|
* This test was originally for 1000 levels, which passes in test builds, but fails on my laptop.
|
||||||
|
* The current value of 900 seems to work.
|
||||||
|
*/
|
||||||
|
public void testRecursiveDepthArrayFor900Levels() {
|
||||||
try {
|
try {
|
||||||
ArrayList<Object> array = buildNestedArray(1000);
|
ArrayList<Object> array = buildNestedArray(900);
|
||||||
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withMaxNestingDepth(1000);
|
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withMaxNestingDepth(900);
|
||||||
new JSONArray(array, parserConfiguration);
|
new JSONArray(array, parserConfiguration);
|
||||||
} catch (StackOverflowError e) {
|
} catch (StackOverflowError e) {
|
||||||
String javaVersion = System.getProperty("java.version");
|
String javaVersion = System.getProperty("java.version");
|
||||||
if (javaVersion.startsWith("11.")) {
|
if (javaVersion.startsWith("11.")) {
|
||||||
System.out.println(
|
System.out.println(
|
||||||
"testRecursiveDepthArrayFor1000Levels() allowing intermittent stackoverflow, Java Version: "
|
"testRecursiveDepthArrayFor900Levels() allowing intermittent stackoverflow, Java Version: "
|
||||||
+ javaVersion);
|
+ javaVersion);
|
||||||
} else {
|
} else {
|
||||||
String errorStr = "testRecursiveDepthArrayFor1000Levels() unexpected stackoverflow, Java Version: "
|
String errorStr = "testRecursiveDepthArrayFor900Levels() unexpected stackoverflow, Java Version: "
|
||||||
+ javaVersion;
|
+ javaVersion;
|
||||||
System.out.println(errorStr);
|
System.out.println(errorStr);
|
||||||
throw new RuntimeException(errorStr);
|
throw new RuntimeException(errorStr);
|
||||||
|
|||||||
@@ -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();
|
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
|
* This is just the control case which happens when the locale.ROOT
|
||||||
* lowercasing behavior is the same as the current locale.
|
* 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 size 2, found: " +jsontr.length(), 2, jsontr.length());
|
||||||
assertEquals("expected jsontr[i] == beanI", "beanI", jsontr.getString("i"));
|
assertEquals("expected jsontr[i] == beanI", "beanI", jsontr.getString("i"));
|
||||||
assertEquals("expected jsontr[id] == beanId", "beanId", jsontr.getString("id"));
|
assertEquals("expected jsontr[id] == beanId", "beanId", jsontr.getString("id"));
|
||||||
|
} finally {
|
||||||
|
Locale.setDefault(defaultLocale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ import org.json.junit.data.CustomClassF;
|
|||||||
import org.json.junit.data.CustomClassG;
|
import org.json.junit.data.CustomClassG;
|
||||||
import org.json.junit.data.CustomClassH;
|
import org.json.junit.data.CustomClassH;
|
||||||
import org.json.junit.data.CustomClassI;
|
import org.json.junit.data.CustomClassI;
|
||||||
|
import org.json.junit.data.CustomClassJ;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
@@ -3117,12 +3118,13 @@ public class JSONObjectTest {
|
|||||||
|
|
||||||
// test a more complex object
|
// test a more complex object
|
||||||
writer = new StringWriter();
|
writer = new StringWriter();
|
||||||
try {
|
|
||||||
new JSONObject()
|
JSONObject object = new JSONObject()
|
||||||
.put("somethingElse", "a value")
|
.put("somethingElse", "a value")
|
||||||
.put("someKey", new JSONArray()
|
.put("someKey", new JSONArray()
|
||||||
.put(new JSONObject().put("key1", new BrokenToString())))
|
.put(new JSONObject().put("key1", new BrokenToString())));
|
||||||
.write(writer).toString();
|
try {
|
||||||
|
object.write(writer).toString();
|
||||||
fail("Expected an exception, got a String value");
|
fail("Expected an exception, got a String value");
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
assertEquals("Unable to write JSONObject value for key: someKey", e.getMessage());
|
assertEquals("Unable to write JSONObject value for key: someKey", e.getMessage());
|
||||||
@@ -3136,14 +3138,15 @@ public class JSONObjectTest {
|
|||||||
|
|
||||||
// test a more slightly complex object
|
// test a more slightly complex object
|
||||||
writer = new StringWriter();
|
writer = new StringWriter();
|
||||||
try {
|
|
||||||
new JSONObject()
|
object = new JSONObject()
|
||||||
.put("somethingElse", "a value")
|
.put("somethingElse", "a value")
|
||||||
.put("someKey", new JSONArray()
|
.put("someKey", new JSONArray()
|
||||||
.put(new JSONObject().put("key1", new BrokenToString()))
|
.put(new JSONObject().put("key1", new BrokenToString()))
|
||||||
.put(12345)
|
.put(12345)
|
||||||
)
|
);
|
||||||
.write(writer).toString();
|
try {
|
||||||
|
object.write(writer).toString();
|
||||||
fail("Expected an exception, got a String value");
|
fail("Expected an exception, got a String value");
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
assertEquals("Unable to write JSONObject value for key: someKey", e.getMessage());
|
assertEquals("Unable to write JSONObject value for key: someKey", e.getMessage());
|
||||||
@@ -4230,4 +4233,21 @@ public class JSONObjectTest {
|
|||||||
CustomClassI compareClassI = new CustomClassI(dataList);
|
CustomClassI compareClassI = new CustomClassI(dataList);
|
||||||
assertEquals(customClassI.integerMap.toString(), compareClassI.integerMap.toString());
|
assertEquals(customClassI.integerMap.toString(), compareClassI.integerMap.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void jsonObjectParseFromJson_9() {
|
||||||
|
JSONObject object = new JSONObject();
|
||||||
|
object.put("number", 12);
|
||||||
|
object.put("classState", "mutated");
|
||||||
|
|
||||||
|
String initialClassState = CustomClassJ.classState;
|
||||||
|
CustomClassJ.classState = "original";
|
||||||
|
try {
|
||||||
|
CustomClassJ customClassJ = object.fromJson(CustomClassJ.class);
|
||||||
|
assertEquals(12, customClassJ.number);
|
||||||
|
assertEquals("original", CustomClassJ.classState);
|
||||||
|
} finally {
|
||||||
|
CustomClassJ.classState = initialClassState;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1092,7 +1092,7 @@ public class XMLConfigurationTest {
|
|||||||
"<addresses></addresses>";
|
"<addresses></addresses>";
|
||||||
|
|
||||||
String expectedStr =
|
String expectedStr =
|
||||||
"{\"addresses\":[]}";
|
"{\"addresses\":[\"\"]}";
|
||||||
|
|
||||||
Set<String> forceList = new HashSet<String>();
|
Set<String> forceList = new HashSet<String>();
|
||||||
forceList.add("addresses");
|
forceList.add("addresses");
|
||||||
@@ -1130,7 +1130,7 @@ public class XMLConfigurationTest {
|
|||||||
"<addresses />";
|
"<addresses />";
|
||||||
|
|
||||||
String expectedStr =
|
String expectedStr =
|
||||||
"{\"addresses\":[]}";
|
"{\"addresses\":[\"\"]}";
|
||||||
|
|
||||||
Set<String> forceList = new HashSet<String>();
|
Set<String> forceList = new HashSet<String>();
|
||||||
forceList.add("addresses");
|
forceList.add("addresses");
|
||||||
@@ -1144,6 +1144,157 @@ public class XMLConfigurationTest {
|
|||||||
Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject);
|
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
|
@Test
|
||||||
public void testMaxNestingDepthIsSet() {
|
public void testMaxNestingDepthIsSet() {
|
||||||
XMLParserConfiguration xmlParserConfiguration = XMLParserConfiguration.ORIGINAL;
|
XMLParserConfiguration xmlParserConfiguration = XMLParserConfiguration.ORIGINAL;
|
||||||
|
|||||||
@@ -1426,6 +1426,117 @@ public class XMLTest {
|
|||||||
assertEquals(jsonObject3.getJSONObject("color").getString("value"), "008E97");
|
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 out-of-range hex entities throw JSONException rather than an uncaught runtime exception.
|
||||||
|
*/
|
||||||
|
@Test(expected = JSONException.class)
|
||||||
|
public void testOutOfRangeHexEntityThrowsJSONException() {
|
||||||
|
String xmlStr = "<a>�</a>";
|
||||||
|
XML.toJSONObject(xmlStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that out-of-range decimal entities throw JSONException rather than an uncaught runtime exception.
|
||||||
|
*/
|
||||||
|
@Test(expected = JSONException.class)
|
||||||
|
public void testOutOfRangeDecimalEntityThrowsJSONException() {
|
||||||
|
String xmlStr = "<a>�</a>";
|
||||||
|
XML.toJSONObject(xmlStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that surrogate code point entities throw JSONException.
|
||||||
|
*/
|
||||||
|
@Test(expected = JSONException.class)
|
||||||
|
public void testSurrogateHexEntityThrowsJSONException() {
|
||||||
|
String xmlStr = "<a>�</a>";
|
||||||
|
XML.toJSONObject(xmlStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that out-of-range numeric entities in attribute values throw JSONException.
|
||||||
|
*/
|
||||||
|
@Test(expected = JSONException.class)
|
||||||
|
public void testOutOfRangeHexEntityInAttributeThrowsJSONException() {
|
||||||
|
String xmlStr = "<a b=\"�\"/>";
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.json.junit.data;
|
||||||
|
|
||||||
|
public class CustomClassJ {
|
||||||
|
public static String classState = "original";
|
||||||
|
public int number;
|
||||||
|
|
||||||
|
public CustomClassJ() {
|
||||||
|
// Required for JSONObject#fromJson(Class<T>) tests.
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user