source: other-projects/tipple-android/tipple-lib/src/org/json1/JSONML.java@ 26899

Last change on this file since 26899 was 26899, checked in by davidb, 11 years ago

Tipple reborn after Chris's Summer of Code 2013

File size: 16.7 KB
Line 
1package org.json1;
2
3/*
4Copyright (c) 2008 JSON.org
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16The Software shall be used for Good, not Evil.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24SOFTWARE.
25*/
26
27import java.util.Iterator;
28
29
30/**
31 * This provides static methods to convert an XML text into a JSONArray or
32 * JSONObject, and to covert a JSONArray or JSONObject into an XML text using
33 * the JsonML transform.
34 *
35 * @author JSON.org
36 * @version 2012-03-28
37 */
38public class JSONML {
39
40 /**
41 * Parse XML values and store them in a JSONArray.
42 * @param x The XMLTokener containing the source string.
43 * @param arrayForm true if array form, false if object form.
44 * @param ja The JSONArray that is containing the current tag or null
45 * if we are at the outermost level.
46 * @return A JSONArray if the value is the outermost tag, otherwise null.
47 * @throws JSONException
48 */
49 private static Object parse(
50 XMLTokener x,
51 boolean arrayForm,
52 JSONArray ja
53 ) throws JSONException {
54 String attribute;
55 char c;
56 String closeTag = null;
57 int i;
58 JSONArray newja = null;
59 JSONObject newjo = null;
60 Object token;
61 String tagName = null;
62
63// Test for and skip past these forms:
64// <!-- ... -->
65// <![ ... ]]>
66// <! ... >
67// <? ... ?>
68
69 while (true) {
70 if (!x.more()) {
71 throw x.syntaxError("Bad XML");
72 }
73 token = x.nextContent();
74 if (token == XML.LT) {
75 token = x.nextToken();
76 if (token instanceof Character) {
77 if (token == XML.SLASH) {
78
79// Close tag </
80
81 token = x.nextToken();
82 if (!(token instanceof String)) {
83 throw new JSONException(
84 "Expected a closing name instead of '" +
85 token + "'.");
86 }
87 if (x.nextToken() != XML.GT) {
88 throw x.syntaxError("Misshaped close tag");
89 }
90 return token;
91 } else if (token == XML.BANG) {
92
93// <!
94
95 c = x.next();
96 if (c == '-') {
97 if (x.next() == '-') {
98 x.skipPast("-->");
99 } else {
100 x.back();
101 }
102 } else if (c == '[') {
103 token = x.nextToken();
104 if (token.equals("CDATA") && x.next() == '[') {
105 if (ja != null) {
106 ja.put(x.nextCDATA());
107 }
108 } else {
109 throw x.syntaxError("Expected 'CDATA['");
110 }
111 } else {
112 i = 1;
113 do {
114 token = x.nextMeta();
115 if (token == null) {
116 throw x.syntaxError("Missing '>' after '<!'.");
117 } else if (token == XML.LT) {
118 i += 1;
119 } else if (token == XML.GT) {
120 i -= 1;
121 }
122 } while (i > 0);
123 }
124 } else if (token == XML.QUEST) {
125
126// <?
127
128 x.skipPast("?>");
129 } else {
130 throw x.syntaxError("Misshaped tag");
131 }
132
133// Open tag <
134
135 } else {
136 if (!(token instanceof String)) {
137 throw x.syntaxError("Bad tagName '" + token + "'.");
138 }
139 tagName = (String)token;
140 newja = new JSONArray();
141 newjo = new JSONObject();
142 if (arrayForm) {
143 newja.put(tagName);
144 if (ja != null) {
145 ja.put(newja);
146 }
147 } else {
148 newjo.put("tagName", tagName);
149 if (ja != null) {
150 ja.put(newjo);
151 }
152 }
153 token = null;
154 for (;;) {
155 if (token == null) {
156 token = x.nextToken();
157 }
158 if (token == null) {
159 throw x.syntaxError("Misshaped tag");
160 }
161 if (!(token instanceof String)) {
162 break;
163 }
164
165// attribute = value
166
167 attribute = (String)token;
168 if (!arrayForm && ("tagName".equals(attribute) || "childNode".equals(attribute))) {
169 throw x.syntaxError("Reserved attribute.");
170 }
171 token = x.nextToken();
172 if (token == XML.EQ) {
173 token = x.nextToken();
174 if (!(token instanceof String)) {
175 throw x.syntaxError("Missing value");
176 }
177 newjo.accumulate(attribute, XML.stringToValue((String)token));
178 token = null;
179 } else {
180 newjo.accumulate(attribute, "");
181 }
182 }
183 if (arrayForm && newjo.length() > 0) {
184 newja.put(newjo);
185 }
186
187// Empty tag <.../>
188
189 if (token == XML.SLASH) {
190 if (x.nextToken() != XML.GT) {
191 throw x.syntaxError("Misshaped tag");
192 }
193 if (ja == null) {
194 if (arrayForm) {
195 return newja;
196 } else {
197 return newjo;
198 }
199 }
200
201// Content, between <...> and </...>
202
203 } else {
204 if (token != XML.GT) {
205 throw x.syntaxError("Misshaped tag");
206 }
207 closeTag = (String)parse(x, arrayForm, newja);
208 if (closeTag != null) {
209 if (!closeTag.equals(tagName)) {
210 throw x.syntaxError("Mismatched '" + tagName +
211 "' and '" + closeTag + "'");
212 }
213 tagName = null;
214 if (!arrayForm && newja.length() > 0) {
215 newjo.put("childNodes", newja);
216 }
217 if (ja == null) {
218 if (arrayForm) {
219 return newja;
220 } else {
221 return newjo;
222 }
223 }
224 }
225 }
226 }
227 } else {
228 if (ja != null) {
229 ja.put(token instanceof String
230 ? XML.stringToValue((String)token)
231 : token);
232 }
233 }
234 }
235 }
236
237
238 /**
239 * Convert a well-formed (but not necessarily valid) XML string into a
240 * JSONArray using the JsonML transform. Each XML tag is represented as
241 * a JSONArray in which the first element is the tag name. If the tag has
242 * attributes, then the second element will be JSONObject containing the
243 * name/value pairs. If the tag contains children, then strings and
244 * JSONArrays will represent the child tags.
245 * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
246 * @param string The source string.
247 * @return A JSONArray containing the structured data from the XML string.
248 * @throws JSONException
249 */
250 public static JSONArray toJSONArray(String string) throws JSONException {
251 return toJSONArray(new XMLTokener(string));
252 }
253
254
255 /**
256 * Convert a well-formed (but not necessarily valid) XML string into a
257 * JSONArray using the JsonML transform. Each XML tag is represented as
258 * a JSONArray in which the first element is the tag name. If the tag has
259 * attributes, then the second element will be JSONObject containing the
260 * name/value pairs. If the tag contains children, then strings and
261 * JSONArrays will represent the child content and tags.
262 * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
263 * @param x An XMLTokener.
264 * @return A JSONArray containing the structured data from the XML string.
265 * @throws JSONException
266 */
267 public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
268 return (JSONArray)parse(x, true, null);
269 }
270
271
272 /**
273 * Convert a well-formed (but not necessarily valid) XML string into a
274 * JSONObject using the JsonML transform. Each XML tag is represented as
275 * a JSONObject with a "tagName" property. If the tag has attributes, then
276 * the attributes will be in the JSONObject as properties. If the tag
277 * contains children, the object will have a "childNodes" property which
278 * will be an array of strings and JsonML JSONObjects.
279
280 * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
281 * @param x An XMLTokener of the XML source text.
282 * @return A JSONObject containing the structured data from the XML string.
283 * @throws JSONException
284 */
285 public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
286 return (JSONObject)parse(x, false, null);
287 }
288
289
290 /**
291 * Convert a well-formed (but not necessarily valid) XML string into a
292 * JSONObject using the JsonML transform. Each XML tag is represented as
293 * a JSONObject with a "tagName" property. If the tag has attributes, then
294 * the attributes will be in the JSONObject as properties. If the tag
295 * contains children, the object will have a "childNodes" property which
296 * will be an array of strings and JsonML JSONObjects.
297
298 * Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
299 * @param string The XML source text.
300 * @return A JSONObject containing the structured data from the XML string.
301 * @throws JSONException
302 */
303 public static JSONObject toJSONObject(String string) throws JSONException {
304 return toJSONObject(new XMLTokener(string));
305 }
306
307
308 /**
309 * Reverse the JSONML transformation, making an XML text from a JSONArray.
310 * @param ja A JSONArray.
311 * @return An XML string.
312 * @throws JSONException
313 */
314 public static String toString(JSONArray ja) throws JSONException {
315 int i;
316 JSONObject jo;
317 String key;
318 Iterator keys;
319 int length;
320 Object object;
321 StringBuffer sb = new StringBuffer();
322 String tagName;
323 String value;
324
325// Emit <tagName
326
327 tagName = ja.getString(0);
328 XML.noSpace(tagName);
329 tagName = XML.escape(tagName);
330 sb.append('<');
331 sb.append(tagName);
332
333 object = ja.opt(1);
334 if (object instanceof JSONObject) {
335 i = 2;
336 jo = (JSONObject)object;
337
338// Emit the attributes
339
340 keys = jo.keys();
341 while (keys.hasNext()) {
342 key = keys.next().toString();
343 XML.noSpace(key);
344 value = jo.optString(key);
345 if (value != null) {
346 sb.append(' ');
347 sb.append(XML.escape(key));
348 sb.append('=');
349 sb.append('"');
350 sb.append(XML.escape(value));
351 sb.append('"');
352 }
353 }
354 } else {
355 i = 1;
356 }
357
358//Emit content in body
359
360 length = ja.length();
361 if (i >= length) {
362 sb.append('/');
363 sb.append('>');
364 } else {
365 sb.append('>');
366 do {
367 object = ja.get(i);
368 i += 1;
369 if (object != null) {
370 if (object instanceof String) {
371 sb.append(XML.escape(object.toString()));
372 } else if (object instanceof JSONObject) {
373 sb.append(toString((JSONObject)object));
374 } else if (object instanceof JSONArray) {
375 sb.append(toString((JSONArray)object));
376 }
377 }
378 } while (i < length);
379 sb.append('<');
380 sb.append('/');
381 sb.append(tagName);
382 sb.append('>');
383 }
384 return sb.toString();
385 }
386
387 /**
388 * Reverse the JSONML transformation, making an XML text from a JSONObject.
389 * The JSONObject must contain a "tagName" property. If it has children,
390 * then it must have a "childNodes" property containing an array of objects.
391 * The other properties are attributes with string values.
392 * @param jo A JSONObject.
393 * @return An XML string.
394 * @throws JSONException
395 */
396 public static String toString(JSONObject jo) throws JSONException {
397 StringBuffer sb = new StringBuffer();
398 int i;
399 JSONArray ja;
400 String key;
401 Iterator keys;
402 int length;
403 Object object;
404 String tagName;
405 String value;
406
407//Emit <tagName
408
409 tagName = jo.optString("tagName");
410 if (tagName == null) {
411 return XML.escape(jo.toString());
412 }
413 XML.noSpace(tagName);
414 tagName = XML.escape(tagName);
415 sb.append('<');
416 sb.append(tagName);
417
418//Emit the attributes
419
420 keys = jo.keys();
421 while (keys.hasNext()) {
422 key = keys.next().toString();
423 if (!"tagName".equals(key) && !"childNodes".equals(key)) {
424 XML.noSpace(key);
425 value = jo.optString(key);
426 if (value != null) {
427 sb.append(' ');
428 sb.append(XML.escape(key));
429 sb.append('=');
430 sb.append('"');
431 sb.append(XML.escape(value));
432 sb.append('"');
433 }
434 }
435 }
436
437//Emit content in body
438
439 ja = jo.optJSONArray("childNodes");
440 if (ja == null) {
441 sb.append('/');
442 sb.append('>');
443 } else {
444 sb.append('>');
445 length = ja.length();
446 for (i = 0; i < length; i += 1) {
447 object = ja.get(i);
448 if (object != null) {
449 if (object instanceof String) {
450 sb.append(XML.escape(object.toString()));
451 } else if (object instanceof JSONObject) {
452 sb.append(toString((JSONObject)object));
453 } else if (object instanceof JSONArray) {
454 sb.append(toString((JSONArray)object));
455 } else {
456 sb.append(object.toString());
457 }
458 }
459 }
460 sb.append('<');
461 sb.append('/');
462 sb.append(tagName);
463 sb.append('>');
464 }
465 return sb.toString();
466 }
467}
Note: See TracBrowser for help on using the repository browser.