4 Copyright (c) 2008 JSON.org
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
16 The Software shall be used for Good, not Evil.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 * This provides static methods to convert an XML text into a JSONArray or
29 * JSONObject, and to covert a JSONArray or JSONObject into an XML text using
30 * the JsonML transform.
38 * Parse XML values and store them in a JSONArray.
41 * The XMLTokener containing the source string.
43 * true if array form, false if object form.
45 * The JSONArray that is containing the current tag or null if we are
46 * at the outermost level.
48 * Don't type-convert text nodes and attribute values
49 * @return A JSONArray if the value is the outermost tag, otherwise null.
50 * @throws JSONException
52 private static Object parse(XMLTokener x, boolean arrayForm, JSONArray ja,
53 boolean keepStrings) throws JSONException
57 String closeTag = null;
59 JSONArray newja = null;
60 JSONObject newjo = null;
62 String tagName = null;
64 // Test for and skip past these forms:
74 throw x.syntaxError("Bad XML");
76 token = x.nextContent();
79 token = x.nextToken();
80 if (token instanceof Character)
82 if (token == XML.SLASH)
87 token = x.nextToken();
88 if (!(token instanceof String))
90 throw new JSONException("Expected a closing name instead of '"
93 if (x.nextToken() != XML.GT)
95 throw x.syntaxError("Misshaped close tag");
99 else if (token == XML.BANG)
118 token = x.nextToken();
119 if (token.equals("CDATA") && x.next() == '[')
123 ja.put(x.nextCDATA());
128 throw x.syntaxError("Expected 'CDATA['");
136 token = x.nextMeta();
139 throw x.syntaxError("Missing '>' after '<!'.");
141 else if (token == XML.LT)
145 else if (token == XML.GT)
152 else if (token == XML.QUEST)
161 throw x.syntaxError("Misshaped tag");
169 if (!(token instanceof String))
171 throw x.syntaxError("Bad tagName '" + token + "'.");
173 tagName = (String) token;
174 newja = new JSONArray();
175 newjo = new JSONObject();
186 newjo.put("tagName", tagName);
197 token = x.nextToken();
201 throw x.syntaxError("Misshaped tag");
203 if (!(token instanceof String))
210 attribute = (String) token;
211 if (!arrayForm && ("tagName".equals(attribute)
212 || "childNode".equals(attribute)))
214 throw x.syntaxError("Reserved attribute.");
216 token = x.nextToken();
219 token = x.nextToken();
220 if (!(token instanceof String))
222 throw x.syntaxError("Missing value");
224 newjo.accumulate(attribute, keepStrings ? ((String) token)
225 : XML.stringToValue((String) token));
230 newjo.accumulate(attribute, "");
233 if (arrayForm && newjo.length() > 0)
240 if (token == XML.SLASH)
242 if (x.nextToken() != XML.GT)
244 throw x.syntaxError("Misshaped tag");
255 // Content, between <...> and </...>
262 throw x.syntaxError("Misshaped tag");
264 closeTag = (String) parse(x, arrayForm, newja, keepStrings);
265 if (closeTag != null)
267 if (!closeTag.equals(tagName))
269 throw x.syntaxError("Mismatched '" + tagName + "' and '"
273 if (!arrayForm && newja.length() > 0)
275 newjo.put("childNodes", newja);
293 ja.put(token instanceof String
294 ? keepStrings ? XML.unescape((String) token)
295 : XML.stringToValue((String) token)
303 * Convert a well-formed (but not necessarily valid) XML string into a
304 * JSONArray using the JsonML transform. Each XML tag is represented as a
305 * JSONArray in which the first element is the tag name. If the tag has
306 * attributes, then the second element will be JSONObject containing the
307 * name/value pairs. If the tag contains children, then strings and JSONArrays
308 * will represent the child tags. Comments, prologs, DTDs, and
309 * <code><[ [ ]]></code> are ignored.
313 * @return A JSONArray containing the structured data from the XML string.
314 * @throws JSONException
315 * Thrown on error converting to a JSONArray
317 public static JSONArray toJSONArray(String string) throws JSONException
319 return (JSONArray) parse(new XMLTokener(string), true, null, false);
323 * Convert a well-formed (but not necessarily valid) XML string into a
324 * JSONArray using the JsonML transform. Each XML tag is represented as a
325 * JSONArray in which the first element is the tag name. If the tag has
326 * attributes, then the second element will be JSONObject containing the
327 * name/value pairs. If the tag contains children, then strings and JSONArrays
328 * will represent the child tags. As opposed to toJSONArray this method does
329 * not attempt to convert any text node or attribute value to any type but
330 * just leaves it as a string. Comments, prologs, DTDs, and
331 * <code><[ [ ]]></code> are ignored.
336 * If true, then values will not be coerced into boolean or numeric
337 * values and will instead be left as strings
338 * @return A JSONArray containing the structured data from the XML string.
339 * @throws JSONException
340 * Thrown on error converting to a JSONArray
342 public static JSONArray toJSONArray(String string, boolean keepStrings)
345 return (JSONArray) parse(new XMLTokener(string), true, null,
350 * Convert a well-formed (but not necessarily valid) XML string into a
351 * JSONArray using the JsonML transform. Each XML tag is represented as a
352 * JSONArray in which the first element is the tag name. If the tag has
353 * attributes, then the second element will be JSONObject containing the
354 * name/value pairs. If the tag contains children, then strings and JSONArrays
355 * will represent the child content and tags. As opposed to toJSONArray this
356 * method does not attempt to convert any text node or attribute value to any
357 * type but just leaves it as a string. Comments, prologs, DTDs, and
358 * <code><[ [ ]]></code> are ignored.
363 * If true, then values will not be coerced into boolean or numeric
364 * values and will instead be left as strings
365 * @return A JSONArray containing the structured data from the XML string.
366 * @throws JSONException
367 * Thrown on error converting to a JSONArray
369 public static JSONArray toJSONArray(XMLTokener x, boolean keepStrings)
372 return (JSONArray) parse(x, true, null, keepStrings);
376 * Convert a well-formed (but not necessarily valid) XML string into a
377 * JSONArray using the JsonML transform. Each XML tag is represented as a
378 * JSONArray in which the first element is the tag name. If the tag has
379 * attributes, then the second element will be JSONObject containing the
380 * name/value pairs. If the tag contains children, then strings and JSONArrays
381 * will represent the child content and tags. Comments, prologs, DTDs, and
382 * <code><[ [ ]]></code> are ignored.
386 * @return A JSONArray containing the structured data from the XML string.
387 * @throws JSONException
388 * Thrown on error converting to a JSONArray
390 public static JSONArray toJSONArray(XMLTokener x) throws JSONException
392 return (JSONArray) parse(x, true, null, false);
396 * Convert a well-formed (but not necessarily valid) XML string into a
397 * JSONObject using the JsonML transform. Each XML tag is represented as a
398 * JSONObject with a "tagName" property. If the tag has attributes, then the
399 * attributes will be in the JSONObject as properties. If the tag contains
400 * children, the object will have a "childNodes" property which will be an
401 * array of strings and JsonML JSONObjects.
403 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
406 * The XML source text.
407 * @return A JSONObject containing the structured data from the XML string.
408 * @throws JSONException
409 * Thrown on error converting to a JSONObject
411 public static JSONObject toJSONObject(String string) throws JSONException
413 return (JSONObject) parse(new XMLTokener(string), false, null, false);
417 * Convert a well-formed (but not necessarily valid) XML string into a
418 * JSONObject using the JsonML transform. Each XML tag is represented as a
419 * JSONObject with a "tagName" property. If the tag has attributes, then the
420 * attributes will be in the JSONObject as properties. If the tag contains
421 * children, the object will have a "childNodes" property which will be an
422 * array of strings and JsonML JSONObjects.
424 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
427 * The XML source text.
429 * If true, then values will not be coerced into boolean or numeric
430 * values and will instead be left as strings
431 * @return A JSONObject containing the structured data from the XML string.
432 * @throws JSONException
433 * Thrown on error converting to a JSONObject
435 public static JSONObject toJSONObject(String string, boolean keepStrings)
438 return (JSONObject) parse(new XMLTokener(string), false, null,
443 * Convert a well-formed (but not necessarily valid) XML string into a
444 * JSONObject using the JsonML transform. Each XML tag is represented as a
445 * JSONObject with a "tagName" property. If the tag has attributes, then the
446 * attributes will be in the JSONObject as properties. If the tag contains
447 * children, the object will have a "childNodes" property which will be an
448 * array of strings and JsonML JSONObjects.
450 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
453 * An XMLTokener of the XML source text.
454 * @return A JSONObject containing the structured data from the XML string.
455 * @throws JSONException
456 * Thrown on error converting to a JSONObject
458 public static JSONObject toJSONObject(XMLTokener x) throws JSONException
460 return (JSONObject) parse(x, false, null, false);
464 * Convert a well-formed (but not necessarily valid) XML string into a
465 * JSONObject using the JsonML transform. Each XML tag is represented as a
466 * JSONObject with a "tagName" property. If the tag has attributes, then the
467 * attributes will be in the JSONObject as properties. If the tag contains
468 * children, the object will have a "childNodes" property which will be an
469 * array of strings and JsonML JSONObjects.
471 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
474 * An XMLTokener of the XML source text.
476 * If true, then values will not be coerced into boolean or numeric
477 * values and will instead be left as strings
478 * @return A JSONObject containing the structured data from the XML string.
479 * @throws JSONException
480 * Thrown on error converting to a JSONObject
482 public static JSONObject toJSONObject(XMLTokener x, boolean keepStrings)
485 return (JSONObject) parse(x, false, null, keepStrings);
489 * Reverse the JSONML transformation, making an XML text from a JSONArray.
493 * @return An XML string.
494 * @throws JSONException
495 * Thrown on error converting to a string
497 public static String toString(JSONArray ja) throws JSONException
503 StringBuilder sb = new StringBuilder();
508 tagName = ja.getString(0);
509 XML.noSpace(tagName);
510 tagName = XML.escape(tagName);
515 if (object instanceof JSONObject)
518 jo = (JSONObject) object;
520 // Emit the attributes
522 // Don't use the new entrySet API to maintain Android support
523 for (final String key : jo.keySet())
525 final Object value = jo.opt(key);
530 sb.append(XML.escape(key));
533 sb.append(XML.escape(value.toString()));
543 // Emit content in body
545 length = ja.length();
560 if (object instanceof String)
562 sb.append(XML.escape(object.toString()));
564 else if (object instanceof JSONObject)
566 sb.append(toString((JSONObject) object));
568 else if (object instanceof JSONArray)
570 sb.append(toString((JSONArray) object));
574 sb.append(object.toString());
577 } while (i < length);
583 return sb.toString();
587 * Reverse the JSONML transformation, making an XML text from a JSONObject.
588 * The JSONObject must contain a "tagName" property. If it has children, then
589 * it must have a "childNodes" property containing an array of objects. The
590 * other properties are attributes with string values.
594 * @return An XML string.
595 * @throws JSONException
596 * Thrown on error converting to a string
598 public static String toString(JSONObject jo) throws JSONException
600 StringBuilder sb = new StringBuilder();
610 tagName = jo.optString("tagName");
613 return XML.escape(jo.toString());
615 XML.noSpace(tagName);
616 tagName = XML.escape(tagName);
620 // Emit the attributes
622 // Don't use the new entrySet API to maintain Android support
623 for (final String key : jo.keySet())
625 if (!"tagName".equals(key) && !"childNodes".equals(key))
632 sb.append(XML.escape(key));
635 sb.append(XML.escape(value.toString()));
641 // Emit content in body
643 ja = jo.optJSONArray("childNodes");
652 length = ja.length();
653 for (i = 0; i < length; i += 1)
658 if (object instanceof String)
660 sb.append(XML.escape(object.toString()));
662 else if (object instanceof JSONObject)
664 sb.append(toString((JSONObject) object));
666 else if (object instanceof JSONArray)
668 sb.append(toString((JSONArray) object));
672 sb.append(object.toString());
681 return sb.toString();