source: main/trunk/model-sites-dev/cambridge-museum/collect/waikato-independent/pre-import/EditableDatabaseTable/src/org/json/XML.java@ 34493

Last change on this file since 34493 was 34493, checked in by davidb, 4 years ago

Base project for providing jquery/jquery-ui controlled interface to editing a database table

File size: 16.7 KB
Line 
1package org.json;
2
3/*
4Copyright (c) 2002 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 JSONObject,
32 * and to covert a JSONObject into an XML text.
33 * @author JSON.org
34 * @version 2011-02-11
35 */
36public class XML {
37
38 /** The Character '&'. */
39 public static final Character AMP = new Character('&');
40
41 /** The Character '''. */
42 public static final Character APOS = new Character('\'');
43
44 /** The Character '!'. */
45 public static final Character BANG = new Character('!');
46
47 /** The Character '='. */
48 public static final Character EQ = new Character('=');
49
50 /** The Character '>'. */
51 public static final Character GT = new Character('>');
52
53 /** The Character '<'. */
54 public static final Character LT = new Character('<');
55
56 /** The Character '?'. */
57 public static final Character QUEST = new Character('?');
58
59 /** The Character '"'. */
60 public static final Character QUOT = new Character('"');
61
62 /** The Character '/'. */
63 public static final Character SLASH = new Character('/');
64
65 /**
66 * Replace special characters with XML escapes:
67 * <pre>
68 * &amp; <small>(ampersand)</small> is replaced by &amp;amp;
69 * &lt; <small>(less than)</small> is replaced by &amp;lt;
70 * &gt; <small>(greater than)</small> is replaced by &amp;gt;
71 * &quot; <small>(double quote)</small> is replaced by &amp;quot;
72 * </pre>
73 * @param string The string to be escaped.
74 * @return The escaped string.
75 */
76 public static String escape(String string) {
77 StringBuffer sb = new StringBuffer();
78 for (int i = 0, length = string.length(); i < length; i++) {
79 char c = string.charAt(i);
80 switch (c) {
81 case '&':
82 sb.append("&amp;");
83 break;
84 case '<':
85 sb.append("&lt;");
86 break;
87 case '>':
88 sb.append("&gt;");
89 break;
90 case '"':
91 sb.append("&quot;");
92 break;
93 case '\'':
94 sb.append("&apos;");
95 break;
96 default:
97 sb.append(c);
98 }
99 }
100 return sb.toString();
101 }
102
103 /**
104 * Throw an exception if the string contains whitespace.
105 * Whitespace is not allowed in tagNames and attributes.
106 * @param string
107 * @throws JSONException
108 */
109 public static void noSpace(String string) throws JSONException {
110 int i, length = string.length();
111 if (length == 0) {
112 throw new JSONException("Empty string.");
113 }
114 for (i = 0; i < length; i += 1) {
115 if (Character.isWhitespace(string.charAt(i))) {
116 throw new JSONException("'" + string +
117 "' contains a space character.");
118 }
119 }
120 }
121
122 /**
123 * Scan the content following the named tag, attaching it to the context.
124 * @param x The XMLTokener containing the source string.
125 * @param context The JSONObject that will include the new material.
126 * @param name The tag name.
127 * @return true if the close tag is processed.
128 * @throws JSONException
129 */
130 private static boolean parse(XMLTokener x, JSONObject context,
131 String name) throws JSONException {
132 char c;
133 int i;
134 JSONObject jsonobject = null;
135 String string;
136 String tagName;
137 Object token;
138
139// Test for and skip past these forms:
140// <!-- ... -->
141// <! ... >
142// <![ ... ]]>
143// <? ... ?>
144// Report errors for these forms:
145// <>
146// <=
147// <<
148
149 token = x.nextToken();
150
151// <!
152
153 if (token == BANG) {
154 c = x.next();
155 if (c == '-') {
156 if (x.next() == '-') {
157 x.skipPast("-->");
158 return false;
159 }
160 x.back();
161 } else if (c == '[') {
162 token = x.nextToken();
163 if (token.equals("CDATA")) {
164 if (x.next() == '[') {
165 string = x.nextCDATA();
166 if (string.length() > 0) {
167 context.accumulate("content", string);
168 }
169 return false;
170 }
171 }
172 throw x.syntaxError("Expected 'CDATA['");
173 }
174 i = 1;
175 do {
176 token = x.nextMeta();
177 if (token == null) {
178 throw x.syntaxError("Missing '>' after '<!'.");
179 } else if (token == LT) {
180 i += 1;
181 } else if (token == GT) {
182 i -= 1;
183 }
184 } while (i > 0);
185 return false;
186 } else if (token == QUEST) {
187
188// <?
189
190 x.skipPast("?>");
191 return false;
192 } else if (token == SLASH) {
193
194// Close tag </
195
196 token = x.nextToken();
197 if (name == null) {
198 throw x.syntaxError("Mismatched close tag " + token);
199 }
200 if (!token.equals(name)) {
201 throw x.syntaxError("Mismatched " + name + " and " + token);
202 }
203 if (x.nextToken() != GT) {
204 throw x.syntaxError("Misshaped close tag");
205 }
206 return true;
207
208 } else if (token instanceof Character) {
209 throw x.syntaxError("Misshaped tag");
210
211// Open tag <
212
213 } else {
214 tagName = (String)token;
215 token = null;
216 jsonobject = new JSONObject();
217 for (;;) {
218 if (token == null) {
219 token = x.nextToken();
220 }
221
222// attribute = value
223
224 if (token instanceof String) {
225 string = (String)token;
226 token = x.nextToken();
227 if (token == EQ) {
228 token = x.nextToken();
229 if (!(token instanceof String)) {
230 throw x.syntaxError("Missing value");
231 }
232 jsonobject.accumulate(string,
233 XML.stringToValue((String)token));
234 token = null;
235 } else {
236 jsonobject.accumulate(string, "");
237 }
238
239// Empty tag <.../>
240
241 } else if (token == SLASH) {
242 if (x.nextToken() != GT) {
243 throw x.syntaxError("Misshaped tag");
244 }
245 if (jsonobject.length() > 0) {
246 context.accumulate(tagName, jsonobject);
247 } else {
248 context.accumulate(tagName, "");
249 }
250 return false;
251
252// Content, between <...> and </...>
253
254 } else if (token == GT) {
255 for (;;) {
256 token = x.nextContent();
257 if (token == null) {
258 if (tagName != null) {
259 throw x.syntaxError("Unclosed tag " + tagName);
260 }
261 return false;
262 } else if (token instanceof String) {
263 string = (String)token;
264 if (string.length() > 0) {
265 jsonobject.accumulate("content",
266 XML.stringToValue(string));
267 }
268
269// Nested element
270
271 } else if (token == LT) {
272 if (parse(x, jsonobject, tagName)) {
273 if (jsonobject.length() == 0) {
274 context.accumulate(tagName, "");
275 } else if (jsonobject.length() == 1 &&
276 jsonobject.opt("content") != null) {
277 context.accumulate(tagName,
278 jsonobject.opt("content"));
279 } else {
280 context.accumulate(tagName, jsonobject);
281 }
282 return false;
283 }
284 }
285 }
286 } else {
287 throw x.syntaxError("Misshaped tag");
288 }
289 }
290 }
291 }
292
293
294 /**
295 * Try to convert a string into a number, boolean, or null. If the string
296 * can't be converted, return the string. This is much less ambitious than
297 * JSONObject.stringToValue, especially because it does not attempt to
298 * convert plus forms, octal forms, hex forms, or E forms lacking decimal
299 * points.
300 * @param string A String.
301 * @return A simple JSON value.
302 */
303 public static Object stringToValue(String string) {
304 if (string.equals("")) {
305 return string;
306 }
307 if (string.equalsIgnoreCase("true")) {
308 return Boolean.TRUE;
309 }
310 if (string.equalsIgnoreCase("false")) {
311 return Boolean.FALSE;
312 }
313 if (string.equalsIgnoreCase("null")) {
314 return JSONObject.NULL;
315 }
316 if (string.equals("0")) {
317 return new Integer(0);
318 }
319
320// If it might be a number, try converting it. If that doesn't work,
321// return the string.
322
323 try {
324 char initial = string.charAt(0);
325 boolean negative = false;
326 if (initial == '-') {
327 initial = string.charAt(1);
328 negative = true;
329 }
330 if (initial == '0' && string.charAt(negative ? 2 : 1) == '0') {
331 return string;
332 }
333 if ((initial >= '0' && initial <= '9')) {
334 if (string.indexOf('.') >= 0) {
335 return Double.valueOf(string);
336 } else if (string.indexOf('e') < 0 && string.indexOf('E') < 0) {
337 Long myLong = new Long(string);
338 if (myLong.longValue() == myLong.intValue()) {
339 return new Integer(myLong.intValue());
340 } else {
341 return myLong;
342 }
343 }
344 }
345 } catch (Exception ignore) {
346 }
347 return string;
348 }
349
350
351 /**
352 * Convert a well-formed (but not necessarily valid) XML string into a
353 * JSONObject. Some information may be lost in this transformation
354 * because JSON is a data format and XML is a document format. XML uses
355 * elements, attributes, and content text, while JSON uses unordered
356 * collections of name/value pairs and arrays of values. JSON does not
357 * does not like to distinguish between elements and attributes.
358 * Sequences of similar elements are represented as JSONArrays. Content
359 * text may be placed in a "content" member. Comments, prologs, DTDs, and
360 * <code>&lt;[ [ ]]></code> are ignored.
361 * @param string The source string.
362 * @return A JSONObject containing the structured data from the XML string.
363 * @throws JSONException
364 */
365 public static JSONObject toJSONObject(String string) throws JSONException {
366 JSONObject jo = new JSONObject();
367 XMLTokener x = new XMLTokener(string);
368 while (x.more() && x.skipPast("<")) {
369 parse(x, jo, null);
370 }
371 return jo;
372 }
373
374
375 /**
376 * Convert a JSONObject into a well-formed, element-normal XML string.
377 * @param object A JSONObject.
378 * @return A string.
379 * @throws JSONException
380 */
381 public static String toString(Object object) throws JSONException {
382 return toString(object, null);
383 }
384
385
386 /**
387 * Convert a JSONObject into a well-formed, element-normal XML string.
388 * @param object A JSONObject.
389 * @param tagName The optional name of the enclosing tag.
390 * @return A string.
391 * @throws JSONException
392 */
393 public static String toString(Object object, String tagName)
394 throws JSONException {
395 StringBuffer sb = new StringBuffer();
396 int i;
397 JSONArray ja;
398 JSONObject jo;
399 String key;
400 Iterator keys;
401 int length;
402 String string;
403 Object value;
404 if (object instanceof JSONObject) {
405
406// Emit <tagName>
407
408 if (tagName != null) {
409 sb.append('<');
410 sb.append(tagName);
411 sb.append('>');
412 }
413
414// Loop thru the keys.
415
416 jo = (JSONObject)object;
417 keys = jo.keys();
418 while (keys.hasNext()) {
419 key = keys.next().toString();
420 value = jo.opt(key);
421 if (value == null) {
422 value = "";
423 }
424 if (value instanceof String) {
425 string = (String)value;
426 } else {
427 string = null;
428 }
429
430// Emit content in body
431
432 if (key.equals("content")) {
433 if (value instanceof JSONArray) {
434 ja = (JSONArray)value;
435 length = ja.length();
436 for (i = 0; i < length; i += 1) {
437 if (i > 0) {
438 sb.append('\n');
439 }
440 sb.append(escape(ja.get(i).toString()));
441 }
442 } else {
443 sb.append(escape(value.toString()));
444 }
445
446// Emit an array of similar keys
447
448 } else if (value instanceof JSONArray) {
449 ja = (JSONArray)value;
450 length = ja.length();
451 for (i = 0; i < length; i += 1) {
452 value = ja.get(i);
453 if (value instanceof JSONArray) {
454 sb.append('<');
455 sb.append(key);
456 sb.append('>');
457 sb.append(toString(value));
458 sb.append("</");
459 sb.append(key);
460 sb.append('>');
461 } else {
462 sb.append(toString(value, key));
463 }
464 }
465 } else if (value.equals("")) {
466 sb.append('<');
467 sb.append(key);
468 sb.append("/>");
469
470// Emit a new tag <k>
471
472 } else {
473 sb.append(toString(value, key));
474 }
475 }
476 if (tagName != null) {
477
478// Emit the </tagname> close tag
479
480 sb.append("</");
481 sb.append(tagName);
482 sb.append('>');
483 }
484 return sb.toString();
485
486// XML does not have good support for arrays. If an array appears in a place
487// where XML is lacking, synthesize an <array> element.
488
489 } else {
490 if (object.getClass().isArray()) {
491 object = new JSONArray(object);
492 }
493 if (object instanceof JSONArray) {
494 ja = (JSONArray)object;
495 length = ja.length();
496 for (i = 0; i < length; i += 1) {
497 sb.append(toString(ja.opt(i), tagName == null ? "array" : tagName));
498 }
499 return sb.toString();
500 } else {
501 string = (object == null) ? "null" : escape(object.toString());
502 return (tagName == null) ? "\"" + string + "\"" :
503 (string.length() == 0) ? "<" + tagName + "/>" :
504 "<" + tagName + ">" + string + "</" + tagName + ">";
505 }
506 }
507 }
508}
Note: See TracBrowser for help on using the repository browser.