Mercurial > dive4elements > framework
comparison artifacts-common/src/main/java/de/intevation/artifacts/common/utils/JSON.java @ 381:bed51de8ae58
Added JSON parser from Artefact Server NG to bridge from JSON to XML speaking services.
artifacts/trunk@4270 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Wed, 18 Apr 2012 17:10:10 +0000 |
parents | |
children | 823752e3d392 |
comparison
equal
deleted
inserted
replaced
380:9798e4d83681 | 381:bed51de8ae58 |
---|---|
1 package de.intevation.artifacts.common.utils; | |
2 | |
3 import java.util.Map; | |
4 import java.util.LinkedHashMap; | |
5 import java.util.List; | |
6 import java.util.ArrayList; | |
7 import java.util.Iterator; | |
8 | |
9 import java.io.IOException; | |
10 import java.io.PushbackInputStream; | |
11 import java.io.InputStream; | |
12 import java.io.PrintWriter; | |
13 import java.io.ByteArrayInputStream; | |
14 | |
15 import java.nio.charset.Charset; | |
16 import java.nio.charset.UnsupportedCharsetException; | |
17 | |
18 public final class JSON | |
19 { | |
20 private JSON() { | |
21 } | |
22 | |
23 private static final boolean isDigit(int c) { | |
24 return c >= '0' && c <= '9'; | |
25 } | |
26 | |
27 public static final boolean isWhitespace(int c) { | |
28 return c == ' ' || c == '\n' || c == '\r' | |
29 || c == '\t' || c == '\f'; | |
30 } | |
31 | |
32 private static final void match(int c, int x) throws IOException { | |
33 if (c != x) { | |
34 throw new IOException( | |
35 "Expecting '" + (char)c + "' found '" + (char)x + "'"); | |
36 } | |
37 } | |
38 | |
39 private static final int eof(InputStream in) | |
40 throws IOException | |
41 { | |
42 int c = in.read(); | |
43 if (c == -1) { | |
44 throw new IOException("EOF unexpected."); | |
45 } | |
46 return c; | |
47 } | |
48 | |
49 private static final int whitespace(InputStream in) | |
50 throws IOException | |
51 { | |
52 int c; | |
53 while (isWhitespace(c = eof(in))); | |
54 return c; | |
55 } | |
56 | |
57 private static final int parseHex(String hex) throws IOException { | |
58 try { | |
59 return Integer.parseInt(hex, 16); | |
60 } | |
61 catch (NumberFormatException nfe) { | |
62 throw new IOException("'" + hex + "' is not a hex string."); | |
63 } | |
64 } | |
65 | |
66 public static final String jsonString(String string) { | |
67 StringBuilder sb = new StringBuilder(string.length()+2); | |
68 | |
69 sb.append('"'); | |
70 | |
71 for (int i = 0, N = string.length(); i < N; ++i) { | |
72 char c = string.charAt(i); | |
73 switch (c) { | |
74 case '"': sb.append("\\\""); break; | |
75 case '\t': sb.append("\\t"); break; | |
76 case '\r': sb.append("\\r"); break; | |
77 case '\n': sb.append("\\n"); break; | |
78 case '\b': sb.append("\\b"); break; | |
79 case '\f': sb.append("\\f"); break; | |
80 default: | |
81 if (c >= 128) { | |
82 sb.append("\\u"); | |
83 String hex = Integer.toHexString((int)c); | |
84 for (int j = 4-hex.length(); j > 0; --j) { | |
85 sb.append('0'); | |
86 } | |
87 sb.append(hex); | |
88 } | |
89 else { | |
90 sb.append(c); | |
91 } | |
92 } | |
93 } | |
94 | |
95 sb.append('"'); | |
96 | |
97 return sb.toString(); | |
98 } | |
99 | |
100 | |
101 public static void write(PrintWriter out, Map<String, Object> map) { | |
102 writeObject(out, map); | |
103 } | |
104 | |
105 private static void writeValue(PrintWriter out, Object value) { | |
106 if (value instanceof Map) { | |
107 writeObject(out, (Map)value); | |
108 } | |
109 else if (value instanceof List) { | |
110 writeList(out, (List)value); | |
111 } | |
112 else if (value instanceof Number) { | |
113 out.print(value); | |
114 } | |
115 else if (value instanceof Boolean) { | |
116 out.print(((Boolean)value) ? "true" : "false"); | |
117 } | |
118 else if (value == null) { | |
119 out.print("null"); | |
120 } | |
121 else { | |
122 out.print(jsonString(value.toString())); | |
123 } | |
124 } | |
125 | |
126 private static void writeObject(PrintWriter out, Map map) { | |
127 | |
128 out.print('{'); | |
129 Iterator iter = map.entrySet().iterator(); | |
130 while (iter.hasNext()) { | |
131 Map.Entry entry = (Map.Entry)iter.next(); | |
132 out.print(jsonString(entry.getKey().toString())); | |
133 out.print(':'); | |
134 writeValue(out, entry.getValue()); | |
135 if (iter.hasNext()) { | |
136 out.print(','); | |
137 } | |
138 } | |
139 out.print('}'); | |
140 } | |
141 | |
142 private static void writeList(PrintWriter out, List list) { | |
143 out.print('['); | |
144 Iterator iter = list.iterator(); | |
145 while (iter.hasNext()) { | |
146 writeValue(out, iter.next()); | |
147 if (iter.hasNext()) { | |
148 out.print(','); | |
149 } | |
150 } | |
151 out.print(']'); | |
152 } | |
153 | |
154 public static Map<String, Object> parse(String in) | |
155 throws IOException | |
156 { | |
157 return parse(asInputStream(in)); | |
158 } | |
159 | |
160 private static InputStream asInputStream(String in) { | |
161 byte [] bytes; | |
162 try { | |
163 bytes = in.getBytes(Charset.forName("US-ASCII")); | |
164 } | |
165 catch (UnsupportedCharsetException uce) { | |
166 // Should not happen. | |
167 bytes = in.getBytes(); | |
168 } | |
169 return new ByteArrayInputStream(bytes); | |
170 } | |
171 | |
172 public static Map<String, Object> parse(InputStream in) | |
173 throws IOException | |
174 { | |
175 return parseObject(new PushbackInputStream(in, 1)); | |
176 } | |
177 | |
178 public static Map<String, Object> parse(PushbackInputStream in) | |
179 throws IOException | |
180 { | |
181 return parseObject(in); | |
182 } | |
183 | |
184 private static final String parseString( | |
185 PushbackInputStream in | |
186 ) | |
187 throws IOException | |
188 { | |
189 StringBuilder sb = new StringBuilder(); | |
190 | |
191 int mode = 0; | |
192 | |
193 char [] hex = new char[4]; | |
194 | |
195 match('"', eof(in)); | |
196 | |
197 OUT: for (int c = eof(in);; c = eof(in)) { | |
198 | |
199 switch (mode) { | |
200 case 0: | |
201 if (c == '"') { | |
202 break OUT; | |
203 } | |
204 if (c == '\\') { | |
205 mode = 1; | |
206 } | |
207 else { | |
208 sb.append((char)c); | |
209 } | |
210 break; | |
211 case 1: | |
212 switch (c) { | |
213 case 'u': | |
214 mode = 2; | |
215 continue; | |
216 case 'b': | |
217 sb.append('\b'); | |
218 break; | |
219 case 'f': | |
220 sb.append('\f'); | |
221 break; | |
222 case 'n': | |
223 sb.append('\n'); | |
224 break; | |
225 case 'r': | |
226 sb.append('\r'); | |
227 break; | |
228 case 't': | |
229 sb.append('\t'); | |
230 break; | |
231 default: | |
232 sb.append((char)c); | |
233 } | |
234 mode = 0; | |
235 break; | |
236 case 2: | |
237 hex[0] = (char)c; | |
238 mode = 3; | |
239 break; | |
240 case 3: | |
241 hex[1] = (char)c; | |
242 mode = 4; | |
243 break; | |
244 case 4: | |
245 hex[2] = (char)c; | |
246 mode = 5; | |
247 break; | |
248 case 5: | |
249 hex[3] = (char)c; | |
250 sb.append((char)parseHex(new String(hex))); | |
251 mode = 0; | |
252 break; | |
253 } | |
254 } | |
255 return sb.toString(); | |
256 } | |
257 | |
258 private static final Boolean parseTrue(InputStream in) | |
259 throws IOException | |
260 { | |
261 match('t', eof(in)); | |
262 match('r', eof(in)); | |
263 match('u', eof(in)); | |
264 match('e', eof(in)); | |
265 return Boolean.TRUE; | |
266 } | |
267 | |
268 private static final Boolean parseFalse(InputStream in) | |
269 throws IOException | |
270 { | |
271 match('f', eof(in)); | |
272 match('a', eof(in)); | |
273 match('l', eof(in)); | |
274 match('s', eof(in)); | |
275 match('e', eof(in)); | |
276 return Boolean.FALSE; | |
277 } | |
278 | |
279 private static final Object parseNull(InputStream in) | |
280 throws IOException | |
281 { | |
282 match('n', eof(in)); | |
283 match('u', eof(in)); | |
284 match('l', eof(in)); | |
285 match('l', eof(in)); | |
286 return null; | |
287 } | |
288 | |
289 private static final Number parseNumber(PushbackInputStream in) | |
290 throws IOException | |
291 { | |
292 StringBuilder sb = new StringBuilder(); | |
293 | |
294 boolean isInteger = true; | |
295 | |
296 int c; | |
297 OUT: for (;;) { | |
298 switch (c = eof(in)) { | |
299 case '0': case '1': case '2': case '3': case '4': | |
300 case '5': case '6': case '7': case '8': case '9': | |
301 case '-': case '+': | |
302 sb.append((char)c); | |
303 break; | |
304 case '.': case 'e': case 'E': | |
305 isInteger = false; | |
306 sb.append((char)c); | |
307 break; | |
308 default: | |
309 in.unread(c); | |
310 break OUT; | |
311 } | |
312 } | |
313 | |
314 try { | |
315 if (isInteger) { | |
316 return sb.length() > 9 | |
317 ? (Number)Long .valueOf(sb.toString()) | |
318 : (Number)Integer.valueOf(sb.toString()); | |
319 } | |
320 return (Number)Double.valueOf(sb.toString()); | |
321 } | |
322 catch (NumberFormatException nfe) { | |
323 throw new IOException("Not a number '" + sb + "'"); | |
324 } | |
325 } | |
326 | |
327 private static List<Object> parseList(PushbackInputStream in) | |
328 throws IOException | |
329 { | |
330 List<Object> list = new ArrayList<Object>(); | |
331 match('[', whitespace(in)); | |
332 int c = whitespace(in); | |
333 if (c == ']') { | |
334 return list; | |
335 } | |
336 | |
337 for (;; c = whitespace(in)) { | |
338 Object value; | |
339 in.unread(c); | |
340 switch (c) { | |
341 case '{': | |
342 value = parseObject(in); | |
343 break; | |
344 case '[': | |
345 value = parseList(in); | |
346 break; | |
347 case '"': | |
348 value = parseString(in); | |
349 break; | |
350 case 't': | |
351 value = parseTrue(in); | |
352 break; | |
353 case 'f': | |
354 value = parseFalse(in); | |
355 break; | |
356 case 'n': | |
357 value = parseNull(in); | |
358 break; | |
359 default: | |
360 value = parseNumber(in); | |
361 } | |
362 list.add(value); | |
363 | |
364 if ((c = whitespace(in)) == ']') break; | |
365 match(',', c); | |
366 } | |
367 return list; | |
368 } | |
369 | |
370 private static void parsePair( | |
371 PushbackInputStream in, | |
372 Map<String, Object> pairs | |
373 ) | |
374 throws IOException | |
375 { | |
376 in.unread(whitespace(in)); | |
377 String string = parseString(in); | |
378 match(':', whitespace(in)); | |
379 | |
380 Object value; | |
381 | |
382 int c = whitespace(in); | |
383 in.unread(c); | |
384 switch (c) { | |
385 case '{': | |
386 value = parseObject(in); | |
387 break; | |
388 case '[': | |
389 value = parseList(in); | |
390 break; | |
391 case '"': | |
392 value = parseString(in); | |
393 break; | |
394 case 't': | |
395 value = parseTrue(in); | |
396 break; | |
397 case 'f': | |
398 value = parseFalse(in); | |
399 break; | |
400 case 'n': | |
401 value = parseNull(in); | |
402 break; | |
403 default: | |
404 value = parseNumber(in); | |
405 } | |
406 pairs.put(string, value); | |
407 } | |
408 | |
409 private static Map<String, Object> parseObject(PushbackInputStream in) | |
410 throws IOException | |
411 { | |
412 Map<String, Object> pairs = new LinkedHashMap<String, Object>(); | |
413 | |
414 int c = whitespace(in); | |
415 match('{', c); | |
416 | |
417 if ((c = whitespace(in)) == '}') { | |
418 return pairs; | |
419 } | |
420 | |
421 in.unread(c); | |
422 | |
423 for (;;) { | |
424 parsePair(in, pairs); | |
425 | |
426 if ((c = whitespace(in)) == '}') { | |
427 break; | |
428 } | |
429 | |
430 if (c == '}') break; | |
431 match(',', c); | |
432 } | |
433 | |
434 return pairs; | |
435 } | |
436 } |