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 }

http://dive4elements.wald.intevation.org