Compare commits

...

40 Commits

Author SHA1 Message Date
Sean Leary
c048b36516 Update pom.xml for 20230618 2023-06-18 12:18:36 -05:00
Sean Leary
f6e5bfa2db Update RELEASES.md for 20230618 2023-06-18 12:17:56 -05:00
Sean Leary
084b24cbe7 Update README.md for 20230618 2023-06-18 12:16:14 -05:00
Sean Leary
22ccf1b8e5 Merge pull request #740 from michael-o/proper-encoding
JSONTokener(InputStream) violates rfc8259#section-8.1 (#739)
2023-05-28 14:11:41 -05:00
Michael Osipov
133c0cc75f JSONTokener(InputStream) violates rfc8259#section-8.1 (#739)
Always use UTF-8 when an InputStream is passed.

This fixes #739.
2023-05-24 11:45:25 +02:00
Sean Leary
0578285036 Merge pull request #734 from superMaaax/fix_flaky
Fixed Flaky Tests Caused by JSON permutations
2023-03-27 16:25:57 -05:00
superMaaax
48fb5261fe Fixed Flaky Tests Caused by JSON permutations
###Description
Flaky Tests found using NonDex by running the commands -
mvn -pl . edu.illinois:nondex-maven-plugin:2.1.1:nondex -Dtest=org.json.junit.XMLTest#testIndentComplicatedJsonObject

mvn -pl . edu.illinois:nondex-maven-plugin:2.1.1:nondex -Dtest=org.json.junit.XMLTest#testIndentSimpleJsonArray

mvn -pl . edu.illinois:nondex-maven-plugin:2.1.1:nondex -Dtest=org.json.junit.XMLTest#testIndentSimpleJsonObject

The logged failure was-

[ERROR] Failures:
[ERROR] XMLTest.testIndentSimpleJsonObject:1193 expected:<...>
<employee>
<[married>true</married>
<name>sonoo</name>
<salary>56000</salary]>
</employee>
</Te...> but was:<...>
<employee>
<[name>sonoo</name>
<salary>56000</salary>
<married>true</married]>
</employee>
</Te...>

The issue is the same for all three tests, so here I only show the failure message for the third test (to reduce the length of the error message).

### Investigation

The tests fail with a comparison error while comparing an expected JSON String and the result from the value returned from XML.toString(). The toString function of XML makes no guarantees as to the iteration order of the attributes in the object. This makes the test outcome non-deterministic, and the test fails whenever the function returns a mismatch in order of the elements in the JSON String. To fix this, the expected and actual keys should be checked in a more deterministic way so that the assertions do not fail.

### Fix

Expected and Actual values can be converted into JSONObject and the similar function can be used to compare these objects. As this function compares the values inside the JSONObjects without needing order, the test becomes deterministic and ensures that the flakiness from the test is removed.

The PR does not introduce a breaking change.
2023-03-21 20:58:32 -05:00
Sean Leary
8353b9c3f0 Merge pull request #731 from JoaoGFarias/patch-1
Removing commented out code in JSONObject optDouble()
2023-03-11 19:28:01 -06:00
Sean Leary
fe22b242b5 Merge pull request #733 from haribabu-dev/jsontokener_closable
JSONTokener implemented java.io.Closeable
2023-03-11 19:26:55 -06:00
HariBabu t
7eca507d13 Removed overriding closable interface. 2023-03-07 13:58:30 +08:00
HariBabu t
e1eabc9c27 JSONTokener implemented java.io.Closeable 2023-03-04 23:08:32 +08:00
JoĂŁo Farias
0d436d92e2 Removing commented out code 2023-03-02 16:39:11 +01:00
Sean Leary
45bcba518f Merge pull request #729 from TamasPergerDWP/f-parserconfig
Refactor ParserConfiguration class hierarchy
2023-02-28 12:49:42 -06:00
Sean Leary
47fb49b6a8 Update for release 20230227 2023-02-27 07:21:11 -06:00
Sean Leary
0df034c9fd Update for release 20230227 2023-02-27 07:20:10 -06:00
Sean Leary
f0a05e6911 Update README.md 2023-02-27 07:17:51 -06:00
Tamas Perger
24093491a8 refactor: introduce ParserConfiguration class hierarchy 2023-02-21 19:13:07 +00:00
Sean Leary
1275f6809d Merge pull request #723 from TamasPergerDWP/master
JSONML should be protected from stack overflow exceptions caused by recursion, resolving #722
2023-02-17 13:47:36 -06:00
Tamas Perger
9234eab00a refactor: make JSONMLParserConfiguration all-args constructor private, enforcing the builder pattern. 2023-02-13 01:09:29 +00:00
Tamas Perger
72f4c3e646 refactor: rename XMLtoJSONMLParserConfiguration to JSONMLParserConfiguration 2023-02-12 01:32:34 +00:00
Tamas Perger
df2d6f8363 fix: introduce optional XMLtoJSONMLParserConfiguration parameter for JSONML.toJSONArray(...) functions, to facilitate max nesting depth override. 2023-02-11 01:52:13 +00:00
Tamas Perger
a6e412bded fix: limit the nesting depth in JSONML
Limit the XML nesting depth for CVE-2022-45688 when using the JsonML transform.
2023-02-10 01:46:44 +00:00
Tamas Perger
2391d248cc fix: amend XMLParserConfiguration.clone() to include the new maxNestingDepth param.
Amend Javadoc for XML and XMLParserConfiguration classes.
2023-02-10 01:45:34 +00:00
Sean Leary
401495ae86 Merge pull request #720 from cleydyr/issue-708
Limit the XML nesting depth for CVE-2022-45688
2023-02-05 19:30:04 -06:00
Cleydyr de Albuquerque
448e204186 docs: remove wrong description of parse method 2023-02-02 20:16:16 +01:00
Cleydyr de Albuquerque
eb56704e68 fix: set default maximum nesting depth as 512 2023-02-02 18:15:03 +01:00
Cleydyr de Albuquerque
651511f500 tests: add new test to verify that an XML having the permitted nesting depth can be converted 2023-02-01 20:22:47 +01:00
Cleydyr de Albuquerque
a14cb12c85 refactor: keep consistence with other tests and tidy up constant 2023-02-01 20:22:32 +01:00
Cleydyr de Albuquerque
f566a1d9ee fix: limit the nesting depth 2023-02-01 16:26:58 +01:00
Sean Leary
5920eca2d7 Merge pull request #711 from 6d64/revert-pull-707-interviewbit-spam
Revert pull 707 - interviewbit spam
2022-11-30 20:20:39 -06:00
6d64
3b097d051a Revert pull 707 - interviewbit spam
Reverted commit that was added by a bot adding interviewbit spam to
repos on github
2022-12-01 03:21:26 +11:00
Sean Leary
4e630e58a4 Merge pull request #707 from ASAlishaa/patch-1
Added new resource to the repos
2022-11-17 19:14:11 -06:00
ASAlisha
b732188e4e Added new resource to this repos.
Added resource in the correct format.
2022-11-15 16:31:05 +05:30
ASAlisha
5369442671 Added new resource to the repos
Added new useful JSON resource.
2022-11-14 03:26:18 +05:30
Sean Leary
bb1138762a Merge pull request #703 from TomerPacific/feature/update-release-for-JSONMap-Change
Update Releases.md for JSONObject(Map): Throws NPE if key is null
2022-11-05 17:39:13 -05:00
Sean Leary
6a732ec99d Merge pull request #704 from niranjanib/fix-javadoc-not-visible-in-website
move javadoc comments above the interface definition to make it visible
2022-11-05 17:38:15 -05:00
Niranjani
c798c76ddd move javadoc comments above the interface definition to make it visible
Fix #670
2022-10-30 22:10:38 +05:30
unknown
23d5e52a53 feature/update-release-for-JSONMap-Change adding breaking change for JSONMap to corresponding release 2022-10-28 08:45:54 +03:00
Sean Leary
98df35449a Merge pull request #696 from bmk15897/fix-flaky-test
Update JSONPointerTest for NonDex compatibility
2022-10-15 08:24:01 -05:00
Bharati Kulkarni
a2d3d3c9b5 Fix Flaky Test 2022-10-11 14:33:43 -05:00
18 changed files with 713 additions and 139 deletions

View File

@@ -8,7 +8,7 @@ JSON in Java [package org.json]
[![Maven Central](https://img.shields.io/maven-central/v/org.json/json.svg)](https://mvnrepository.com/artifact/org.json/json) [![Maven Central](https://img.shields.io/maven-central/v/org.json/json.svg)](https://mvnrepository.com/artifact/org.json/json)
**[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20220924/json-20220924.jar)** **[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20230618/json-20230618.jar)**
# Overview # Overview

View File

@@ -5,6 +5,10 @@ and artifactId "json". For example:
[https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav](https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav) [https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav](https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav)
~~~ ~~~
20230618 Final release with Java 1.6 compatibility. Future releases will require Java 1.8 or greater.
20230227 Fix for CVE-2022-45688 and recent commits
20220924 New License - public domain, and some minor updates 20220924 New License - public domain, and some minor updates
20220320 Wrap StackOverflow with JSONException 20220320 Wrap StackOverflow with JSONException
@@ -20,6 +24,7 @@ and artifactId "json". For example:
20190722 Recent commits 20190722 Recent commits
20180813 POM change to include Automatic-Module-Name (#431) 20180813 POM change to include Automatic-Module-Name (#431)
JSONObject(Map) now throws an exception if any of a map keys are null (#405)
20180130 Recent commits 20180130 Recent commits

View File

@@ -3,7 +3,7 @@
<groupId>org.json</groupId> <groupId>org.json</groupId>
<artifactId>json</artifactId> <artifactId>json</artifactId>
<version>20220924</version> <version>20230618</version>
<packaging>bundle</packaging> <packaging>bundle</packaging>
<name>JSON in Java</name> <name>JSON in Java</name>

View File

@@ -27,7 +27,32 @@ public class JSONML {
XMLTokener x, XMLTokener x,
boolean arrayForm, boolean arrayForm,
JSONArray ja, JSONArray ja,
boolean keepStrings boolean keepStrings,
int currentNestingDepth
) throws JSONException {
return parse(x,arrayForm, ja,
keepStrings ? JSONMLParserConfiguration.KEEP_STRINGS : JSONMLParserConfiguration.ORIGINAL,
currentNestingDepth);
}
/**
* Parse XML values and store them in a JSONArray.
* @param x The XMLTokener containing the source string.
* @param arrayForm true if array form, false if object form.
* @param ja The JSONArray that is containing the current tag or null
* if we are at the outermost level.
* @param config The parser configuration:
* JSONMLParserConfiguration.ORIGINAL is the default behaviour;
* JSONMLParserConfiguration.KEEP_STRINGS means Don't type-convert text nodes and attribute values.
* @return A JSONArray if the value is the outermost tag, otherwise null.
* @throws JSONException if a parsing error occurs
*/
private static Object parse(
XMLTokener x,
boolean arrayForm,
JSONArray ja,
JSONMLParserConfiguration config,
int currentNestingDepth
) throws JSONException { ) throws JSONException {
String attribute; String attribute;
char c; char c;
@@ -152,7 +177,7 @@ public class JSONML {
if (!(token instanceof String)) { if (!(token instanceof String)) {
throw x.syntaxError("Missing value"); throw x.syntaxError("Missing value");
} }
newjo.accumulate(attribute, keepStrings ? ((String)token) :XML.stringToValue((String)token)); newjo.accumulate(attribute, config.isKeepStrings() ? ((String)token) :XML.stringToValue((String)token));
token = null; token = null;
} else { } else {
newjo.accumulate(attribute, ""); newjo.accumulate(attribute, "");
@@ -181,7 +206,12 @@ public class JSONML {
if (token != XML.GT) { if (token != XML.GT) {
throw x.syntaxError("Misshaped tag"); throw x.syntaxError("Misshaped tag");
} }
closeTag = (String)parse(x, arrayForm, newja, keepStrings);
if (currentNestingDepth == config.getMaxNestingDepth()) {
throw x.syntaxError("Maximum nesting depth of " + config.getMaxNestingDepth() + " reached");
}
closeTag = (String)parse(x, arrayForm, newja, config, currentNestingDepth + 1);
if (closeTag != null) { if (closeTag != null) {
if (!closeTag.equals(tagName)) { if (!closeTag.equals(tagName)) {
throw x.syntaxError("Mismatched '" + tagName + throw x.syntaxError("Mismatched '" + tagName +
@@ -203,7 +233,7 @@ public class JSONML {
} else { } else {
if (ja != null) { if (ja != null) {
ja.put(token instanceof String ja.put(token instanceof String
? keepStrings ? XML.unescape((String)token) :XML.stringToValue((String)token) ? (config.isKeepStrings() ? XML.unescape((String)token) : XML.stringToValue((String)token))
: token); : token);
} }
} }
@@ -224,7 +254,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, false); return (JSONArray)parse(new XMLTokener(string), true, null, JSONMLParserConfiguration.ORIGINAL, 0);
} }
@@ -235,8 +265,8 @@ public class JSONML {
* attributes, then the second element will be JSONObject containing the * attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and * name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child tags. * JSONArrays will represent the child tags.
* As opposed to toJSONArray this method does not attempt to convert * As opposed to toJSONArray this method does not attempt to convert
* any text node or attribute value to any type * any text node or attribute value to any type
* but just leaves it as a string. * but just leaves it as a string.
* Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored. * Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored.
* @param string The source string. * @param string The source string.
@@ -246,7 +276,32 @@ 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); return (JSONArray)parse(new XMLTokener(string), true, null, keepStrings, 0);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONArray using the JsonML transform. Each XML tag is represented as
* a JSONArray in which the first element is the tag name. If the tag has
* attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child tags.
* As opposed to toJSONArray this method does not attempt to convert
* any text node or attribute value to any type
* but just leaves it as a string.
* Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored.
* @param string The source string.
* @param config The parser configuration:
* JSONMLParserConfiguration.ORIGINAL is the default behaviour;
* JSONMLParserConfiguration.KEEP_STRINGS means values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONArray containing the structured data from the XML string.
* @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);
} }
@@ -257,8 +312,32 @@ public class JSONML {
* attributes, then the second element will be JSONObject containing the * attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and * name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child content and tags. * JSONArrays will represent the child content and tags.
* As opposed to toJSONArray this method does not attempt to convert * As opposed to toJSONArray this method does not attempt to convert
* any text node or attribute value to any type * any text node or attribute value to any type
* but just leaves it as a string.
* Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored.
* @param x An XMLTokener.
* @param config The parser configuration:
* JSONMLParserConfiguration.ORIGINAL is the default behaviour;
* JSONMLParserConfiguration.KEEP_STRINGS means values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONArray containing the structured data from the XML string.
* @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);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONArray using the JsonML transform. Each XML tag is represented as
* a JSONArray in which the first element is the tag name. If the tag has
* attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child content and tags.
* As opposed to toJSONArray this method does not attempt to convert
* any text node or attribute value to any type
* but just leaves it as a string. * but just leaves it as a string.
* Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored. * Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored.
* @param x An XMLTokener. * @param x An XMLTokener.
@@ -268,7 +347,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); return (JSONArray)parse(x, true, null, keepStrings, 0);
} }
@@ -285,7 +364,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); return (JSONArray)parse(x, true, null, false, 0);
} }
@@ -303,10 +382,10 @@ 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); return (JSONObject)parse(new XMLTokener(string), false, null, false, 0);
} }
/** /**
* Convert a well-formed (but not necessarily valid) XML string into a * Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as * JSONObject using the JsonML transform. Each XML tag is represented as
@@ -323,10 +402,32 @@ 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); return (JSONObject)parse(new XMLTokener(string), false, null, keepStrings, 0);
} }
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as
* a JSONObject with a "tagName" property. If the tag has attributes, then
* the attributes will be in the JSONObject as properties. If the tag
* contains children, the object will have a "childNodes" property which
* will be an array of strings and JsonML JSONObjects.
* Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored.
* @param string The XML source text.
* @param config The parser configuration:
* JSONMLParserConfiguration.ORIGINAL is the default behaviour;
* JSONMLParserConfiguration.KEEP_STRINGS means values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @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);
}
/** /**
* Convert a well-formed (but not necessarily valid) XML string into a * Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as * JSONObject using the JsonML transform. Each XML tag is represented as
@@ -341,7 +442,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); return (JSONObject)parse(x, false, null, false, 0);
} }
@@ -361,7 +462,29 @@ 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); return (JSONObject)parse(x, false, null, keepStrings, 0);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as
* a JSONObject with a "tagName" property. If the tag has attributes, then
* the attributes will be in the JSONObject as properties. If the tag
* contains children, the object will have a "childNodes" property which
* will be an array of strings and JsonML JSONObjects.
* Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored.
* @param x An XMLTokener of the XML source text.
* @param config The parser configuration:
* JSONMLParserConfiguration.ORIGINAL is the default behaviour;
* JSONMLParserConfiguration.KEEP_STRINGS means values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @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);
} }
@@ -442,6 +565,7 @@ public class JSONML {
return sb.toString(); return sb.toString();
} }
/** /**
* Reverse the JSONML transformation, making an XML text from a JSONObject. * Reverse the JSONML transformation, making an XML text from a JSONObject.
* The JSONObject must contain a "tagName" property. If it has children, * The JSONObject must contain a "tagName" property. If it has children,

View File

@@ -0,0 +1,67 @@
package org.json;
/*
Public Domain.
*/
/**
* Configuration object for the XML to JSONML parser. The configuration is immutable.
*/
@SuppressWarnings({""})
public class JSONMLParserConfiguration extends ParserConfiguration {
/**
* We can override the default maximum nesting depth if needed.
*/
public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH;
/** Original Configuration of the XML to JSONML Parser. */
public static final JSONMLParserConfiguration ORIGINAL
= new JSONMLParserConfiguration();
/** Original configuration of the XML to JSONML Parser except that values are kept as strings. */
public static final JSONMLParserConfiguration KEEP_STRINGS
= new JSONMLParserConfiguration().withKeepStrings(true);
/**
* Default parser configuration. Does not keep strings (tries to implicitly convert values).
*/
public JSONMLParserConfiguration() {
super();
this.maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH;
}
/**
* Configure the parser string processing and use the default CDATA Tag Name as "content".
* @param keepStrings <code>true</code> to parse all values as string.
* <code>false</code> to try and convert XML string values into a JSON value.
* @param maxNestingDepth <code>int</code> to limit the nesting depth
*/
protected JSONMLParserConfiguration(final boolean keepStrings, final int maxNestingDepth) {
super(keepStrings, maxNestingDepth);
}
/**
* Provides a new instance of the same configuration.
*/
@Override
protected JSONMLParserConfiguration clone() {
// future modifications to this method should always ensure a "deep"
// clone in the case of collections. i.e. if a Map is added as a configuration
// item, a new map instance should be created and if possible each value in the
// map should be cloned as well. If the values of the map are known to also
// be immutable, then a shallow clone of the map is acceptable.
return new JSONMLParserConfiguration(
this.keepStrings,
this.maxNestingDepth
);
}
@Override
public JSONMLParserConfiguration withKeepStrings(final boolean newVal) {
return super.withKeepStrings(newVal);
}
@Override
public JSONMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) {
return super.withMaxNestingDepth(maxNestingDepth);
}
}

View File

@@ -1290,11 +1290,7 @@ public class JSONObject {
if (val == null) { if (val == null) {
return defaultValue; return defaultValue;
} }
final double doubleValue = val.doubleValue(); return val.doubleValue();
// if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) {
// return defaultValue;
// }
return doubleValue;
} }
/** /**

View File

@@ -11,13 +11,13 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Documented
@Retention(RUNTIME)
@Target({METHOD})
/** /**
* Use this annotation on a getter method to override the Bean name * Use this annotation on a getter method to override the Bean name
* parser for Bean -&gt; JSONObject mapping. If this annotation is * parser for Bean -&gt; JSONObject mapping. If this annotation is
* present at any level in the class hierarchy, then the method will * present at any level in the class hierarchy, then the method will
* not be serialized from the bean into the JSONObject. * not be serialized from the bean into the JSONObject.
*/ */
@Documented
@Retention(RUNTIME)
@Target({METHOD})
public @interface JSONPropertyIgnore { } public @interface JSONPropertyIgnore { }

View File

@@ -11,14 +11,14 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Documented
@Retention(RUNTIME)
@Target({METHOD})
/** /**
* Use this annotation on a getter method to override the Bean name * Use this annotation on a getter method to override the Bean name
* parser for Bean -&gt; JSONObject mapping. A value set to empty string <code>""</code> * parser for Bean -&gt; JSONObject mapping. A value set to empty string <code>""</code>
* will have the Bean parser fall back to the default field name processing. * will have the Bean parser fall back to the default field name processing.
*/ */
@Documented
@Retention(RUNTIME)
@Target({METHOD})
public @interface JSONPropertyName { public @interface JSONPropertyName {
/** /**
* @return The name of the property as to be used in the JSON Object. * @return The name of the property as to be used in the JSON Object.

View File

@@ -1,11 +1,7 @@
package org.json; package org.json;
import java.io.BufferedReader; import java.io.*;
import java.io.IOException; import java.nio.charset.Charset;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
/* /*
Public Domain. Public Domain.
@@ -61,7 +57,7 @@ public class JSONTokener {
* @param inputStream The source. * @param inputStream The source.
*/ */
public JSONTokener(InputStream inputStream) { public JSONTokener(InputStream inputStream) {
this(new InputStreamReader(inputStream)); this(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
} }
@@ -125,7 +121,7 @@ public class JSONTokener {
/** /**
* Checks if the end of the input has been reached. * Checks if the end of the input has been reached.
* *
* @return true if at the end of the file and we didn't step back * @return true if at the end of the file and we didn't step back
*/ */
public boolean end() { public boolean end() {
@@ -189,7 +185,7 @@ public class JSONTokener {
this.previous = (char) c; this.previous = (char) c;
return this.previous; return this.previous;
} }
/** /**
* Get the last character read from the input or '\0' if nothing has been read yet. * Get the last character read from the input or '\0' if nothing has been read yet.
* @return the last character read from the input. * @return the last character read from the input.
@@ -522,4 +518,10 @@ public class JSONTokener {
return " at " + this.index + " [character " + this.character + " line " + return " at " + this.index + " [character " + this.character + " line " +
this.line + "]"; this.line + "]";
} }
public void close() throws IOException {
if(reader!=null){
reader.close();
}
}
} }

View File

@@ -0,0 +1,112 @@
package org.json;
/*
Public Domain.
*/
/**
* Configuration base object for parsers. The configuration is immutable.
*/
@SuppressWarnings({""})
public class ParserConfiguration {
/**
* Used to indicate there's no defined limit to the maximum nesting depth when parsing a document.
*/
public static final int UNDEFINED_MAXIMUM_NESTING_DEPTH = -1;
/**
* The default maximum nesting depth when parsing a document.
*/
public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512;
/**
* Specifies if values should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*/
protected boolean keepStrings;
/**
* The maximum nesting depth when parsing a document.
*/
protected int maxNestingDepth;
public ParserConfiguration() {
this.keepStrings = false;
this.maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH;
}
protected ParserConfiguration(final boolean keepStrings, final int maxNestingDepth) {
this.keepStrings = keepStrings;
this.maxNestingDepth = maxNestingDepth;
}
/**
* Provides a new instance of the same configuration.
*/
@Override
protected ParserConfiguration clone() {
// future modifications to this method should always ensure a "deep"
// clone in the case of collections. i.e. if a Map is added as a configuration
// item, a new map instance should be created and if possible each value in the
// map should be cloned as well. If the values of the map are known to also
// be immutable, then a shallow clone of the map is acceptable.
return new ParserConfiguration(
this.keepStrings,
this.maxNestingDepth
);
}
/**
* When parsing the XML into JSONML, specifies if values should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*
* @return The <code>keepStrings</code> configuration value.
*/
public boolean isKeepStrings() {
return this.keepStrings;
}
/**
* When parsing the XML into JSONML, specifies if values should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*
* @param newVal
* new value to use for the <code>keepStrings</code> configuration option.
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
public <T extends ParserConfiguration> T withKeepStrings(final boolean newVal) {
T newConfig = (T)this.clone();
newConfig.keepStrings = newVal;
return newConfig;
}
/**
* The maximum nesting depth that the parser will descend before throwing an exception
* when parsing the XML into JSONML.
* @return the maximum nesting depth set for this configuration
*/
public int getMaxNestingDepth() {
return maxNestingDepth;
}
/**
* Defines the maximum nesting depth that the parser will descend before throwing an exception
* when parsing the XML into JSONML. The default max nesting depth is 512, which means the parser
* will throw a JsonException if the maximum depth is reached.
* Using any negative value as a parameter is equivalent to setting no limit to the nesting depth,
* which means the parses will go as deep as the maximum call stack size allows.
* @param maxNestingDepth the maximum nesting depth allowed to the XML parser
* @return The existing configuration will not be modified. A new configuration is returned.
*/
public <T extends ParserConfiguration> T withMaxNestingDepth(int maxNestingDepth) {
T newConfig = (T)this.clone();
if (maxNestingDepth > UNDEFINED_MAXIMUM_NESTING_DEPTH) {
newConfig.maxNestingDepth = maxNestingDepth;
} else {
newConfig.maxNestingDepth = UNDEFINED_MAXIMUM_NESTING_DEPTH;
}
return newConfig;
}
}

View File

@@ -98,7 +98,7 @@ public class XML {
/** /**
* Replace special characters with XML escapes: * Replace special characters with XML escapes:
* *
* <pre>{@code * <pre>{@code
* &amp; (ampersand) is replaced by &amp;amp; * &amp; (ampersand) is replaced by &amp;amp;
* &lt; (less than) is replaced by &amp;lt; * &lt; (less than) is replaced by &amp;lt;
* &gt; (greater than) is replaced by &amp;gt; * &gt; (greater than) is replaced by &amp;gt;
@@ -229,10 +229,14 @@ public class XML {
* The JSONObject that will include the new material. * The JSONObject that will include the new material.
* @param name * @param name
* The tag name. * The tag name.
* @param config
* The XML parser configuration.
* @param currentNestingDepth
* The current nesting depth.
* @return true if the close tag is processed. * @return true if the close tag is processed.
* @throws JSONException * @throws JSONException Thrown if any parsing error occurs.
*/ */
private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config) private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, int currentNestingDepth)
throws JSONException { throws JSONException {
char c; char c;
int i; int i;
@@ -402,7 +406,11 @@ public class XML {
} else if (token == LT) { } else if (token == LT) {
// Nested element // Nested element
if (parse(x, jsonObject, tagName, config)) { if (currentNestingDepth == config.getMaxNestingDepth()) {
throw x.syntaxError("Maximum nesting depth of " + config.getMaxNestingDepth() + " reached");
}
if (parse(x, jsonObject, tagName, config, currentNestingDepth + 1)) {
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) {
@@ -423,7 +431,7 @@ public class XML {
context.accumulate(tagName, jsonObject); context.accumulate(tagName, jsonObject);
} }
} }
return false; return false;
} }
} }
@@ -487,7 +495,7 @@ public class XML {
} }
return string; return string;
} }
/** /**
* direct copy of {@link JSONObject#stringToNumber(String)} to maintain Android support. * direct copy of {@link JSONObject#stringToNumber(String)} to maintain Android support.
*/ */
@@ -534,7 +542,7 @@ public class XML {
// integer representation. // integer representation.
// This will narrow any values to the smallest reasonable Object representation // This will narrow any values to the smallest reasonable Object representation
// (Integer, Long, or BigInteger) // (Integer, Long, or BigInteger)
// BigInteger down conversion: We use a similar bitLength compare as // BigInteger down conversion: We use a similar bitLength compare as
// BigInteger#intValueExact uses. Increases GC, but objects hold // BigInteger#intValueExact uses. Increases GC, but objects hold
// only what they need. i.e. Less runtime overhead if the value is // only what they need. i.e. Less runtime overhead if the value is
@@ -550,7 +558,7 @@ public class XML {
} }
throw new NumberFormatException("val ["+val+"] is not a valid number."); throw new NumberFormatException("val ["+val+"] is not a valid number.");
} }
/** /**
* direct copy of {@link JSONObject#isDecimalNotation(String)} to maintain Android support. * direct copy of {@link JSONObject#isDecimalNotation(String)} to maintain Android support.
*/ */
@@ -568,7 +576,7 @@ public class XML {
* name/value pairs and arrays of values. JSON does not does not like to * name/value pairs and arrays of values. JSON does not does not like to
* distinguish between elements and attributes. Sequences of similar * distinguish between elements and attributes. Sequences of similar
* elements are represented as JSONArrays. Content text may be placed in a * elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <pre>{@code * "content" member. Comments, prologs, DTDs, and <pre>{@code
* &lt;[ [ ]]>}</pre> * &lt;[ [ ]]>}</pre>
* are ignored. * are ignored.
* *
@@ -589,7 +597,7 @@ public class XML {
* name/value pairs and arrays of values. JSON does not does not like to * name/value pairs and arrays of values. JSON does not does not like to
* distinguish between elements and attributes. Sequences of similar * distinguish between elements and attributes. Sequences of similar
* elements are represented as JSONArrays. Content text may be placed in a * elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <pre>{@code * "content" member. Comments, prologs, DTDs, and <pre>{@code
* &lt;[ [ ]]>}</pre> * &lt;[ [ ]]>}</pre>
* are ignored. * are ignored.
* *
@@ -655,7 +663,7 @@ public class XML {
while (x.more()) { while (x.more()) {
x.skipPast("<"); x.skipPast("<");
if(x.more()) { if(x.more()) {
parse(x, jo, null, config); parse(x, jo, null, config, 0);
} }
} }
return jo; return jo;
@@ -669,7 +677,7 @@ public class XML {
* name/value pairs and arrays of values. JSON does not does not like to * name/value pairs and arrays of values. JSON does not does not like to
* distinguish between elements and attributes. Sequences of similar * distinguish between elements and attributes. Sequences of similar
* elements are represented as JSONArrays. Content text may be placed in a * elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <pre>{@code * "content" member. Comments, prologs, DTDs, and <pre>{@code
* &lt;[ [ ]]>}</pre> * &lt;[ [ ]]>}</pre>
* are ignored. * are ignored.
* *
@@ -695,7 +703,7 @@ public class XML {
* name/value pairs and arrays of values. JSON does not does not like to * name/value pairs and arrays of values. JSON does not does not like to
* distinguish between elements and attributes. Sequences of similar * distinguish between elements and attributes. Sequences of similar
* elements are represented as JSONArrays. Content text may be placed in a * elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <pre>{@code * "content" member. Comments, prologs, DTDs, and <pre>{@code
* &lt;[ [ ]]>}</pre> * &lt;[ [ ]]>}</pre>
* are ignored. * are ignored.
* *

View File

@@ -15,7 +15,13 @@ import java.util.Set;
* @author AylwardJ * @author AylwardJ
*/ */
@SuppressWarnings({""}) @SuppressWarnings({""})
public class XMLParserConfiguration { public class XMLParserConfiguration extends ParserConfiguration {
/**
* The default maximum nesting depth when parsing a XML document to JSON.
*/
// public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512; // We could override
/** Original Configuration of the XML Parser. */ /** Original Configuration of the XML Parser. */
public static final XMLParserConfiguration ORIGINAL public static final XMLParserConfiguration ORIGINAL
= new XMLParserConfiguration(); = new XMLParserConfiguration();
@@ -23,19 +29,13 @@ public class XMLParserConfiguration {
public static final XMLParserConfiguration KEEP_STRINGS public static final XMLParserConfiguration KEEP_STRINGS
= new XMLParserConfiguration().withKeepStrings(true); = new XMLParserConfiguration().withKeepStrings(true);
/**
* When parsing the XML into JSON, specifies if values should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*/
private boolean keepStrings;
/** /**
* The name of the key in a JSON Object that indicates a CDATA section. Historically this has * The name of the key in a JSON Object that indicates a CDATA section. Historically this has
* been the value "content" but can be changed. Use <code>null</code> to indicate no CDATA * been the value "content" but can be changed. Use <code>null</code> to indicate no CDATA
* processing. * processing.
*/ */
private String cDataTagName; private String cDataTagName;
/** /**
* When parsing the XML into JSON, specifies if values with attribute xsi:nil="true" * When parsing the XML into JSON, specifies if values with attribute xsi:nil="true"
* should be kept as attribute(<code>false</code>), or they should be converted to * should be kept as attribute(<code>false</code>), or they should be converted to
@@ -59,7 +59,7 @@ public class XMLParserConfiguration {
* values), and the CDATA Tag Name is "content". * values), and the CDATA Tag Name is "content".
*/ */
public XMLParserConfiguration () { public XMLParserConfiguration () {
this.keepStrings = false; super();
this.cDataTagName = "content"; this.cDataTagName = "content";
this.convertNilAttributeToNull = false; this.convertNilAttributeToNull = false;
this.xsiTypeMap = Collections.emptyMap(); this.xsiTypeMap = Collections.emptyMap();
@@ -106,7 +106,7 @@ public class XMLParserConfiguration {
*/ */
@Deprecated @Deprecated
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName) { public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName) {
this.keepStrings = keepStrings; super(keepStrings, DEFAULT_MAXIMUM_NESTING_DEPTH);
this.cDataTagName = cDataTagName; this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = false; this.convertNilAttributeToNull = false;
} }
@@ -125,7 +125,7 @@ public class XMLParserConfiguration {
*/ */
@Deprecated @Deprecated
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) { public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) {
this.keepStrings = keepStrings; super(keepStrings, DEFAULT_MAXIMUM_NESTING_DEPTH);
this.cDataTagName = cDataTagName; this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = convertNilAttributeToNull; this.convertNilAttributeToNull = convertNilAttributeToNull;
} }
@@ -140,11 +140,13 @@ public class XMLParserConfiguration {
* <code>false</code> to parse values with attribute xsi:nil="true" as {"xsi:nil":true}. * <code>false</code> to parse values with attribute xsi:nil="true" as {"xsi:nil":true}.
* @param xsiTypeMap <code>new HashMap<String, XMLXsiTypeConverter<?>>()</code> to parse values with attribute * @param xsiTypeMap <code>new HashMap<String, XMLXsiTypeConverter<?>>()</code> to parse values with attribute
* xsi:type="integer" as integer, xsi:type="string" as string * xsi:type="integer" as integer, xsi:type="string" as string
* @param forceList <code>new HashSet<String>()</code> to parse the provided tags' values as arrays * @param forceList <code>new HashSet<String>()</code> to parse the provided tags' values as arrays
* @param maxNestingDepth <code>int</code> to limit the nesting depth
*/ */
private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName,
final boolean convertNilAttributeToNull, final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap, final Set<String> forceList ) { final boolean convertNilAttributeToNull, final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap, final Set<String> forceList,
this.keepStrings = keepStrings; final int maxNestingDepth) {
super(keepStrings, maxNestingDepth);
this.cDataTagName = cDataTagName; this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = convertNilAttributeToNull; this.convertNilAttributeToNull = convertNilAttributeToNull;
this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap); this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap);
@@ -166,40 +168,30 @@ public class XMLParserConfiguration {
this.cDataTagName, this.cDataTagName,
this.convertNilAttributeToNull, this.convertNilAttributeToNull,
this.xsiTypeMap, this.xsiTypeMap,
this.forceList this.forceList,
this.maxNestingDepth
); );
} }
/**
* When parsing the XML into JSON, specifies if values should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*
* @return The <code>keepStrings</code> configuration value.
*/
public boolean isKeepStrings() {
return this.keepStrings;
}
/** /**
* When parsing the XML into JSON, specifies if values should be kept as strings (<code>true</code>), or if * When parsing the XML into JSON, specifies if values should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string) * they should try to be guessed into JSON values (numeric, boolean, string)
* *
* @param newVal * @param newVal
* new value to use for the <code>keepStrings</code> configuration option. * new value to use for the <code>keepStrings</code> configuration option.
* *
* @return The existing configuration will not be modified. A new configuration is returned. * @return The existing configuration will not be modified. A new configuration is returned.
*/ */
@Override
public XMLParserConfiguration withKeepStrings(final boolean newVal) { public XMLParserConfiguration withKeepStrings(final boolean newVal) {
XMLParserConfiguration newConfig = this.clone(); return super.withKeepStrings(newVal);
newConfig.keepStrings = newVal;
return newConfig;
} }
/** /**
* The name of the key in a JSON Object that indicates a CDATA section. Historically this has * The name of the key in a JSON Object that indicates a CDATA section. Historically this has
* been the value "content" but can be changed. Use <code>null</code> to indicate no CDATA * been the value "content" but can be changed. Use <code>null</code> to indicate no CDATA
* processing. * processing.
* *
* @return The <code>cDataTagName</code> configuration value. * @return The <code>cDataTagName</code> configuration value.
*/ */
public String getcDataTagName() { public String getcDataTagName() {
@@ -210,10 +202,10 @@ public class XMLParserConfiguration {
* The name of the key in a JSON Object that indicates a CDATA section. Historically this has * The name of the key in a JSON Object that indicates a CDATA section. Historically this has
* been the value "content" but can be changed. Use <code>null</code> to indicate no CDATA * been the value "content" but can be changed. Use <code>null</code> to indicate no CDATA
* processing. * processing.
* *
* @param newVal * @param newVal
* new value to use for the <code>cDataTagName</code> configuration option. * new value to use for the <code>cDataTagName</code> configuration option.
* *
* @return The existing configuration will not be modified. A new configuration is returned. * @return The existing configuration will not be modified. A new configuration is returned.
*/ */
public XMLParserConfiguration withcDataTagName(final String newVal) { public XMLParserConfiguration withcDataTagName(final String newVal) {
@@ -226,7 +218,7 @@ public class XMLParserConfiguration {
* When parsing the XML into JSON, specifies if values with attribute xsi:nil="true" * When parsing the XML into JSON, specifies if values with attribute xsi:nil="true"
* should be kept as attribute(<code>false</code>), or they should be converted to * should be kept as attribute(<code>false</code>), or they should be converted to
* <code>null</code>(<code>true</code>) * <code>null</code>(<code>true</code>)
* *
* @return The <code>convertNilAttributeToNull</code> configuration value. * @return The <code>convertNilAttributeToNull</code> configuration value.
*/ */
public boolean isConvertNilAttributeToNull() { public boolean isConvertNilAttributeToNull() {
@@ -237,10 +229,10 @@ public class XMLParserConfiguration {
* When parsing the XML into JSON, specifies if values with attribute xsi:nil="true" * When parsing the XML into JSON, specifies if values with attribute xsi:nil="true"
* should be kept as attribute(<code>false</code>), or they should be converted to * should be kept as attribute(<code>false</code>), or they should be converted to
* <code>null</code>(<code>true</code>) * <code>null</code>(<code>true</code>)
* *
* @param newVal * @param newVal
* new value to use for the <code>convertNilAttributeToNull</code> configuration option. * new value to use for the <code>convertNilAttributeToNull</code> configuration option.
* *
* @return The existing configuration will not be modified. A new configuration is returned. * @return The existing configuration will not be modified. A new configuration is returned.
*/ */
public XMLParserConfiguration withConvertNilAttributeToNull(final boolean newVal) { public XMLParserConfiguration withConvertNilAttributeToNull(final boolean newVal) {
@@ -278,7 +270,7 @@ public class XMLParserConfiguration {
/** /**
* When parsing the XML into JSON, specifies that tags that will be converted to arrays * When parsing the XML into JSON, specifies that tags that will be converted to arrays
* in this configuration {@code Set<String>} to parse the provided tags' values as arrays * in this configuration {@code Set<String>} to parse the provided tags' values as arrays
* @return <code>forceList</code> unmodifiable configuration set. * @return <code>forceList</code> unmodifiable configuration set.
*/ */
public Set<String> getForceList() { public Set<String> getForceList() {
@@ -287,8 +279,8 @@ public class XMLParserConfiguration {
/** /**
* When parsing the XML into JSON, specifies that tags that will be converted to arrays * When parsing the XML into JSON, specifies that tags that will be converted to arrays
* in this configuration {@code Set<String>} to parse the provided tags' values as arrays * in this configuration {@code Set<String>} to parse the provided tags' values as arrays
* @param forceList {@code new HashSet<String>()} to parse the provided tags' values as arrays * @param forceList {@code new HashSet<String>()} to parse the provided tags' values as arrays
* @return The existing configuration will not be modified. A new configuration is returned. * @return The existing configuration will not be modified. A new configuration is returned.
*/ */
public XMLParserConfiguration withForceList(final Set<String> forceList) { public XMLParserConfiguration withForceList(final Set<String> forceList) {
@@ -297,4 +289,18 @@ public class XMLParserConfiguration {
newConfig.forceList = Collections.unmodifiableSet(cloneForceList); newConfig.forceList = Collections.unmodifiableSet(cloneForceList);
return newConfig; return newConfig;
} }
/**
* Defines the maximum nesting depth that the parser will descend before throwing an exception
* when parsing the XML into JSON. The default max nesting depth is 512, which means the parser
* will throw a JsonException if the maximum depth is reached.
* Using any negative value as a parameter is equivalent to setting no limit to the nesting depth,
* which means the parses will go as deep as the maximum call stack size allows.
* @param maxNestingDepth the maximum nesting depth allowed to the XML parser
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@Override
public XMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) {
return super.withMaxNestingDepth(maxNestingDepth);
}
} }

View File

@@ -6,7 +6,6 @@ Public Domain.
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;

View File

@@ -11,19 +11,19 @@ import org.junit.Test;
/** /**
* Tests for org.json.JSONML.java * Tests for org.json.JSONML.java
* *
* Certain inputs are expected to result in exceptions. These tests are * Certain inputs are expected to result in exceptions. These tests are
* executed first. JSONML provides an API to: * executed first. JSONML provides an API to:
* Convert an XML string into a JSONArray or a JSONObject. * Convert an XML string into a JSONArray or a JSONObject.
* Convert a JSONArray or JSONObject into an XML string. * Convert a JSONArray or JSONObject into an XML string.
* Both fromstring and tostring operations operations should be symmetrical * Both fromstring and tostring operations operations should be symmetrical
* within the limits of JSONML. * within the limits of JSONML.
* It should be possible to perform the following operations, which should * It should be possible to perform the following operations, which should
* result in the original string being recovered, within the limits of the * result in the original string being recovered, within the limits of the
* underlying classes: * underlying classes:
* Convert a string -> JSONArray -> string -> JSONObject -> string * Convert a string -> JSONArray -> string -> JSONObject -> string
* Convert a string -> JSONObject -> string -> JSONArray -> string * Convert a string -> JSONObject -> string -> JSONArray -> string
* *
*/ */
public class JSONMLTest { public class JSONMLTest {
@@ -56,7 +56,7 @@ public class JSONMLTest {
/** /**
* Attempts to call JSONML.toString() with a null JSONArray. * Attempts to call JSONML.toString() with a null JSONArray.
* Expects a NullPointerException. * Expects a NullPointerException.
*/ */
@Test(expected=NullPointerException.class) @Test(expected=NullPointerException.class)
public void nullJSONXMLException() { public void nullJSONXMLException() {
@@ -69,7 +69,7 @@ public class JSONMLTest {
/** /**
* Attempts to call JSONML.toString() with a null JSONArray. * Attempts to call JSONML.toString() with a null JSONArray.
* Expects a JSONException. * Expects a JSONException.
*/ */
@Test @Test
public void emptyJSONXMLException() { public void emptyJSONXMLException() {
@@ -125,7 +125,7 @@ public class JSONMLTest {
"[\"addresses\","+ "[\"addresses\","+
"{\"xsi:noNamespaceSchemaLocation\":\"test.xsd\","+ "{\"xsi:noNamespaceSchemaLocation\":\"test.xsd\","+
"\"xmlns:xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"},"+ "\"xmlns:xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"},"+
// this array has no name // this array has no name
"["+ "["+
"[\"name\"],"+ "[\"name\"],"+
"[\"nocontent\"],"+ "[\"nocontent\"],"+
@@ -180,7 +180,7 @@ public class JSONMLTest {
} }
/** /**
* Attempts to transform a malformed XML document * Attempts to transform a malformed XML document
* (element tag has a frontslash) to a JSONArray.\ * (element tag has a frontslash) to a JSONArray.\
* Expects a JSONException * Expects a JSONException
*/ */
@@ -191,7 +191,7 @@ public class JSONMLTest {
* In this case, the XML is invalid because the 'name' element * In this case, the XML is invalid because the 'name' element
* contains an invalid frontslash. * contains an invalid frontslash.
*/ */
String xmlStr = String xmlStr =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+ "<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+ " xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
@@ -216,7 +216,7 @@ public class JSONMLTest {
*/ */
@Test @Test
public void invalidBangInTagException() { public void invalidBangInTagException() {
String xmlStr = String xmlStr =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+ "<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+ " xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
@@ -246,7 +246,7 @@ public class JSONMLTest {
* In this case, the XML is invalid because an element * In this case, the XML is invalid because an element
* starts with '!' and has no closing tag * starts with '!' and has no closing tag
*/ */
String xmlStr = String xmlStr =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+ "<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+ " xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
@@ -276,7 +276,7 @@ public class JSONMLTest {
* In this case, the XML is invalid because an element * In this case, the XML is invalid because an element
* has no closing '>'. * has no closing '>'.
*/ */
String xmlStr = String xmlStr =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+ "<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+ " xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
@@ -306,7 +306,7 @@ public class JSONMLTest {
* In this case, the XML is invalid because an element * In this case, the XML is invalid because an element
* has no name after the closing tag '</'. * has no name after the closing tag '</'.
*/ */
String xmlStr = String xmlStr =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+ "<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+ " xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
@@ -336,7 +336,7 @@ public class JSONMLTest {
* In this case, the XML is invalid because an element * In this case, the XML is invalid because an element
* has '>' after the closing tag '</' and name. * has '>' after the closing tag '</' and name.
*/ */
String xmlStr = String xmlStr =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+ "<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
" xsi:noNamespaceSchemaLocation=\"test.xsd\">\n"+ " xsi:noNamespaceSchemaLocation=\"test.xsd\">\n"+
@@ -364,9 +364,9 @@ public class JSONMLTest {
/** /**
* xmlStr contains XML text which is transformed into a JSONArray. * xmlStr contains XML text which is transformed into a JSONArray.
* In this case, the XML is invalid because an element * In this case, the XML is invalid because an element
* does not have a complete CDATA string. * does not have a complete CDATA string.
*/ */
String xmlStr = String xmlStr =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+ "<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
" xsi:noNamespaceSchemaLocation='test.xsd'>\n"+ " xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
@@ -388,7 +388,7 @@ public class JSONMLTest {
/** /**
* Convert an XML document into a JSONArray, then use JSONML.toString() * Convert an XML document into a JSONArray, then use JSONML.toString()
* to convert it into a string. This string is then converted back into * to convert it into a string. This string is then converted back into
* a JSONArray. Both JSONArrays are compared against a control to * a JSONArray. Both JSONArrays are compared against a control to
* confirm the contents. * confirm the contents.
*/ */
@Test @Test
@@ -405,7 +405,7 @@ public class JSONMLTest {
* which is used to create a final JSONArray, which is also compared * which is used to create a final JSONArray, which is also compared
* against the expected JSONArray. * against the expected JSONArray.
*/ */
String xmlStr = String xmlStr =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+ "<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
"xsi:noNamespaceSchemaLocation='test.xsd'>\n"+ "xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
@@ -414,7 +414,7 @@ public class JSONMLTest {
"<nocontent/>>\n"+ "<nocontent/>>\n"+
"</address>\n"+ "</address>\n"+
"</addresses>"; "</addresses>";
String expectedStr = String expectedStr =
"[\"addresses\","+ "[\"addresses\","+
"{\"xsi:noNamespaceSchemaLocation\":\"test.xsd\","+ "{\"xsi:noNamespaceSchemaLocation\":\"test.xsd\","+
"\"xmlns:xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"},"+ "\"xmlns:xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"},"+
@@ -434,12 +434,12 @@ public class JSONMLTest {
} }
/** /**
* Convert an XML document into a JSONObject. Use JSONML.toString() to * Convert an XML document into a JSONObject. Use JSONML.toString() to
* convert it back into a string, and then re-convert it into a JSONObject. * convert it back into a string, and then re-convert it into a JSONObject.
* Both JSONObjects are compared against a control JSONObject to confirm * Both JSONObjects are compared against a control JSONObject to confirm
* the contents. * the contents.
* <p> * <p>
* Next convert the XML document into a JSONArray. Use JSONML.toString() to * Next convert the XML document into a JSONArray. Use JSONML.toString() to
* convert it back into a string, and then re-convert it into a JSONArray. * convert it back into a string, and then re-convert it into a JSONArray.
* Both JSONArrays are compared against a control JSONArray to confirm * Both JSONArrays are compared against a control JSONArray to confirm
* the contents. * the contents.
@@ -452,23 +452,23 @@ public class JSONMLTest {
/** /**
* xmlStr contains XML text which is transformed into a JSONObject, * xmlStr contains XML text which is transformed into a JSONObject,
* restored to XML, transformed into a JSONArray, and then restored * restored to XML, transformed into a JSONArray, and then restored
* to XML again. Both JSONObject and JSONArray should contain the same * to XML again. Both JSONObject and JSONArray should contain the same
* information and should produce the same XML, allowing for non-ordered * information and should produce the same XML, allowing for non-ordered
* attributes. * attributes.
* *
* Transformation to JSONObject: * Transformation to JSONObject:
* The elementName is stored as a string where key="tagName" * The elementName is stored as a string where key="tagName"
* Attributes are simply stored as key/value pairs * Attributes are simply stored as key/value pairs
* If the element has either content or child elements, they are stored * If the element has either content or child elements, they are stored
* in a jsonArray with key="childNodes". * in a jsonArray with key="childNodes".
* *
* Transformation to JSONArray: * Transformation to JSONArray:
* 1st entry = elementname * 1st entry = elementname
* 2nd entry = attributes object (if present) * 2nd entry = attributes object (if present)
* 3rd entry = content (if present) * 3rd entry = content (if present)
* 4th entry = child element JSONArrays (if present) * 4th entry = child element JSONArrays (if present)
*/ */
String xmlStr = String xmlStr =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+ "<addresses xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""+
"xsi:noNamespaceSchemaLocation='test.xsd'>\n"+ "xsi:noNamespaceSchemaLocation='test.xsd'>\n"+
@@ -585,7 +585,7 @@ public class JSONMLTest {
"\"tagName\":\"addresses\""+ "\"tagName\":\"addresses\""+
"}"; "}";
String expectedJSONArrayStr = String expectedJSONArrayStr =
"["+ "["+
"\"addresses\","+ "\"addresses\","+
"{"+ "{"+
@@ -645,12 +645,12 @@ public class JSONMLTest {
JSONObject finalJsonObject = JSONML.toJSONObject(jsonObjectXmlToStr); JSONObject finalJsonObject = JSONML.toJSONObject(jsonObjectXmlToStr);
Util.compareActualVsExpectedJsonObjects(finalJsonObject, expectedJsonObject); Util.compareActualVsExpectedJsonObjects(finalJsonObject, expectedJsonObject);
// create a JSON array from the original string and make sure it // create a JSON array from the original string and make sure it
// looks as expected // looks as expected
JSONArray jsonArray = JSONML.toJSONArray(xmlStr); JSONArray jsonArray = JSONML.toJSONArray(xmlStr);
JSONArray expectedJsonArray = new JSONArray(expectedJSONArrayStr); JSONArray expectedJsonArray = new JSONArray(expectedJSONArrayStr);
Util.compareActualVsExpectedJsonArrays(jsonArray,expectedJsonArray); Util.compareActualVsExpectedJsonArrays(jsonArray,expectedJsonArray);
// restore the XML, then make another JSONArray and make sure it // restore the XML, then make another JSONArray and make sure it
// looks as expected // looks as expected
String jsonArrayXmlToStr = JSONML.toString(jsonArray); String jsonArrayXmlToStr = JSONML.toString(jsonArray);
@@ -668,14 +668,14 @@ public class JSONMLTest {
* Convert an XML document which contains embedded comments into * Convert an XML document which contains embedded comments into
* a JSONArray. Use JSONML.toString() to turn it into a string, then * a JSONArray. Use JSONML.toString() to turn it into a string, then
* reconvert it into a JSONArray. Compare both JSONArrays to a control * reconvert it into a JSONArray. Compare both JSONArrays to a control
* JSONArray to confirm the contents. * JSONArray to confirm the contents.
* <p> * <p>
* This test shows how XML comments are handled. * This test shows how XML comments are handled.
*/ */
@Test @Test
public void commentsInXML() { public void commentsInXML() {
String xmlStr = String xmlStr =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<!-- this is a comment -->\n"+ "<!-- this is a comment -->\n"+
"<addresses>\n"+ "<addresses>\n"+
@@ -734,7 +734,7 @@ public class JSONMLTest {
final String expectedJsonString = "[\"root\",[\"id\",\"01\"],[\"id\",\"1\"],[\"id\",\"00\"],[\"id\",\"0\"],[\"item\",{\"id\":\"01\"}],[\"title\",\"True\"]]"; final String expectedJsonString = "[\"root\",[\"id\",\"01\"],[\"id\",\"1\"],[\"id\",\"00\"],[\"id\",\"0\"],[\"item\",{\"id\":\"01\"}],[\"title\",\"True\"]]";
final JSONArray json = JSONML.toJSONArray(originalXml,true); final JSONArray json = JSONML.toJSONArray(originalXml,true);
assertEquals(expectedJsonString, json.toString()); assertEquals(expectedJsonString, json.toString());
final String reverseXml = JSONML.toString(json); final String reverseXml = JSONML.toString(json);
assertEquals(originalXml, reverseXml); assertEquals(originalXml, reverseXml);
} }
@@ -749,7 +749,7 @@ public class JSONMLTest {
final String revertedXml = JSONML.toString(jsonArray); final String revertedXml = JSONML.toString(jsonArray);
assertEquals(revertedXml, originalXml); assertEquals(revertedXml, originalXml);
} }
/** /**
* JSON string cannot be reverted to original xml. See test result in * JSON string cannot be reverted to original xml. See test result in
* comment below. * comment below.
@@ -770,7 +770,7 @@ public class JSONMLTest {
// 1. Our XML parser does not handle generic HTML entities, only valid XML entities. Hence &nbsp; // 1. Our XML parser does not handle generic HTML entities, only valid XML entities. Hence &nbsp;
// or other HTML specific entities would fail on reversability // or other HTML specific entities would fail on reversability
// 2. Our JSON implementation for storing the XML attributes uses the standard unordered map. // 2. Our JSON implementation for storing the XML attributes uses the standard unordered map.
// This means that <tag attr1="v1" attr2="v2" /> can not be reversed reliably. // This means that <tag attr1="v1" attr2="v2" /> can not be reversed reliably.
// //
// /** // /**
// * Test texts taken from jsonml.org. Currently our implementation FAILS this conversion but shouldn't. // * Test texts taken from jsonml.org. Currently our implementation FAILS this conversion but shouldn't.
@@ -783,13 +783,13 @@ public class JSONMLTest {
// final String expectedJsonString = "[\"table\",{\"class\" : \"MyTable\",\"style\" : \"background-color:yellow\"},[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#550758\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:red\"},\"Example text here\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#993101\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:green\"},\"127624015\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#E33D87\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:blue\"},\"\u00A0\",[\"span\",{ \"style\" : \"background-color:maroon\" },\"\u00A9\"],\"\u00A0\"]]]"; // final String expectedJsonString = "[\"table\",{\"class\" : \"MyTable\",\"style\" : \"background-color:yellow\"},[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#550758\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:red\"},\"Example text here\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#993101\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:green\"},\"127624015\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#E33D87\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:blue\"},\"\u00A0\",[\"span\",{ \"style\" : \"background-color:maroon\" },\"\u00A9\"],\"\u00A0\"]]]";
// final JSONArray json = JSONML.toJSONArray(originalXml,true); // final JSONArray json = JSONML.toJSONArray(originalXml,true);
// final String actualJsonString = json.toString(); // final String actualJsonString = json.toString();
// //
// final String reverseXml = JSONML.toString(json); // final String reverseXml = JSONML.toString(json);
// assertNotEquals(originalXml, reverseXml); // assertNotEquals(originalXml, reverseXml);
// //
// assertNotEquals(expectedJsonString, actualJsonString); // assertNotEquals(expectedJsonString, actualJsonString);
// } // }
// //
// /** // /**
// * Test texts taken from jsonml.org but modified to have XML entities only. // * Test texts taken from jsonml.org but modified to have XML entities only.
// */ // */
@@ -799,15 +799,15 @@ public class JSONMLTest {
// final String expectedJsonString = "[\"table\",{\"class\" : \"MyTable\",\"style\" : \"background-color:yellow\"},[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#550758\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:red\"},\"Example text here\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#993101\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:green\"},\"127624015\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#E33D87\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:blue\"},\"&\",[\"span\",{ \"style\" : \"background-color:maroon\" },\">\"],\"<\"]]]"; // final String expectedJsonString = "[\"table\",{\"class\" : \"MyTable\",\"style\" : \"background-color:yellow\"},[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#550758\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:red\"},\"Example text here\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#993101\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:green\"},\"127624015\"]],[\"tr\",[\"td\",{\"class\" : \"MyTD\",\"style\" : \"border:1px solid black\"},\"#E33D87\"],[\"td\",{\"class\" : \"MyTD\",\"style\" : \"background-color:blue\"},\"&\",[\"span\",{ \"style\" : \"background-color:maroon\" },\">\"],\"<\"]]]";
// final JSONArray jsonML = JSONML.toJSONArray(originalXml,true); // final JSONArray jsonML = JSONML.toJSONArray(originalXml,true);
// final String actualJsonString = jsonML.toString(); // final String actualJsonString = jsonML.toString();
// //
// final String reverseXml = JSONML.toString(jsonML); // final String reverseXml = JSONML.toString(jsonML);
// // currently not equal because the hashing of the attribute objects makes the attribute // // currently not equal because the hashing of the attribute objects makes the attribute
// // order not happen the same way twice // // order not happen the same way twice
// assertEquals(originalXml, reverseXml); // assertEquals(originalXml, reverseXml);
// //
// assertEquals(expectedJsonString, actualJsonString); // assertEquals(expectedJsonString, actualJsonString);
// } // }
@Test (timeout = 6000) @Test (timeout = 6000)
public void testIssue484InfinteLoop1() { public void testIssue484InfinteLoop1() {
try { try {
@@ -819,11 +819,11 @@ public class JSONMLTest {
ex.getMessage()); ex.getMessage());
} }
} }
@Test (timeout = 6000) @Test (timeout = 6000)
public void testIssue484InfinteLoop2() { public void testIssue484InfinteLoop2() {
try { try {
String input = "??*\n" + String input = "??*\n" +
"??|?CglR??`??>?w??PIlr??D?$?-?o??O?*??{OD?Y??`2a????NM?bq?:O?>S$ ?J?B.gUK?m\b??zE???!v]???????c??????h???s???g???`?qbi??:Zl?)?}1^??k?0??:$V?$?Ovs(}J??????2;gQ????Tg?K?`?h%c?hmGA?<!C*?9?~?t?)??,zA???S}?Q??.q?j????]"; "??|?CglR??`??>?w??PIlr??D?$?-?o??O?*??{OD?Y??`2a????NM?bq?:O?>S$ ?J?B.gUK?m\b??zE???!v]???????c??????h???s???g???`?qbi??:Zl?)?}1^??k?0??:$V?$?Ovs(}J??????2;gQ????Tg?K?`?h%c?hmGA?<!C*?9?~?t?)??,zA???S}?Q??.q?j????]";
JSONML.toJSONObject(input); JSONML.toJSONObject(input);
fail("Exception expected for invalid JSON."); fail("Exception expected for invalid JSON.");
@@ -833,4 +833,157 @@ public class JSONMLTest {
ex.getMessage()); ex.getMessage());
} }
} }
@Test
public void testToJSONArrayMaxNestingDepthOf42IsRespected() {
final String wayTooLongMalformedXML = new String(new char[6000]).replace("\0", "<a>");
final int maxNestingDepth = 42;
try {
JSONML.toJSONArray(wayTooLongMalformedXML, JSONMLParserConfiguration.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 testToJSONArrayMaxNestingDepthIsRespectedWithValidXML() {
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 {
JSONML.toJSONArray(perfectlyFineXML, JSONMLParserConfiguration.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 testToJSONArrayMaxNestingDepthWithValidFittingXML() {
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 {
JSONML.toJSONArray(perfectlyFineXML, JSONMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
} catch (JSONException e) {
e.printStackTrace();
fail("XML document should be parsed as its maximum depth fits the maxNestingDepth " +
"parameter of the JSONMLParserConfiguration used");
}
}
@Test
public void testToJSONObjectMaxDefaultNestingDepthIsRespected() {
final String wayTooLongMalformedXML = new String(new char[6000]).replace("\0", "<a>");
try {
JSONML.toJSONObject(wayTooLongMalformedXML, JSONMLParserConfiguration.ORIGINAL);
fail("Expecting a JSONException");
} catch (JSONException e) {
assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">",
e.getMessage().startsWith("Maximum nesting depth of " + JSONMLParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH));
}
}
@Test
public void testToJSONObjectUnlimitedNestingDepthIsPossible() {
int actualDepth = JSONMLParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH +10;
final String deeperThanDefaultMax = new String(new char[actualDepth]).replace("\0", "<a>") +
"value" +
new String(new char[actualDepth]).replace("\0", "</a>");
try {
JSONML.toJSONObject(deeperThanDefaultMax, JSONMLParserConfiguration.ORIGINAL
.withMaxNestingDepth(JSONMLParserConfiguration.UNDEFINED_MAXIMUM_NESTING_DEPTH));
} catch (JSONException e) {
e.printStackTrace();
fail("XML document should be parsed beyond the default maximum depth if maxNestingDepth " +
"parameter is set to -1 in JSONMLParserConfiguration");
}
}
@Test
public void testToJSONObjectMaxNestingDepthOf42IsRespected() {
final String wayTooLongMalformedXML = new String(new char[6000]).replace("\0", "<a>");
final int maxNestingDepth = 42;
try {
JSONML.toJSONObject(wayTooLongMalformedXML, JSONMLParserConfiguration.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 testToJSONObjectMaxNestingDepthIsRespectedWithValidXML() {
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 {
JSONML.toJSONObject(perfectlyFineXML, JSONMLParserConfiguration.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 testToJSONObjectMaxNestingDepthWithValidFittingXML() {
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 {
JSONML.toJSONObject(perfectlyFineXML, JSONMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
} catch (JSONException e) {
e.printStackTrace();
fail("XML document should be parsed as its maximum depth fits the maxNestingDepth " +
"parameter of the JSONMLParserConfiguration used");
}
}
} }

View File

@@ -72,8 +72,10 @@ public class JSONPointerTest {
@Test @Test
public void queryByEmptyKeySubObject() { public void queryByEmptyKeySubObject() {
assertEquals( "{\"\":\"empty key of an object with an empty key\",\"subKey\":\"Some" + JSONObject json = new JSONObject("{\"\":\"empty key of an object with an empty key\",\"subKey\":\"Some" +
" other value\"}", query("/obj/").toString()); " other value\"}");
JSONObject obj = (JSONObject) query("/obj/");
assertTrue(json.similar(obj));
} }
@Test @Test

View File

@@ -313,4 +313,16 @@ public class JSONTokenerTest {
assertEquals(0, t2.next()); assertEquals(0, t2.next());
assertFalse(t2.more()); assertFalse(t2.more());
} }
@Test
public void testAutoClose(){
Reader reader = new StringReader("some test string");
try {
JSONTokener tokener = new JSONTokener(reader);
tokener.close();
tokener.next();
} catch (Exception exception){
assertEquals("Stream closed", exception.getMessage());
}
}
} }

View File

@@ -1051,6 +1051,29 @@ public class XMLConfigurationTest {
Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject);
} }
@Test
public void testMaxNestingDepthIsSet() {
XMLParserConfiguration xmlParserConfiguration = XMLParserConfiguration.ORIGINAL;
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), XMLParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(42);
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), 42);
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(0);
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), 0);
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(-31415926);
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), XMLParserConfiguration.UNDEFINED_MAXIMUM_NESTING_DEPTH);
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(Integer.MIN_VALUE);
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), XMLParserConfiguration.UNDEFINED_MAXIMUM_NESTING_DEPTH);
}
/** /**
* Convenience method, given an input string and expected result, * Convenience method, given an input string and expected result,

View File

@@ -1111,6 +1111,7 @@ public class XMLTest {
"}" ; "}" ;
JSONObject jsonObject = new JSONObject(str); JSONObject jsonObject = new JSONObject(str);
String actualIndentedXmlString = XML.toString(jsonObject, 1); String actualIndentedXmlString = XML.toString(jsonObject, 1);
JSONObject actualJsonObject = XML.toJSONObject(actualIndentedXmlString);
String expected = "<success>true</success>\n" + String expected = "<success>true</success>\n" +
"<response>\n" + "<response>\n" +
" <dateTimeISO>2022-10-05T00:00:00+03:00</dateTimeISO>\n" + " <dateTimeISO>2022-10-05T00:00:00+03:00</dateTimeISO>\n" +
@@ -1170,7 +1171,8 @@ public class XMLTest {
" <timestamp>1664917200</timestamp>\n" + " <timestamp>1664917200</timestamp>\n" +
"</response>\n" + "</response>\n" +
"<error>null</error>\n"; "<error>null</error>\n";
assertEquals(actualIndentedXmlString, expected); JSONObject expectedJsonObject = XML.toJSONObject(expected);
assertTrue(expectedJsonObject.similar(actualJsonObject));
} }
@@ -1183,6 +1185,7 @@ public class XMLTest {
" }}"; " }}";
JSONObject jsonObject = new JSONObject(str); JSONObject jsonObject = new JSONObject(str);
String actual = XML.toString(jsonObject, "Test", 2); String actual = XML.toString(jsonObject, "Test", 2);
JSONObject actualJsonObject = XML.toJSONObject(actual);
String expected = "<Test>\n" + String expected = "<Test>\n" +
" <employee>\n" + " <employee>\n" +
" <name>sonoo</name>\n" + " <name>sonoo</name>\n" +
@@ -1190,7 +1193,8 @@ public class XMLTest {
" <married>true</married>\n" + " <married>true</married>\n" +
" </employee>\n" + " </employee>\n" +
"</Test>\n"; "</Test>\n";
assertEquals(actual, expected); JSONObject expectedJsonObject = XML.toJSONObject(expected);
assertTrue(expectedJsonObject.similar(actualJsonObject));
} }
@Test @Test
@@ -1201,6 +1205,7 @@ public class XMLTest {
"] "; "] ";
JSONArray jsonObject = new JSONArray(str); JSONArray jsonObject = new JSONArray(str);
String actual = XML.toString(jsonObject, 2); String actual = XML.toString(jsonObject, 2);
JSONObject actualJsonObject = XML.toJSONObject(actual);
String expected = "<array>\n" + String expected = "<array>\n" +
" <name>Ram</name>\n" + " <name>Ram</name>\n" +
" <email>Ram@gmail.com</email>\n" + " <email>Ram@gmail.com</email>\n" +
@@ -1209,7 +1214,8 @@ public class XMLTest {
" <name>Bob</name>\n" + " <name>Bob</name>\n" +
" <email>bob32@gmail.com</email>\n" + " <email>bob32@gmail.com</email>\n" +
"</array>\n"; "</array>\n";
assertEquals(actual, expected); JSONObject expectedJsonObject = XML.toJSONObject(expected);
assertTrue(expectedJsonObject.similar(actualJsonObject));
} }
@@ -1247,6 +1253,65 @@ public class XMLTest {
fail("file writer error: " +e.getMessage()); 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");
}
}
} }