diff --git a/XML.java b/XML.java index 73090fc..f660d23 100644 --- a/XML.java +++ b/XML.java @@ -31,7 +31,7 @@ import java.util.Iterator; /** * This provides static methods to convert an XML text into a JSONObject, and to * covert a JSONObject into an XML text. - * + * * @author JSON.org * @version 2016-08-10 */ @@ -64,7 +64,12 @@ public class XML { /** The Character '/'. */ public static final Character SLASH = '/'; - + + /** + * Null attrubute name + */ + public static final String NULL_ATTR = "xsi:nil"; + /** * Creates an iterator for navigating Code Points in a string instead of * characters. Once Java7 support is dropped, this can be replaced with @@ -72,7 +77,7 @@ public class XML { * string.codePoints() * * which is available in Java8 and above. - * + * * @see http://stackoverflow.com/a/21791059/6030888 */ @@ -107,7 +112,7 @@ public class XML { /** * Replace special characters with XML escapes: - * + * *
* & (ampersand) is replaced by & * < (less than) is replaced by < @@ -115,7 +120,7 @@ public class XML { * " (double quote) is replaced by " * ' (single quote / apostrophe) is replaced by ' *- * + * * @param string * The string to be escaped. * @return The escaped string. @@ -151,17 +156,17 @@ public class XML { } return sb.toString(); } - + /** * @param cp code point to test * @return true if the code point is not valid for an XML */ private static boolean mustEscape(int cp) { /* Valid range from https://www.w3.org/TR/REC-xml/#charsets - * - * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] - * - * any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. + * + * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + * + * any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */ // isISOControl is true when (cp >= 0 && cp <= 0x1F) || (cp >= 0x7F && cp <= 0x9F) // all ISO control characters are out of range except tabs and new lines @@ -180,7 +185,7 @@ public class XML { /** * Removes XML escapes from the string. - * + * * @param string * string to remove escapes from * @return string with converted entities @@ -212,7 +217,7 @@ public class XML { /** * Throw an exception if the string contains whitespace. Whitespace is not * allowed in tagNames and attributes. - * + * * @param string * A string. * @throws JSONException Thrown if the string contains whitespace or is empty. @@ -232,7 +237,7 @@ public class XML { /** * Scan the content following the named tag, attaching it to the context. - * + * * @param x * The XMLTokener containing the source string. * @param context @@ -328,6 +333,7 @@ public class XML { tagName = (String) token; token = null; jsonobject = new JSONObject(); + boolean nilAttributeFound = false; for (;;) { if (token == null) { token = x.nextToken(); @@ -341,8 +347,17 @@ public class XML { if (!(token instanceof String)) { throw x.syntaxError("Missing value"); } - jsonobject.accumulate(string, - config.keepStrings ? ((String)token) : stringToValue((String) token)); + + if (config.convertNilAttributeToNull + && NULL_ATTR.equals(string) + && Boolean.parseBoolean((String) token)) { + nilAttributeFound = true; + } else if (!nilAttributeFound) { + jsonobject.accumulate(string, + config.keepStrings + ? ((String) token) + : stringToValue((String) token)); + } token = null; } else { jsonobject.accumulate(string, ""); @@ -354,7 +369,9 @@ public class XML { if (x.nextToken() != GT) { throw x.syntaxError("Misshaped tag"); } - if (jsonobject.length() > 0) { + if (nilAttributeFound) { + context.accumulate(tagName, JSONObject.NULL); + } else if (jsonobject.length() > 0) { context.accumulate(tagName, jsonobject); } else { context.accumulate(tagName, ""); @@ -399,10 +416,10 @@ public class XML { } } } - + /** * This method is the same as {@link JSONObject#stringToValue(String)}. - * + * * @param string String to convert * @return JSON value of this string or the string */ @@ -463,7 +480,7 @@ public class XML { * elements are represented as JSONArrays. Content text may be placed in a * "content" member. Comments, prologs, DTDs, and
<[ [ ]]>
* are ignored.
- *
+ *
* @param string
* The source string.
* @return A JSONObject containing the structured data from the XML string.
@@ -518,7 +535,7 @@ public class XML {
}
return toJSONObject(reader, XMLParserConfiguration.ORIGINAL);
}
-
+
/**
* Convert a well-formed (but not necessarily valid) XML into a
* JSONObject. Some information may be lost in this transformation because
@@ -560,10 +577,10 @@ public class XML {
* elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <[ [ ]]>
* are ignored.
- *
+ *
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to
* numbers but will instead be the exact value as seen in the XML document.
- *
+ *
* @param string
* The source string.
* @param keepStrings If true, then values will not be coerced into boolean
@@ -585,10 +602,10 @@ public class XML {
* elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <[ [ ]]>
* are ignored.
- *
+ *
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to
* numbers but will instead be the exact value as seen in the XML document.
- *
+ *
* @param string
* The source string.
* @param config Configuration options for the parser.
@@ -601,7 +618,7 @@ public class XML {
/**
* Convert a JSONObject into a well-formed, element-normal XML string.
- *
+ *
* @param object
* A JSONObject.
* @return A string.
@@ -610,10 +627,10 @@ public class XML {
public static String toString(Object object) throws JSONException {
return toString(object, null, XMLParserConfiguration.ORIGINAL);
}
-
+
/**
* Convert a JSONObject into a well-formed, element-normal XML string.
- *
+ *
* @param object
* A JSONObject.
* @param tagName
@@ -627,7 +644,7 @@ public class XML {
/**
* Convert a JSONObject into a well-formed, element-normal XML string.
- *
+ *
* @param object
* A JSONObject.
* @param tagName
diff --git a/XMLParserConfiguration.java b/XMLParserConfiguration.java
index 45af175..c0186f7 100644
--- a/XMLParserConfiguration.java
+++ b/XMLParserConfiguration.java
@@ -32,7 +32,7 @@ public class XMLParserConfiguration {
/** Original Configuration of the XML Parser. */
public static final XMLParserConfiguration ORIGINAL = new XMLParserConfiguration();
/** Original configuration of the XML Parser except that values are kept as strings. */
- public static final XMLParserConfiguration KEEP_STRINGS = new XMLParserConfiguration(true);
+ public static final XMLParserConfiguration KEEP_STRINGS = new XMLParserConfiguration(true);
/**
* When parsing the XML into JSON, specifies if values should be kept as strings (true), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
@@ -44,12 +44,17 @@ public class XMLParserConfiguration {
* processing.
*/
public final String cDataTagName;
+ /**
+ * When parsing the XML into JSON, specifies if values with attribute xsi:nil="true"
+ * should be kept as attribute(false), or they should be converted to null(true)
+ */
+ public final boolean convertNilAttributeToNull;
/**
* Default parser configuration. Does not keep strings, and the CDATA Tag Name is "content".
*/
public XMLParserConfiguration () {
- this(false, "content");
+ this(false, "content", false);
}
/**
@@ -58,7 +63,7 @@ public class XMLParserConfiguration {
* false
to try and convert XML string values into a JSON value.
*/
public XMLParserConfiguration (final boolean keepStrings) {
- this(keepStrings, "content");
+ this(keepStrings, "content", false);
}
/**
@@ -69,7 +74,7 @@ public class XMLParserConfiguration {
* to use that value as the JSONObject key name to process as CDATA.
*/
public XMLParserConfiguration (final String cDataTagName) {
- this(false, cDataTagName);
+ this(false, cDataTagName, false);
}
/**
@@ -80,7 +85,23 @@ public class XMLParserConfiguration {
* to use that value as the JSONObject key name to process as CDATA.
*/
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName) {
- this.keepStrings = keepStrings;
- this.cDataTagName = cDataTagName;
+ this.keepStrings = keepStrings;
+ this.cDataTagName = cDataTagName;
+ this.convertNilAttributeToNull = false;
+ }
+
+ /**
+ * Configure the parser to use custom settings.
+ * @param keepStrings true
to parse all values as string.
+ * false
to try and convert XML string values into a JSON value.
+ * @param cDataTagName null
to disable CDATA processing. Any other value
+ * to use that value as the JSONObject key name to process as CDATA.
+ * @param convertNilAttributeToNull true
to parse values with attribute xsi:nil="true" as null.
+ * false
to parse values with attribute xsi:nil="true" as {"xsi:nil":true}.
+ */
+ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) {
+ this.keepStrings = keepStrings;
+ this.cDataTagName = cDataTagName;
+ this.convertNilAttributeToNull = convertNilAttributeToNull;
}
}