001 package net.sourceforge.javajson;
002
003 import java.io.InputStream;
004 import java.io.Reader;
005 import java.io.StringReader;
006 import java.util.HashMap;
007 import java.util.Iterator;
008 import java.util.Map;
009
010 import net.sourceforge.javajson.parser.ASTparse;
011 import net.sourceforge.javajson.parser.JsonParser;
012 import net.sourceforge.javajson.parser.ParseException;
013 import net.sourceforge.javajson.parser.TokenMgrError;
014
015 /**
016 * Simpler implementation of Json that throws less exceptions. For all the
017 * getXXX method, if the item is not found, it returns null, false, or 0. If
018 * they key is null or otherwise invalid, it throws an exception.
019 *
020 * @author mdeanda
021 */
022 public class JsonObject implements Iterable<String> {
023 private Map<String, JsonValue> map;
024
025 /** Parses a string to a json object. */
026 public static JsonObject parse(String input) throws JsonException {
027 return parse(new StringReader(input));
028 }
029
030 /** Parses a string to a json object. */
031 public static JsonObject parse(Reader reader) throws JsonException {
032 try {
033 JsonParser parser = new JsonParser(reader);
034 ASTparse root = (ASTparse) parser.parse();
035 return root.getJsonObject();
036 } catch (ParseException pe) {
037 throw new JsonException(pe);
038 } catch (TokenMgrError error) {
039 throw new JsonException(error);
040 }
041 }
042
043 /** Parses a string to a json object. */
044 public static JsonObject parse(InputStream is) throws JsonException {
045 try {
046 JsonParser parser = new JsonParser(is);
047 ASTparse root = (ASTparse) parser.parse();
048 return root.getJsonObject();
049 } catch (ParseException pe) {
050 throw new JsonException(pe);
051 } catch (TokenMgrError error) {
052 throw new JsonException(error);
053 }
054 }
055
056 public JsonObject() {
057 map = new HashMap<String, JsonValue>();
058 }
059
060 /**
061 * Accumlates multiple values into an array. This is a convenience function
062 * for creating an array, adding it to the object, then adding to the array.
063 * If there is already a value for this key, it will be the first item in
064 * the array
065 *
066 * @param key
067 * @param val
068 */
069 public JsonObject accumulate(String key, boolean val) {
070 if (!map.containsKey(key)) {
071 put(key, new JsonArray());
072 } else if (!map.get(key).isJsonArray()) {
073 JsonValue old = map.get(key);
074 put(key, new JsonArray());
075 getJsonArray(key).add(old);
076 }
077
078 getJsonArray(key).add(val);
079 return this;
080 }
081
082 /**
083 * Accumlates multiple values into an array. This is a convenience function
084 * for creating an array, adding it to the object, then adding to the array.
085 * If there is already a value for this key, it will be the first item in
086 * the array
087 *
088 * @param key
089 * @param val
090 */
091 public JsonObject accumulate(String key, double val) {
092 if (!map.containsKey(key)) {
093 put(key, new JsonArray());
094 } else if (!map.get(key).isJsonArray()) {
095 JsonValue old = map.get(key);
096 put(key, new JsonArray());
097 getJsonArray(key).add(old);
098 }
099
100 getJsonArray(key).add(val);
101 return this;
102 }
103
104 /**
105 * Accumlates multiple values into an array. This is a convenience function
106 * for creating an array, adding it to the object, then adding to the array.
107 * If there is already a value for this key, it will be the first item in
108 * the array
109 *
110 * @param key
111 * @param val
112 */
113 public JsonObject accumulate(String key, float val) {
114 if (!map.containsKey(key)) {
115 put(key, new JsonArray());
116 } else if (!map.get(key).isJsonArray()) {
117 JsonValue old = map.get(key);
118 put(key, new JsonArray());
119 getJsonArray(key).add(old);
120 }
121
122 getJsonArray(key).add(val);
123 return this;
124 }
125
126 /**
127 * Accumlates multiple values into an array. This is a convenience function
128 * for creating an array, adding it to the object, then adding to the array.
129 * If there is already a value for this key, it will be the first item in
130 * the array
131 *
132 * @param key
133 * @param val
134 */
135 public JsonObject accumulate(String key, int val) {
136 if (!map.containsKey(key)) {
137 put(key, new JsonArray());
138 } else if (!map.get(key).isJsonArray()) {
139 JsonValue old = map.get(key);
140 put(key, new JsonArray());
141 getJsonArray(key).add(old);
142 }
143
144 getJsonArray(key).add(val);
145 return this;
146 }
147
148 /**
149 * Accumlates multiple values into an array. This is a convenience function
150 * for creating an array, adding it to the object, then adding to the array.
151 * If there is already a value for this key, it will be the first item in
152 * the array
153 *
154 * @param key
155 * @param val
156 */
157 public JsonObject accumulate(String key, JsonArray val) {
158 if (!map.containsKey(key)) {
159 put(key, new JsonArray());
160 } else if (!map.get(key).isJsonArray()) {
161 JsonValue old = map.get(key);
162 put(key, new JsonArray());
163 getJsonArray(key).add(old);
164 }
165
166 getJsonArray(key).add(val);
167 return this;
168 }
169
170 /**
171 * Accumlates multiple values into an array. This is a convenience function
172 * for creating an array, adding it to the object, then adding to the array.
173 * If there is already a value for this key, it will be the first item in
174 * the array
175 *
176 * @param key
177 * @param val
178 */
179 public JsonObject accumulate(String key, JsonObject val) {
180 if (!map.containsKey(key)) {
181 put(key, new JsonArray());
182 } else if (!map.get(key).isJsonArray()) {
183 JsonValue old = map.get(key);
184 put(key, new JsonArray());
185 getJsonArray(key).add(old);
186 }
187
188 getJsonArray(key).add(val);
189 return this;
190 }
191
192 /**
193 * Accumlates multiple values into an array. This is a convenience function
194 * for creating an array, adding it to the object, then adding to the array.
195 * If there is already a value for this key, it will be the first item in
196 * the array
197 *
198 * @param key
199 * @param val
200 */
201 public JsonObject accumulate(String key, String val) {
202 if (!map.containsKey(key)) {
203 put(key, new JsonArray());
204 } else if (!map.get(key).isJsonArray()) {
205 JsonValue old = map.get(key);
206 put(key, new JsonArray());
207 getJsonArray(key).add(old);
208 }
209
210 getJsonArray(key).add(val);
211 return this;
212 }
213
214 public boolean getBoolean(String key) {
215 if (map.containsKey(key))
216 return map.get(key).getBoolean();
217 return false;
218 }
219
220 public double getDouble(String key) {
221 if (map.containsKey(key))
222 return map.get(key).getDouble();
223 return 0.0;
224 }
225
226 public float getFloat(String key) {
227 if (map.containsKey(key))
228 return map.get(key).getFloat();
229 return 0.0f;
230 }
231
232 public int getInt(String key) {
233 if (map.containsKey(key))
234 return map.get(key).getInt();
235 return 0;
236 }
237
238 public JsonArray getJsonArray(String key) {
239 if (map.containsKey(key))
240 return map.get(key).getJsonArray();
241 return null;
242 }
243
244 public JsonObject getJsonObject(String key) {
245 if (map.containsKey(key))
246 return map.get(key).getJsonObject();
247 return null;
248 }
249
250 public long getLong(String key) {
251 if (map.containsKey(key))
252 return map.get(key).getLong();
253 return 0l;
254 }
255
256 public String getString(String key) {
257 if (map.containsKey(key))
258 return map.get(key).getString();
259 return null;
260 }
261
262 public boolean hasKey(String key) {
263 return map.containsKey(key);
264 }
265
266 /**
267 * Checks if the item at a key is a boolean. See
268 * {@link JsonValue#isBoolean()} for more information
269 *
270 * @param key
271 * @return True if the object contains the item and is a boolean
272 */
273 public boolean isBoolean(String key) {
274 if (map.containsKey(key))
275 return map.get(key).isBoolean();
276 else
277 return false;
278 }
279
280 /**
281 * Checks if the item at a key is a double. See {@link JsonValue#isDouble()}
282 * for more information
283 *
284 * @param key
285 */
286 public boolean isDouble(String key) {
287 if (map.containsKey(key))
288 return map.get(key).isDouble();
289 else
290 return false;
291 }
292
293 /**
294 * Checks if the item at a key is a float. See {@link JsonValue#isFloat()}
295 * for more information
296 *
297 * @param key
298 */
299 public boolean isFloat(String key) {
300 if (map.containsKey(key))
301 return map.get(key).isFloat();
302 else
303 return false;
304 }
305
306 /**
307 * Checks if the item at a key is a int. See {@link JsonValue#isInt()} for
308 * more information
309 *
310 * @param key
311 */
312 public boolean isInt(String key) {
313 if (map.containsKey(key))
314 return map.get(key).isInt();
315 else
316 return false;
317 }
318
319 /**
320 * Checks if the item at a key is a long. See {@link JsonValue#isLong()} for
321 * more information
322 *
323 * @param key
324 */
325 public boolean isLong(String key) {
326 if (map.containsKey(key))
327 return map.get(key).isLong();
328 else
329 return false;
330 }
331
332 /**
333 * Checks if the item at a key is an array. See
334 * {@link JsonValue#isJsonArray()} for more information
335 *
336 * @param key
337 * @return True if the object contains the item and is a json array
338 */
339 public boolean isJsonArray(String key) {
340 if (map.containsKey(key))
341 return map.get(key).isJsonArray();
342 else
343 return false;
344 }
345
346 /**
347 * Checks if the item at a key is an object. See
348 * {@link JsonValue#isJsonObject()} for more information
349 *
350 * @param key
351 * @return True if the object contains the item and is a json object
352 */
353 public boolean isJsonObject(String key) {
354 if (map.containsKey(key))
355 return map.get(key).isJsonObject();
356 else
357 return false;
358 }
359
360 /**
361 * Checks if the field contains a null value (different than hasKey because
362 * it can have the key but be null)
363 */
364 public boolean isNull(String key) {
365 if (map.containsKey(key)) {
366 JsonValue jv = map.get(key);
367 return jv.isNull();
368 }
369
370 return false;
371 }
372
373 /**
374 * checks that this object contains all fields as passed object. also checks
375 * that types match. if this object contains more fields then they can still
376 * be similar, but calling in reverse order may return false
377 *
378 * @param obj
379 * @return
380 */
381 public boolean isSimilar(JsonObject obj) {
382 if (obj == null)
383 return false;
384
385 for (String key : this) {
386 if (!obj.hasKey(key))
387 return false;
388 else if (!map.get(key).isSimilar(obj.map.get(key)))
389 return false;
390 }
391
392 return true;
393 }
394
395 /**
396 * Checks if the item at a key is an string. See
397 * {@link JsonValue#isString()} for more information
398 *
399 * @param key
400 * @return True if the object contains the item and is a string
401 */
402 public boolean isString(String key) {
403 if (map.containsKey(key))
404 return map.get(key).isString();
405 else
406 return false;
407 }
408
409 public Iterator<String> iterator() {
410 return map.keySet().iterator();
411 }
412
413 public void put(String key, Object value) {
414 if (value instanceof Boolean)
415 put(key, ((Boolean) value).booleanValue());
416 else if (value instanceof Double)
417 put(key, ((Double) value).doubleValue());
418 else if (value instanceof Float)
419 put(key, ((Float) value).floatValue());
420 else if (value instanceof Integer)
421 put(key, ((Integer) value).intValue());
422 else if (value instanceof Long)
423 put(key, ((Long) value).longValue());
424 else if (value instanceof JsonObject)
425 put(key, (JsonObject) value);
426 else if (value instanceof JsonArray)
427 put(key, (JsonArray) value);
428 else if (value instanceof String)
429 put(key, (String) value);
430 else if (value == null)
431 put(key, (String) null);
432 else
433 throw new ClassCastException("Unrecognized class");
434 }
435
436 public JsonObject put(String key, boolean value) {
437 map.put(key, new JsonValue(value));
438 return this;
439 }
440
441 public JsonObject put(String key, double value) {
442 map.put(key, new JsonValue(value));
443 return this;
444 }
445
446 public JsonObject put(String key, float value) {
447 map.put(key, new JsonValue(value));
448 return this;
449 }
450
451 public JsonObject put(String key, int value) {
452 map.put(key, new JsonValue(value));
453 return this;
454 }
455
456 public JsonObject put(String key, JsonArray value) {
457 map.put(key, new JsonValue(value));
458 return this;
459 }
460
461 public JsonObject put(String key, JsonObject value) {
462 map.put(key, new JsonValue(value));
463 return this;
464 }
465
466 public JsonObject put(String key, long value) {
467 map.put(key, new JsonValue(value));
468 return this;
469 }
470
471 public JsonObject put(String key, String value) {
472 map.put(key, new JsonValue(value));
473 return this;
474 }
475
476 public int size() {
477 return map.size();
478 }
479
480 @Override
481 public String toString() {
482 StringBuffer sb = new StringBuffer();
483 boolean hadSome = false;
484 sb.append("{");
485 for (String key : map.keySet()) {
486 JsonValue val = map.get(key);
487 if (hadSome)
488 sb.append(",");
489 // TODO: escape keys
490 sb.append("\"" + JsonValue.escape(key) + "\":");
491 sb.append(val);
492 hadSome = true;
493 }
494 sb.append("}");
495 return sb.toString();
496 }
497
498 /**
499 * Returns a nicely formatted string
500 *
501 * @param spacing
502 * @return
503 */
504 public String toString(int spacing) {
505 return toString(spacing, 0);
506 }
507
508 /**
509 * Called by toString(int) to keep track of the spacing
510 *
511 * @param spacing
512 * @param margin
513 * @return
514 */
515 protected String toString(int spacing, int margin) {
516 if (map.isEmpty())
517 return "{}";
518 else {
519 StringBuffer sb = new StringBuffer();
520 boolean hadSome = false;
521 // sb.append(getSpaces(margin));
522 sb.append("{\n");
523 for (String key : map.keySet()) {
524 JsonValue val = map.get(key);
525 if (hadSome)
526 sb.append(",\n");
527
528 sb.append(getSpaces(spacing + margin));
529 sb.append("\"" + JsonValue.escape(key) + "\":");
530 sb.append(val.toString(spacing, margin + spacing));
531 hadSome = true;
532 }
533 sb.append("\n");
534 sb.append(getSpaces(margin));
535 sb.append("}");
536
537 return sb.toString();
538 }
539 }
540
541 private String getSpaces(int spaces) {
542 StringBuffer sb = new StringBuffer();
543 for (int i = 0; i < spaces; i++)
544 sb.append(" ");
545 return sb.toString();
546 }
547
548 }