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.
37 * Parse XML values and store them in a JSONArray.
38 * @param x The XMLTokener containing the source string.
39 * @param arrayForm true if array form, false if object form.
40 * @param ja The JSONArray that is containing the current tag or null
41 * if we are at the outermost level.
42 * @param keepStrings Don't type-convert text nodes and attribute values
43 * @return A JSONArray if the value is the outermost tag, otherwise null.
44 * @throws JSONException
46 private static Object parse(
51 ) throws JSONException {
54 String closeTag = null;
56 JSONArray newja = null;
57 JSONObject newjo = null;
59 String tagName = null;
61 // Test for and skip past these forms:
69 throw x.syntaxError("Bad XML");
71 token = x.nextContent();
72 if (token == XML.LT) {
73 token = x.nextToken();
74 if (token instanceof Character) {
75 if (token == XML.SLASH) {
79 token = x.nextToken();
80 if (!(token instanceof String)) {
81 throw new JSONException(
82 "Expected a closing name instead of '" +
85 if (x.nextToken() != XML.GT) {
86 throw x.syntaxError("Misshaped close tag");
89 } else if (token == XML.BANG) {
95 if (x.next() == '-') {
100 } else if (c == '[') {
101 token = x.nextToken();
102 if (token.equals("CDATA") && x.next() == '[') {
104 ja.put(x.nextCDATA());
107 throw x.syntaxError("Expected 'CDATA['");
112 token = x.nextMeta();
114 throw x.syntaxError("Missing '>' after '<!'.");
115 } else if (token == XML.LT) {
117 } else if (token == XML.GT) {
122 } else if (token == XML.QUEST) {
128 throw x.syntaxError("Misshaped tag");
134 if (!(token instanceof String)) {
135 throw x.syntaxError("Bad tagName '" + token + "'.");
137 tagName = (String)token;
138 newja = new JSONArray();
139 newjo = new JSONObject();
146 newjo.put("tagName", tagName);
154 token = x.nextToken();
157 throw x.syntaxError("Misshaped tag");
159 if (!(token instanceof String)) {
165 attribute = (String)token;
166 if (!arrayForm && ("tagName".equals(attribute) || "childNode".equals(attribute))) {
167 throw x.syntaxError("Reserved attribute.");
169 token = x.nextToken();
170 if (token == XML.EQ) {
171 token = x.nextToken();
172 if (!(token instanceof String)) {
173 throw x.syntaxError("Missing value");
175 newjo.accumulate(attribute, keepStrings ? ((String)token) :XML.stringToValue((String)token));
178 newjo.accumulate(attribute, "");
181 if (arrayForm && newjo.length() > 0) {
187 if (token == XML.SLASH) {
188 if (x.nextToken() != XML.GT) {
189 throw x.syntaxError("Misshaped tag");
198 // Content, between <...> and </...>
201 if (token != XML.GT) {
202 throw x.syntaxError("Misshaped tag");
204 closeTag = (String)parse(x, arrayForm, newja, keepStrings);
205 if (closeTag != null) {
206 if (!closeTag.equals(tagName)) {
207 throw x.syntaxError("Mismatched '" + tagName +
208 "' and '" + closeTag + "'");
211 if (!arrayForm && newja.length() > 0) {
212 newjo.put("childNodes", newja);
225 ja.put(token instanceof String
226 ? keepStrings ? XML.unescape((String)token) :XML.stringToValue((String)token)
235 * Convert a well-formed (but not necessarily valid) XML string into a
236 * JSONArray using the JsonML transform. Each XML tag is represented as
237 * a JSONArray in which the first element is the tag name. If the tag has
238 * attributes, then the second element will be JSONObject containing the
239 * name/value pairs. If the tag contains children, then strings and
240 * JSONArrays will represent the child tags.
241 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
242 * @param string The source string.
243 * @return A JSONArray containing the structured data from the XML string.
244 * @throws JSONException Thrown on error converting to a JSONArray
246 public static JSONArray toJSONArray(String string) throws JSONException {
247 return (JSONArray)parse(new XMLTokener(string), true, null, false);
252 * Convert a well-formed (but not necessarily valid) XML string into a
253 * JSONArray using the JsonML transform. Each XML tag is represented as
254 * a JSONArray in which the first element is the tag name. If the tag has
255 * attributes, then the second element will be JSONObject containing the
256 * name/value pairs. If the tag contains children, then strings and
257 * JSONArrays will represent the child tags.
258 * As opposed to toJSONArray this method does not attempt to convert
259 * any text node or attribute value to any type
260 * but just leaves it as a string.
261 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
262 * @param string The source string.
263 * @param keepStrings If true, then values will not be coerced into boolean
264 * or numeric values and will instead be left as strings
265 * @return A JSONArray containing the structured data from the XML string.
266 * @throws JSONException Thrown on error converting to a JSONArray
268 public static JSONArray toJSONArray(String string, boolean keepStrings) throws JSONException {
269 return (JSONArray)parse(new XMLTokener(string), true, null, keepStrings);
274 * Convert a well-formed (but not necessarily valid) XML string into a
275 * JSONArray using the JsonML transform. Each XML tag is represented as
276 * a JSONArray in which the first element is the tag name. If the tag has
277 * attributes, then the second element will be JSONObject containing the
278 * name/value pairs. If the tag contains children, then strings and
279 * JSONArrays will represent the child content and tags.
280 * As opposed to toJSONArray this method does not attempt to convert
281 * any text node or attribute value to any type
282 * but just leaves it as a string.
283 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
284 * @param x An XMLTokener.
285 * @param keepStrings If true, then values will not be coerced into boolean
286 * or numeric values and will instead be left as strings
287 * @return A JSONArray containing the structured data from the XML string.
288 * @throws JSONException Thrown on error converting to a JSONArray
290 public static JSONArray toJSONArray(XMLTokener x, boolean keepStrings) throws JSONException {
291 return (JSONArray)parse(x, true, null, keepStrings);
296 * Convert a well-formed (but not necessarily valid) XML string into a
297 * JSONArray using the JsonML transform. Each XML tag is represented as
298 * a JSONArray in which the first element is the tag name. If the tag has
299 * attributes, then the second element will be JSONObject containing the
300 * name/value pairs. If the tag contains children, then strings and
301 * JSONArrays will represent the child content and tags.
302 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
303 * @param x An XMLTokener.
304 * @return A JSONArray containing the structured data from the XML string.
305 * @throws JSONException Thrown on error converting to a JSONArray
307 public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
308 return (JSONArray)parse(x, true, null, false);
313 * Convert a well-formed (but not necessarily valid) XML string into a
314 * JSONObject using the JsonML transform. Each XML tag is represented as
315 * a JSONObject with a "tagName" property. If the tag has attributes, then
316 * the attributes will be in the JSONObject as properties. If the tag
317 * contains children, the object will have a "childNodes" property which
318 * will be an array of strings and JsonML JSONObjects.
320 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
321 * @param string The XML source text.
322 * @return A JSONObject containing the structured data from the XML string.
323 * @throws JSONException Thrown on error converting to a JSONObject
325 public static JSONObject toJSONObject(String string) throws JSONException {
326 return (JSONObject)parse(new XMLTokener(string), false, null, false);
331 * Convert a well-formed (but not necessarily valid) XML string into a
332 * JSONObject using the JsonML transform. Each XML tag is represented as
333 * a JSONObject with a "tagName" property. If the tag has attributes, then
334 * the attributes will be in the JSONObject as properties. If the tag
335 * contains children, the object will have a "childNodes" property which
336 * will be an array of strings and JsonML JSONObjects.
338 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
339 * @param string The XML source text.
340 * @param keepStrings If true, then values will not be coerced into boolean
341 * or numeric values and will instead be left as strings
342 * @return A JSONObject containing the structured data from the XML string.
343 * @throws JSONException Thrown on error converting to a JSONObject
345 public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
346 return (JSONObject)parse(new XMLTokener(string), false, null, keepStrings);
351 * Convert a well-formed (but not necessarily valid) XML string into a
352 * JSONObject using the JsonML transform. Each XML tag is represented as
353 * a JSONObject with a "tagName" property. If the tag has attributes, then
354 * the attributes will be in the JSONObject as properties. If the tag
355 * contains children, the object will have a "childNodes" property which
356 * will be an array of strings and JsonML JSONObjects.
358 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
359 * @param x An XMLTokener of the XML source text.
360 * @return A JSONObject containing the structured data from the XML string.
361 * @throws JSONException Thrown on error converting to a JSONObject
363 public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
364 return (JSONObject)parse(x, false, null, false);
369 * Convert a well-formed (but not necessarily valid) XML string into a
370 * JSONObject using the JsonML transform. Each XML tag is represented as
371 * a JSONObject with a "tagName" property. If the tag has attributes, then
372 * the attributes will be in the JSONObject as properties. If the tag
373 * contains children, the object will have a "childNodes" property which
374 * will be an array of strings and JsonML JSONObjects.
376 * Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
377 * @param x An XMLTokener of the XML source text.
378 * @param keepStrings If true, then values will not be coerced into boolean
379 * or numeric values and will instead be left as strings
380 * @return A JSONObject containing the structured data from the XML string.
381 * @throws JSONException Thrown on error converting to a JSONObject
383 public static JSONObject toJSONObject(XMLTokener x, boolean keepStrings) throws JSONException {
384 return (JSONObject)parse(x, false, null, keepStrings);
389 * Reverse the JSONML transformation, making an XML text from a JSONArray.
390 * @param ja A JSONArray.
391 * @return An XML string.
392 * @throws JSONException Thrown on error converting to a string
394 public static String toString(JSONArray ja) throws JSONException {
399 StringBuilder sb = new StringBuilder();
404 tagName = ja.getString(0);
405 XML.noSpace(tagName);
406 tagName = XML.escape(tagName);
411 if (object instanceof JSONObject) {
413 jo = (JSONObject)object;
415 // Emit the attributes
417 // Don't use the new entrySet API to maintain Android support
418 for (final String key : jo.keySet()) {
419 final Object value = jo.opt(key);
423 sb.append(XML.escape(key));
426 sb.append(XML.escape(value.toString()));
434 // Emit content in body
436 length = ja.length();
445 if (object != null) {
446 if (object instanceof String) {
447 sb.append(XML.escape(object.toString()));
448 } else if (object instanceof JSONObject) {
449 sb.append(toString((JSONObject)object));
450 } else if (object instanceof JSONArray) {
451 sb.append(toString((JSONArray)object));
453 sb.append(object.toString());
456 } while (i < length);
462 return sb.toString();
466 * Reverse the JSONML transformation, making an XML text from a JSONObject.
467 * The JSONObject must contain a "tagName" property. If it has children,
468 * then it must have a "childNodes" property containing an array of objects.
469 * The other properties are attributes with string values.
470 * @param jo A JSONObject.
471 * @return An XML string.
472 * @throws JSONException Thrown on error converting to a string
474 public static String toString(JSONObject jo) throws JSONException {
475 StringBuilder sb = new StringBuilder();
485 tagName = jo.optString("tagName");
486 if (tagName == null) {
487 return XML.escape(jo.toString());
489 XML.noSpace(tagName);
490 tagName = XML.escape(tagName);
494 //Emit the attributes
496 // Don't use the new entrySet API to maintain Android support
497 for (final String key : jo.keySet()) {
498 if (!"tagName".equals(key) && !"childNodes".equals(key)) {
503 sb.append(XML.escape(key));
506 sb.append(XML.escape(value.toString()));
512 //Emit content in body
514 ja = jo.optJSONArray("childNodes");
520 length = ja.length();
521 for (i = 0; i < length; i += 1) {
523 if (object != null) {
524 if (object instanceof String) {
525 sb.append(XML.escape(object.toString()));
526 } else if (object instanceof JSONObject) {
527 sb.append(toString((JSONObject)object));
528 } else if (object instanceof JSONArray) {
529 sb.append(toString((JSONArray)object));
531 sb.append(object.toString());
540 return sb.toString();