mirror of
https://github.com/stleary/JSON-java.git
synced 2025-08-03 11:25:30 -04:00
Merge pull request #723 from TamasPergerDWP/master
JSONML should be protected from stack overflow exceptions caused by recursion, resolving #722
This commit is contained in:
commit
1275f6809d
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -246,7 +276,56 @@ 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 <[ [ ]]>}</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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* Comments, prologs, DTDs, and <pre>{@code <[ [ ]]>}</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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -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,7 +382,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);
|
return (JSONObject)parse(new XMLTokener(string), false, null, false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -323,7 +402,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(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 <[ [ ]]>}</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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -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 <[ [ ]]>}</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,
|
||||||
|
128
src/main/java/org/json/JSONMLParserConfiguration.java
Normal file
128
src/main/java/org/json/JSONMLParserConfiguration.java
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package org.json;
|
||||||
|
/*
|
||||||
|
Public Domain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration object for the XML to JSONML parser. The configuration is immutable.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({""})
|
||||||
|
public class JSONMLParserConfiguration {
|
||||||
|
/**
|
||||||
|
* Used to indicate there's no defined limit to the maximum nesting depth when parsing a XML
|
||||||
|
* document to JSONML.
|
||||||
|
*/
|
||||||
|
public static final int UNDEFINED_MAXIMUM_NESTING_DEPTH = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default maximum nesting depth when parsing a XML document to JSONML.
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512;
|
||||||
|
|
||||||
|
/** 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
*/
|
||||||
|
private boolean keepStrings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum nesting depth when parsing a XML document to JSONML.
|
||||||
|
*/
|
||||||
|
private int maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default parser configuration. Does not keep strings (tries to implicitly convert values).
|
||||||
|
*/
|
||||||
|
public JSONMLParserConfiguration() {
|
||||||
|
this.keepStrings = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
private JSONMLParserConfiguration(final boolean keepStrings, final int maxNestingDepth) {
|
||||||
|
this.keepStrings = keepStrings;
|
||||||
|
this.maxNestingDepth = 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 JSONMLParserConfiguration withKeepStrings(final boolean newVal) {
|
||||||
|
JSONMLParserConfiguration newConfig = 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 JSONMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) {
|
||||||
|
JSONMLParserConfiguration newConfig = this.clone();
|
||||||
|
|
||||||
|
if (maxNestingDepth > UNDEFINED_MAXIMUM_NESTING_DEPTH) {
|
||||||
|
newConfig.maxNestingDepth = maxNestingDepth;
|
||||||
|
} else {
|
||||||
|
newConfig.maxNestingDepth = UNDEFINED_MAXIMUM_NESTING_DEPTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newConfig;
|
||||||
|
}
|
||||||
|
}
|
@ -229,8 +229,12 @@ 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, int currentNestingDepth)
|
private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, int currentNestingDepth)
|
||||||
throws JSONException {
|
throws JSONException {
|
||||||
|
@ -66,8 +66,7 @@ public class XMLParserConfiguration {
|
|||||||
private Set<String> forceList;
|
private Set<String> forceList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When parsing the XML into JSON, specifies the tags whose values should be converted
|
* The maximum nesting depth when parsing a XML document to JSON.
|
||||||
* to arrays
|
|
||||||
*/
|
*/
|
||||||
private int maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH;
|
private int maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH;
|
||||||
|
|
||||||
@ -158,14 +157,17 @@ public class XMLParserConfiguration {
|
|||||||
* @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,
|
||||||
|
final int maxNestingDepth) {
|
||||||
this.keepStrings = keepStrings;
|
this.keepStrings = keepStrings;
|
||||||
this.cDataTagName = cDataTagName;
|
this.cDataTagName = cDataTagName;
|
||||||
this.convertNilAttributeToNull = convertNilAttributeToNull;
|
this.convertNilAttributeToNull = convertNilAttributeToNull;
|
||||||
this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap);
|
this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap);
|
||||||
this.forceList = Collections.unmodifiableSet(forceList);
|
this.forceList = Collections.unmodifiableSet(forceList);
|
||||||
|
this.maxNestingDepth = maxNestingDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -183,7 +185,8 @@ public class XMLParserConfiguration {
|
|||||||
this.cDataTagName,
|
this.cDataTagName,
|
||||||
this.convertNilAttributeToNull,
|
this.convertNilAttributeToNull,
|
||||||
this.xsiTypeMap,
|
this.xsiTypeMap,
|
||||||
this.forceList
|
this.forceList,
|
||||||
|
this.maxNestingDepth
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,8 +330,9 @@ public class XMLParserConfiguration {
|
|||||||
/**
|
/**
|
||||||
* Defines the maximum nesting depth that the parser will descend before throwing an exception
|
* 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
|
* when parsing the XML into JSON. The default max nesting depth is 512, which means the parser
|
||||||
* will go as deep as the maximum call stack size allows. Using any negative value as a
|
* will throw a JsonException if the maximum depth is reached.
|
||||||
* parameter is equivalent to setting no limit to the nesting depth.
|
* 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
|
* @param maxNestingDepth the maximum nesting depth allowed to the XML parser
|
||||||
* @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.
|
||||||
*/
|
*/
|
||||||
|
@ -833,4 +833,128 @@ 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 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user