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 }