@@ -31,7 +31,7 @@ of this software and associated documentation files (the "Software"), to deal
3131/**
3232 * This provides static methods to convert an XML text into a JSONObject, and to
3333 * covert a JSONObject into an XML text.
34- *
34+ *
3535 * @author JSON.org
3636 * @version 2016-08-10
3737 */
@@ -64,15 +64,20 @@ public class XML {
6464
6565 /** The Character '/'. */
6666 public static final Character SLASH = '/' ;
67-
67+
68+ /**
69+ * Null attrubute name
70+ */
71+ public static final String NULL_ATTR = "xsi:nil" ;
72+
6873 /**
6974 * Creates an iterator for navigating Code Points in a string instead of
7075 * characters. Once Java7 support is dropped, this can be replaced with
7176 * <code>
7277 * string.codePoints()
7378 * </code>
7479 * which is available in Java8 and above.
75- *
80+ *
7681 * @see <a href=
7782 * "http://stackoverflow.com/a/21791059/6030888">http://stackoverflow.com/a/21791059/6030888</a>
7883 */
@@ -107,15 +112,15 @@ public void remove() {
107112
108113 /**
109114 * Replace special characters with XML escapes:
110- *
115+ *
111116 * <pre>
112117 * & <small>(ampersand)</small> is replaced by &amp;
113118 * < <small>(less than)</small> is replaced by &lt;
114119 * > <small>(greater than)</small> is replaced by &gt;
115120 * " <small>(double quote)</small> is replaced by &quot;
116121 * ' <small>(single quote / apostrophe)</small> is replaced by &apos;
117122 * </pre>
118- *
123+ *
119124 * @param string
120125 * The string to be escaped.
121126 * @return The escaped string.
@@ -151,17 +156,17 @@ public static String escape(String string) {
151156 }
152157 return sb .toString ();
153158 }
154-
159+
155160 /**
156161 * @param cp code point to test
157162 * @return true if the code point is not valid for an XML
158163 */
159164 private static boolean mustEscape (int cp ) {
160165 /* Valid range from https://www.w3.org/TR/REC-xml/#charsets
161- *
162- * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
163- *
164- * any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
166+ *
167+ * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
168+ *
169+ * any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
165170 */
166171 // isISOControl is true when (cp >= 0 && cp <= 0x1F) || (cp >= 0x7F && cp <= 0x9F)
167172 // all ISO control characters are out of range except tabs and new lines
@@ -180,7 +185,7 @@ private static boolean mustEscape(int cp) {
180185
181186 /**
182187 * Removes XML escapes from the string.
183- *
188+ *
184189 * @param string
185190 * string to remove escapes from
186191 * @return string with converted entities
@@ -212,7 +217,7 @@ public static String unescape(String string) {
212217 /**
213218 * Throw an exception if the string contains whitespace. Whitespace is not
214219 * allowed in tagNames and attributes.
215- *
220+ *
216221 * @param string
217222 * A string.
218223 * @throws JSONException Thrown if the string contains whitespace or is empty.
@@ -232,7 +237,7 @@ public static void noSpace(String string) throws JSONException {
232237
233238 /**
234239 * Scan the content following the named tag, attaching it to the context.
235- *
240+ *
236241 * @param x
237242 * The XMLTokener containing the source string.
238243 * @param context
@@ -328,6 +333,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
328333 tagName = (String ) token ;
329334 token = null ;
330335 jsonobject = new JSONObject ();
336+ boolean nilAttributeFound = false ;
331337 for (;;) {
332338 if (token == null ) {
333339 token = x .nextToken ();
@@ -341,8 +347,17 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
341347 if (!(token instanceof String )) {
342348 throw x .syntaxError ("Missing value" );
343349 }
344- jsonobject .accumulate (string ,
345- config .keepStrings ? ((String )token ) : stringToValue ((String ) token ));
350+
351+ if (config .convertNilAttributeToNull
352+ && NULL_ATTR .equals (string )
353+ && Boolean .parseBoolean ((String ) token )) {
354+ nilAttributeFound = true ;
355+ } else if (!nilAttributeFound ) {
356+ jsonobject .accumulate (string ,
357+ config .keepStrings
358+ ? ((String ) token )
359+ : stringToValue ((String ) token ));
360+ }
346361 token = null ;
347362 } else {
348363 jsonobject .accumulate (string , "" );
@@ -354,7 +369,9 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
354369 if (x .nextToken () != GT ) {
355370 throw x .syntaxError ("Misshaped tag" );
356371 }
357- if (jsonobject .length () > 0 ) {
372+ if (nilAttributeFound ) {
373+ context .accumulate (tagName , JSONObject .NULL );
374+ } else if (jsonobject .length () > 0 ) {
358375 context .accumulate (tagName , jsonobject );
359376 } else {
360377 context .accumulate (tagName , "" );
@@ -399,10 +416,10 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
399416 }
400417 }
401418 }
402-
419+
403420 /**
404421 * This method is the same as {@link JSONObject#stringToValue(String)}.
405- *
422+ *
406423 * @param string String to convert
407424 * @return JSON value of this string or the string
408425 */
@@ -463,7 +480,7 @@ public static Object stringToValue(String string) {
463480 * elements are represented as JSONArrays. Content text may be placed in a
464481 * "content" member. Comments, prologs, DTDs, and <code><[ [ ]]></code>
465482 * are ignored.
466- *
483+ *
467484 * @param string
468485 * The source string.
469486 * @return A JSONObject containing the structured data from the XML string.
@@ -518,7 +535,7 @@ public static JSONObject toJSONObject(Reader reader, boolean keepStrings) throws
518535 }
519536 return toJSONObject (reader , XMLParserConfiguration .ORIGINAL );
520537 }
521-
538+
522539 /**
523540 * Convert a well-formed (but not necessarily valid) XML into a
524541 * JSONObject. Some information may be lost in this transformation because
@@ -560,10 +577,10 @@ public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration conf
560577 * elements are represented as JSONArrays. Content text may be placed in a
561578 * "content" member. Comments, prologs, DTDs, and <code><[ [ ]]></code>
562579 * are ignored.
563- *
580+ *
564581 * All values are converted as strings, for 1, 01, 29.0 will not be coerced to
565582 * numbers but will instead be the exact value as seen in the XML document.
566- *
583+ *
567584 * @param string
568585 * The source string.
569586 * @param keepStrings If true, then values will not be coerced into boolean
@@ -585,10 +602,10 @@ public static JSONObject toJSONObject(String string, boolean keepStrings) throws
585602 * elements are represented as JSONArrays. Content text may be placed in a
586603 * "content" member. Comments, prologs, DTDs, and <code><[ [ ]]></code>
587604 * are ignored.
588- *
605+ *
589606 * All values are converted as strings, for 1, 01, 29.0 will not be coerced to
590607 * numbers but will instead be the exact value as seen in the XML document.
591- *
608+ *
592609 * @param string
593610 * The source string.
594611 * @param config Configuration options for the parser.
@@ -601,7 +618,7 @@ public static JSONObject toJSONObject(String string, XMLParserConfiguration conf
601618
602619 /**
603620 * Convert a JSONObject into a well-formed, element-normal XML string.
604- *
621+ *
605622 * @param object
606623 * A JSONObject.
607624 * @return A string.
@@ -610,10 +627,10 @@ public static JSONObject toJSONObject(String string, XMLParserConfiguration conf
610627 public static String toString (Object object ) throws JSONException {
611628 return toString (object , null , XMLParserConfiguration .ORIGINAL );
612629 }
613-
630+
614631 /**
615632 * Convert a JSONObject into a well-formed, element-normal XML string.
616- *
633+ *
617634 * @param object
618635 * A JSONObject.
619636 * @param tagName
@@ -627,7 +644,7 @@ public static String toString(final Object object, final String tagName) {
627644
628645 /**
629646 * Convert a JSONObject into a well-formed, element-normal XML string.
630- *
647+ *
631648 * @param object
632649 * A JSONObject.
633650 * @param tagName
0 commit comments