1 package atg.taglib.json.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 import java.util.Iterator;
28
29 /**
30 * This provides static methods to convert an XML text into a JSONObject,
31 * and to covert a JSONObject into an XML text.
32 * @author JSON.org
33 * @version 2
34 */
35 public class XML {
36
37 /** The Character '&'. */
38 public static final Character AMP = new Character('&');
39
40 /** The Character '''. */
41 public static final Character APOS = new Character('\'');
42
43 /** The Character '!'. */
44 public static final Character BANG = new Character('!');
45
46 /** The Character '='. */
47 public static final Character EQ = new Character('=');
48
49 /** The Character '>'. */
50 public static final Character GT = new Character('>');
51
52 /** The Character '<'. */
53 public static final Character LT = new Character('<');
54
55 /** The Character '?'. */
56 public static final Character QUEST = new Character('?');
57
58 /** The Character '"'. */
59 public static final Character QUOT = new Character('"');
60
61 /** The Character '/'. */
62 public static final Character SLASH = new Character('/');
63
64 /**
65 * Replace special characters with XML escapes:
66 * <pre>
67 * & <small>(ampersand)</small> is replaced by &amp;
68 * < <small>(less than)</small> is replaced by &lt;
69 * > <small>(greater than)</small> is replaced by &gt;
70 * " <small>(double quote)</small> is replaced by &quot;
71 * </pre>
72 * @param string The string to be escaped.
73 * @return The escaped string.
74 */
75 public static String escape(String string) {
76 StringBuffer sb = new StringBuffer();
77 for (int i = 0, len = string.length(); i < len; i++) {
78 char c = string.charAt(i);
79 switch (c) {
80 case '&':
81 sb.append("&");
82 break;
83 case '<':
84 sb.append("<");
85 break;
86 case '>':
87 sb.append(">");
88 break;
89 case '"':
90 sb.append(""");
91 break;
92 default:
93 sb.append(c);
94 }
95 }
96 return sb.toString();
97 }
98
99 /**
100 * Scan the content following the named tag, attaching it to the context.
101 * @param x The XMLTokener containing the source string.
102 * @param context The JSONObject that will include the new material.
103 * @param name The tag name.
104 * @return true if the close tag is processed.
105 * @throws JSONException
106 */
107 private static boolean parse(XMLTokener x, JSONObject context,
108 String name) throws JSONException {
109 char c;
110 int i;
111 String n;
112 JSONObject o = null;
113 String s;
114 Object t;
115
116
117
118
119
120
121
122
123
124
125
126 t = x.nextToken();
127
128
129
130 if (t == BANG) {
131 c = x.next();
132 if (c == '-') {
133 if (x.next() == '-') {
134 x.skipPast("-->");
135 return false;
136 }
137 x.back();
138 } else if (c == '[') {
139 t = x.nextToken();
140 if (t.equals("CDATA")) {
141 if (x.next() == '[') {
142 s = x.nextCDATA();
143 if (s.length() > 0) {
144 context.accumulate("content", s);
145 }
146 return false;
147 }
148 }
149 throw x.syntaxError("Expected 'CDATA['");
150 }
151 i = 1;
152 do {
153 t = x.nextMeta();
154 if (t == null) {
155 throw x.syntaxError("Missing '>' after '<!'.");
156 } else if (t == LT) {
157 i += 1;
158 } else if (t == GT) {
159 i -= 1;
160 }
161 } while (i > 0);
162 return false;
163 } else if (t == QUEST) {
164
165
166
167 x.skipPast("?>");
168 return false;
169 } else if (t == SLASH) {
170
171
172
173 if (name == null || !x.nextToken().equals(name)) {
174 throw x.syntaxError("Mismatched close tag");
175 }
176 if (x.nextToken() != GT) {
177 throw x.syntaxError("Misshaped close tag");
178 }
179 return true;
180
181 } else if (t instanceof Character) {
182 throw x.syntaxError("Misshaped tag");
183
184
185
186 } else {
187 n = (String)t;
188 t = null;
189 o = new JSONObject();
190 for (;;) {
191 if (t == null) {
192 t = x.nextToken();
193 }
194
195
196
197 if (t instanceof String) {
198 s = (String)t;
199 t = x.nextToken();
200 if (t == EQ) {
201 t = x.nextToken();
202 if (!(t instanceof String)) {
203 throw x.syntaxError("Missing value");
204 }
205 o.accumulate(s, t);
206 t = null;
207 } else {
208 o.accumulate(s, "");
209 }
210
211
212
213 } else if (t == SLASH) {
214 if (x.nextToken() != GT) {
215 throw x.syntaxError("Misshaped tag");
216 }
217 context.accumulate(n, o);
218 return false;
219
220
221
222 } else if (t == GT) {
223 for (;;) {
224 t = x.nextContent();
225 if (t == null) {
226 if (name != null) {
227 throw x.syntaxError("Unclosed tag " + name);
228 }
229 return false;
230 } else if (t instanceof String) {
231 s = (String)t;
232 if (s.length() > 0) {
233 o.accumulate("content", s);
234 }
235
236
237
238 } else if (t == LT) {
239 if (parse(x, o, n)) {
240 if (o.length() == 0) {
241 context.accumulate(n, "");
242 } else if (o.length() == 1 &&
243 o.opt("content") != null) {
244 context.accumulate(n, o.opt("content"));
245 } else {
246 context.accumulate(n, o);
247 }
248 return false;
249 }
250 }
251 }
252 } else {
253 throw x.syntaxError("Misshaped tag");
254 }
255 }
256 }
257 }
258
259
260 /**
261 * Convert a well-formed (but not necessarily valid) XML string into a
262 * JSONObject. Some information may be lost in this transformation
263 * because JSON is a data format and XML is a document format. XML uses
264 * elements, attributes, and content text, while JSON uses unordered
265 * collections of name/value pairs and arrays of values. JSON does not
266 * does not like to distinguish between elements and attributes.
267 * Sequences of similar elements are represented as JSONArrays. Content
268 * text may be placed in a "content" member. Comments, prologs, DTDs, and
269 * <code><[ [ ]]></code> are ignored.
270 * @param string The source string.
271 * @return A JSONObject containing the structured data from the XML string.
272 * @throws JSONException
273 */
274 public static JSONObject toJSONObject(String string) throws JSONException {
275 JSONObject o = new JSONObject();
276 XMLTokener x = new XMLTokener(string);
277 while (x.more()) {
278 x.skipPast("<");
279 parse(x, o, null);
280 }
281 return o;
282 }
283
284
285 /**
286 * Convert a JSONObject into a well-formed, element-normal XML string.
287 * @param o A JSONObject.
288 * @return A string.
289 * @throws JSONException
290 */
291 public static String toString(Object o) throws JSONException {
292 return toString(o, null);
293 }
294
295
296 /**
297 * Convert a JSONObject into a well-formed, element-normal XML string.
298 * @param o A JSONObject.
299 * @param tagName The optional name of the enclosing tag.
300 * @return A string.
301 * @throws JSONException
302 */
303 public static String toString(Object o, String tagName)
304 throws JSONException {
305 StringBuffer b = new StringBuffer();
306 int i;
307 JSONArray ja;
308 JSONObject jo;
309 String k;
310 Iterator keys;
311 int len;
312 String s;
313 Object v;
314 if (o instanceof JSONObject) {
315
316
317
318 if (tagName != null) {
319 b.append('<');
320 b.append(tagName);
321 b.append('>');
322 }
323
324
325
326 jo = (JSONObject)o;
327 keys = jo.keys();
328 while (keys.hasNext()) {
329 k = keys.next().toString();
330 v = jo.get(k);
331 if (v instanceof String) {
332 s = (String)v;
333 } else {
334 s = null;
335 }
336
337
338
339 if (k.equals("content")) {
340 if (v instanceof JSONArray) {
341 ja = (JSONArray)v;
342 len = ja.length();
343 for (i = 0; i < len; i += 1) {
344 if (i > 0) {
345 b.append('\n');
346 }
347 b.append(escape(ja.get(i).toString()));
348 }
349 } else {
350 b.append(escape(v.toString()));
351 }
352
353
354
355 } else if (v instanceof JSONArray) {
356 ja = (JSONArray)v;
357 len = ja.length();
358 for (i = 0; i < len; i += 1) {
359 b.append(toString(ja.get(i), k));
360 }
361 } else if (v.equals("")) {
362 b.append('<');
363 b.append(k);
364 b.append("/>");
365
366
367
368 } else {
369 b.append(toString(v, k));
370 }
371 }
372 if (tagName != null) {
373
374
375
376 b.append("</");
377 b.append(tagName);
378 b.append('>');
379 }
380 return b.toString();
381
382
383
384
385 } else if (o instanceof JSONArray) {
386 ja = (JSONArray)o;
387 len = ja.length();
388 for (i = 0; i < len; ++i) {
389 b.append(toString(
390 ja.opt(i), (tagName == null) ? "array" : tagName));
391 }
392 return b.toString();
393 } else {
394 s = (o == null) ? "null" : escape(o.toString());
395 return (tagName == null) ? "\"" + s + "\"" :
396 (s.length() == 0) ? "<" + tagName + "/>" :
397 "<" + tagName + ">" + s + "</" + tagName + ">";
398 }
399 }
400 }