1 package org
.codehaus
.groovy
.grails
.web
.json
;
4 Copyright (c) 2002 JSON.org
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
16 The Software shall be used for Good, not Evil.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 import org
.apache
.commons
.lang
.UnhandledException
;
29 import java
.io
.IOException
;
30 import java
.io
.Writer
;
34 * A JSONArray is an ordered sequence of values. Its external text form is a
35 * string wrapped in square brackets with commas separating the values. The
36 * internal form is an object having <code>get</code> and <code>opt</code>
37 * methods for accessing the values by index, and <code>put</code> methods for
38 * adding or replacing values. The values can be any of these types:
39 * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
40 * <code>Number</code>, <code>String</code>, or the
41 * <code>JSONObject.NULL object</code>.
43 * The constructor can convert a JSON text into a Java object. The
44 * <code>toString</code> method converts to JSON text.
46 * A <code>get</code> method returns a value if one can be found, and throws an
47 * exception if one cannot be found. An <code>opt</code> method returns a
48 * default value instead of throwing an exception, and so is useful for
49 * obtaining optional values.
51 * The generic <code>get()</code> and <code>opt()</code> methods return an
52 * object which you can cast or query for type. There are also typed
53 * <code>get</code> and <code>opt</code> methods that do type checking and type
56 * The texts produced by the <code>toString</code> methods strictly conform to
57 * JSON syntax rules. The constructors are more forgiving in the texts they will
60 * <li>An extra <code>,</code> <small>(comma)</small> may appear just
61 * before the closing bracket.</li>
62 * <li>The <code>null</code> value will be inserted when there
63 * is <code>,</code> <small>(comma)</small> elision.</li>
64 * <li>Strings may be quoted with <code>'</code> <small>(single
65 * quote)</small>.</li>
66 * <li>Strings do not need to be quoted at all if they do not begin with a quote
67 * or single quote, and if they do not contain leading or trailing spaces,
68 * and if they do not contain any of these characters:
69 * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
70 * and if they are not the reserved words <code>true</code>,
71 * <code>false</code>, or <code>null</code>.</li>
72 * <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
73 * well as by <code>,</code> <small>(comma)</small>.</li>
74 * <li>Numbers may have the <code>0-</code> <small>(octal)</small> or
75 * <code>0x-</code> <small>(hex)</small> prefix.</li>
76 * <li>Comments written in the slashshlash, slashstar, and hash conventions
77 * will be ignored.</li>
83 public class JSONArray
implements List
{
87 * The arrayList where the JSONArray's properties are kept.
89 private ArrayList myArrayList
;
93 * Construct an empty JSONArray.
96 this.myArrayList
= new ArrayList();
100 * Construct a JSONArray from a JSONTokener.
102 * @param x A JSONTokener
103 * @throws JSONException If there is a syntax error.
105 public JSONArray(JSONTokener x
) throws JSONException
{
107 if (x
.nextClean() != '[') {
108 throw x
.syntaxError("A JSONArray text must start with '['");
110 if (x
.nextClean() == ']') {
115 if (x
.nextClean() == ',') {
117 this.myArrayList
.add(null);
120 this.myArrayList
.add(x
.nextValue());
122 switch (x
.nextClean()) {
125 if (x
.nextClean() == ']') {
133 throw x
.syntaxError("Expected a ',' or ']'");
140 * Construct a JSONArray from a source sJSON text.
142 * @param string A string that begins with
143 * <code>[</code> <small>(left bracket)</small>
144 * and ends with <code>]</code> <small>(right bracket)</small>.
145 * @throws JSONException If there is a syntax error.
147 public JSONArray(String string
) throws JSONException
{
148 this(new JSONTokener(string
));
153 * Construct a JSONArray from a Collection.
155 * @param collection A Collection.
157 public JSONArray(Collection collection
) {
158 this.myArrayList
= new ArrayList(collection
);
163 * Get the object value associated with an index.
165 * @param index The index must be between 0 and length() - 1.
166 * @return An object value.
167 * @throws JSONException If there is no value for the index.
169 public Object
get(int index
) {
170 Object o
= opt(index
);
172 throw new UnhandledException(new JSONException("JSONArray[" + index
+ "] not found."));
177 public Object
set(int i
, Object o
) {
178 return myArrayList
.set(i
, o
);
181 public boolean add(Object o
) {
182 return myArrayList
.add(o
);
185 public void add(int i
, Object o
) {
186 myArrayList
.add(i
, o
);
189 public Object
remove(int i
) {
190 return myArrayList
.remove(i
);
193 public boolean remove(Object o
) {
194 return myArrayList
.remove(o
);
197 public void clear() {
201 public boolean addAll(Collection collection
) {
202 return myArrayList
.addAll(collection
);
205 public boolean addAll(int i
, Collection collection
) {
206 return myArrayList
.addAll(i
, collection
);
209 public Iterator
iterator() {
210 return myArrayList
.iterator();
213 public ListIterator
listIterator() {
214 return myArrayList
.listIterator();
217 public ListIterator
listIterator(int i
) {
218 return myArrayList
.listIterator(i
);
221 public List
subList(int i
, int i1
) {
222 return myArrayList
.subList(i
, i1
);
225 public boolean containsAll(Collection collection
) {
226 return myArrayList
.containsAll(collection
);
229 public boolean removeAll(Collection collection
) {
230 return myArrayList
.removeAll(collection
);
233 public boolean retainAll(Collection collection
) {
234 return myArrayList
.retainAll(collection
);
239 * Get the boolean value associated with an index.
240 * The string values "true" and "false" are converted to boolean.
242 * @param index The index must be between 0 and length() - 1.
244 * @throws JSONException If there is no value for the index or if the
245 * value is not convertable to boolean.
247 public boolean getBoolean(int index
) throws JSONException
{
248 Object o
= get(index
);
249 if (o
.equals(Boolean
.FALSE
) ||
250 (o
instanceof String
&&
251 ((String
) o
).equalsIgnoreCase("false"))) {
253 } else if (o
.equals(Boolean
.TRUE
) ||
254 (o
instanceof String
&&
255 ((String
) o
).equalsIgnoreCase("true"))) {
258 throw new JSONException("JSONArray[" + index
+ "] is not a Boolean.");
263 * Get the double value associated with an index.
265 * @param index The index must be between 0 and length() - 1.
267 * @throws JSONException If the key is not found or if the value cannot
268 * be converted to a number.
270 public double getDouble(int index
) throws JSONException
{
271 Object o
= get(index
);
273 return o
instanceof Number ?
274 ((Number
) o
).doubleValue() : Double
.parseDouble((String
) o
);
275 } catch (Exception e
) {
276 throw new JSONException("JSONArray[" + index
+
277 "] is not a number.");
283 * Get the int value associated with an index.
285 * @param index The index must be between 0 and length() - 1.
287 * @throws JSONException If the key is not found or if the value cannot
288 * be converted to a number.
289 * if the value cannot be converted to a number.
291 public int getInt(int index
) throws JSONException
{
292 Object o
= get(index
);
293 return o
instanceof Number ?
294 ((Number
) o
).intValue() : (int) getDouble(index
);
299 * Get the JSONArray associated with an index.
301 * @param index The index must be between 0 and length() - 1.
302 * @return A JSONArray value.
303 * @throws JSONException If there is no value for the index. or if the
304 * value is not a JSONArray
306 public JSONArray
getJSONArray(int index
) throws JSONException
{
307 Object o
= get(index
);
308 if (o
instanceof JSONArray
) {
309 return (JSONArray
) o
;
311 throw new JSONException("JSONArray[" + index
+
312 "] is not a JSONArray.");
317 * Get the JSONObject associated with an index.
319 * @param index subscript
320 * @return A JSONObject value.
321 * @throws JSONException If there is no value for the index or if the
322 * value is not a JSONObject
324 public JSONObject
getJSONObject(int index
) throws JSONException
{
325 Object o
= get(index
);
326 if (o
instanceof JSONObject
) {
327 return (JSONObject
) o
;
329 throw new JSONException("JSONArray[" + index
+
330 "] is not a JSONObject.");
335 * Get the long value associated with an index.
337 * @param index The index must be between 0 and length() - 1.
339 * @throws JSONException If the key is not found or if the value cannot
340 * be converted to a number.
342 public long getLong(int index
) throws JSONException
{
343 Object o
= get(index
);
344 return o
instanceof Number ?
345 ((Number
) o
).longValue() : (long) getDouble(index
);
350 * Get the string associated with an index.
352 * @param index The index must be between 0 and length() - 1.
353 * @return A string value.
354 * @throws JSONException If there is no value for the index.
356 public String
getString(int index
) throws JSONException
{
357 return get(index
).toString();
362 * Determine if the value is null.
364 * @param index The index must be between 0 and length() - 1.
365 * @return true if the value at the index is null, or if there is no value.
367 public boolean isNull(int index
) {
368 return JSONObject
.NULL
.equals(opt(index
));
373 * Make a string from the contents of this JSONArray. The
374 * <code>separator</code> string is inserted between each element.
375 * Warning: This method assumes that the data structure is acyclical.
377 * @param separator A string that will be inserted between the elements.
379 * @throws JSONException If the array contains an invalid number.
381 public String
join(String separator
) throws JSONException
{
383 StringBuffer sb
= new StringBuffer();
385 for (int i
= 0; i
< len
; i
+= 1) {
387 sb
.append(separator
);
389 sb
.append(JSONObject
.valueToString(this.myArrayList
.get(i
)));
391 return sb
.toString();
396 * Get the number of elements in the JSONArray, included nulls.
398 * @return The length (or size).
400 public int length() {
401 return this.myArrayList
.size();
406 * Get the optional object value associated with an index.
408 * @param index The index must be between 0 and length() - 1.
409 * @return An object value, or null if there is no
410 * object at that index.
412 public Object
opt(int index
) {
413 return (index
< 0 || index
>= length()) ?
414 null : this.myArrayList
.get(index
);
419 * Get the optional boolean value associated with an index.
420 * It returns false if there is no value at that index,
421 * or if the value is not Boolean.TRUE or the String "true".
423 * @param index The index must be between 0 and length() - 1.
426 public boolean optBoolean(int index
) {
427 return optBoolean(index
, false);
432 * Get the optional boolean value associated with an index.
433 * It returns the defaultValue if there is no value at that index or if
434 * it is not a Boolean or the String "true" or "false" (case insensitive).
436 * @param index The index must be between 0 and length() - 1.
437 * @param defaultValue A boolean default.
440 public boolean optBoolean(int index
, boolean defaultValue
) {
442 return getBoolean(index
);
443 } catch (Exception e
) {
450 * Get the optional double value associated with an index.
451 * NaN is returned if there is no value for the index,
452 * or if the value is not a number and cannot be converted to a number.
454 * @param index The index must be between 0 and length() - 1.
457 public double optDouble(int index
) {
458 return optDouble(index
, Double
.NaN
);
463 * Get the optional double value associated with an index.
464 * The defaultValue is returned if there is no value for the index,
465 * or if the value is not a number and cannot be converted to a number.
467 * @param index subscript
468 * @param defaultValue The default value.
471 public double optDouble(int index
, double defaultValue
) {
473 return getDouble(index
);
474 } catch (Exception e
) {
481 * Get the optional int value associated with an index.
482 * Zero is returned if there is no value for the index,
483 * or if the value is not a number and cannot be converted to a number.
485 * @param index The index must be between 0 and length() - 1.
488 public int optInt(int index
) {
489 return optInt(index
, 0);
494 * Get the optional int value associated with an index.
495 * The defaultValue is returned if there is no value for the index,
496 * or if the value is not a number and cannot be converted to a number.
498 * @param index The index must be between 0 and length() - 1.
499 * @param defaultValue The default value.
502 public int optInt(int index
, int defaultValue
) {
504 return getInt(index
);
505 } catch (Exception e
) {
512 * Get the optional JSONArray associated with an index.
514 * @param index subscript
515 * @return A JSONArray value, or null if the index has no value,
516 * or if the value is not a JSONArray.
518 public JSONArray
optJSONArray(int index
) {
519 Object o
= opt(index
);
520 return o
instanceof JSONArray ?
(JSONArray
) o
: null;
525 * Get the optional JSONObject associated with an index.
526 * Null is returned if the key is not found, or null if the index has
527 * no value, or if the value is not a JSONObject.
529 * @param index The index must be between 0 and length() - 1.
530 * @return A JSONObject value.
532 public JSONObject
optJSONObject(int index
) {
533 Object o
= opt(index
);
534 return o
instanceof JSONObject ?
(JSONObject
) o
: null;
539 * Get the optional long value associated with an index.
540 * Zero is returned if there is no value for the index,
541 * or if the value is not a number and cannot be converted to a number.
543 * @param index The index must be between 0 and length() - 1.
546 public long optLong(int index
) {
547 return optLong(index
, 0);
552 * Get the optional long value associated with an index.
553 * The defaultValue is returned if there is no value for the index,
554 * or if the value is not a number and cannot be converted to a number.
556 * @param index The index must be between 0 and length() - 1.
557 * @param defaultValue The default value.
560 public long optLong(int index
, long defaultValue
) {
562 return getLong(index
);
563 } catch (Exception e
) {
570 * Get the optional string value associated with an index. It returns an
571 * empty string if there is no value at that index. If the value
572 * is not a string and is not null, then it is coverted to a string.
574 * @param index The index must be between 0 and length() - 1.
575 * @return A String value.
577 public String
optString(int index
) {
578 return optString(index
, "");
583 * Get the optional string associated with an index.
584 * The defaultValue is returned if the key is not found.
586 * @param index The index must be between 0 and length() - 1.
587 * @param defaultValue The default value.
588 * @return A String value.
590 public String
optString(int index
, String defaultValue
) {
591 Object o
= opt(index
);
592 return o
!= null ? o
.toString() : defaultValue
;
597 * Append a boolean value. This increases the array's length by one.
599 * @param value A boolean value.
602 public JSONArray
put(boolean value
) {
603 put(value ? Boolean
.TRUE
: Boolean
.FALSE
);
609 * Append a double value. This increases the array's length by one.
611 * @param value A double value.
613 * @throws JSONException if the value is not finite.
615 public JSONArray
put(double value
) throws JSONException
{
616 Double d
= new Double(value
);
617 JSONObject
.testValidity(d
);
624 * Append an int value. This increases the array's length by one.
626 * @param value An int value.
629 public JSONArray
put(int value
) {
630 put(new Integer(value
));
636 * Append an long value. This increases the array's length by one.
638 * @param value A long value.
641 public JSONArray
put(long value
) {
642 put(new Long(value
));
648 * Append an object value. This increases the array's length by one.
650 * @param value An object value. The value should be a
651 * Boolean, Double, Integer, JSONArray, JSObject, Long, or String, or the
652 * JSONObject.NULL object.
655 public JSONArray
put(Object value
) {
656 this.myArrayList
.add(value
);
662 * Put or replace a boolean value in the JSONArray. If the index is greater
663 * than the length of the JSONArray, then null elements will be added as
664 * necessary to pad it out.
666 * @param index The subscript.
667 * @param value A boolean value.
669 * @throws JSONException If the index is negative.
671 public JSONArray
put(int index
, boolean value
) throws JSONException
{
672 put(index
, value ? Boolean
.TRUE
: Boolean
.FALSE
);
678 * Put or replace a double value. If the index is greater than the length of
679 * the JSONArray, then null elements will be added as necessary to pad
682 * @param index The subscript.
683 * @param value A double value.
685 * @throws JSONException If the index is negative or if the value is
688 public JSONArray
put(int index
, double value
) throws JSONException
{
689 put(index
, new Double(value
));
695 * Put or replace an int value. If the index is greater than the length of
696 * the JSONArray, then null elements will be added as necessary to pad
699 * @param index The subscript.
700 * @param value An int value.
702 * @throws JSONException If the index is negative.
704 public JSONArray
put(int index
, int value
) throws JSONException
{
705 put(index
, new Integer(value
));
711 * Put or replace a long value. If the index is greater than the length of
712 * the JSONArray, then null elements will be added as necessary to pad
715 * @param index The subscript.
716 * @param value A long value.
718 * @throws JSONException If the index is negative.
720 public JSONArray
put(int index
, long value
) throws JSONException
{
721 put(index
, new Long(value
));
727 * Put or replace an object value in the JSONArray. If the index is greater
728 * than the length of the JSONArray, then null elements will be added as
729 * necessary to pad it out.
731 * @param index The subscript.
732 * @param value The value to put into the array.
734 * @throws JSONException If the index is negative or if the the value is
737 public JSONArray
put(int index
, Object value
) throws JSONException
{
738 JSONObject
.testValidity(value
);
740 throw new JSONException("JSONArray[" + index
+ "] not found.");
742 if (index
< length()) {
743 this.myArrayList
.set(index
, value
);
745 while (index
!= length()) {
755 * Produce a JSONObject by combining a JSONArray of names with the values
758 * @param names A JSONArray containing a list of key strings. These will be
759 * paired with the values.
760 * @return A JSONObject, or null if there are no names or if this JSONArray
762 * @throws JSONException If any of the names are null.
764 public JSONObject
toJSONObject(JSONArray names
) throws JSONException
{
765 if (names
== null || names
.length() == 0 || length() == 0) {
768 JSONObject jo
= new JSONObject();
769 for (int i
= 0; i
< names
.length(); i
+= 1) {
770 jo
.put(names
.getString(i
), this.opt(i
));
777 * Make an JSON text of this JSONArray. For compactness, no
778 * unnecessary whitespace is added. If it is not possible to produce a
779 * syntactically correct JSON text then null will be returned instead. This
780 * could occur if the array contains an invalid number.
782 * Warning: This method assumes that the data structure is acyclical.
784 * @return a printable, displayable, transmittable
785 * representation of the array.
787 public String
toString() {
789 return '[' + join(",") + ']';
790 } catch (Exception e
) {
797 * Make a prettyprinted JSON text of this JSONArray.
798 * Warning: This method assumes that the data structure is acyclical.
800 * @param indentFactor The number of spaces to add to each level of
802 * @return a printable, displayable, transmittable
803 * representation of the object, beginning
804 * with <code>[</code> <small>(left bracket)</small> and ending
805 * with <code>]</code> <small>(right bracket)</small>.
806 * @throws JSONException
808 public String
toString(int indentFactor
) throws JSONException
{
809 return toString(indentFactor
, 0);
814 * Make a prettyprinted JSON text of this JSONArray.
815 * Warning: This method assumes that the data structure is acyclical.
817 * @param indentFactor The number of spaces to add to each level of
819 * @param indent The indention of the top level.
820 * @return a printable, displayable, transmittable
821 * representation of the array.
822 * @throws JSONException
824 String
toString(int indentFactor
, int indent
) throws JSONException
{
830 StringBuffer sb
= new StringBuffer("[");
832 sb
.append(JSONObject
.valueToString(this.myArrayList
.get(0),
833 indentFactor
, indent
));
835 int newindent
= indent
+ indentFactor
;
837 for (i
= 0; i
< len
; i
+= 1) {
841 for (int j
= 0; j
< newindent
; j
+= 1) {
844 sb
.append(JSONObject
.valueToString(this.myArrayList
.get(i
),
845 indentFactor
, newindent
));
848 for (i
= 0; i
< indent
; i
+= 1) {
853 return sb
.toString();
858 * Write the contents of the JSONArray as JSON text to a writer.
859 * For compactness, no whitespace is added.
861 * Warning: This method assumes that the data structure is acyclical.
863 * @return The writer.
864 * @throws JSONException
866 public Writer
write(Writer writer
) throws JSONException
{
873 for (int i
= 0; i
< len
; i
+= 1) {
877 Object v
= this.myArrayList
.get(i
);
878 if (v
instanceof JSONObject
) {
879 ((JSONObject
) v
).write(writer
);
880 } else if (v
instanceof JSONArray
) {
881 ((JSONArray
) v
).write(writer
);
883 writer
.write(JSONObject
.valueToString(v
));
889 } catch (IOException e
) {
890 throw new JSONException(e
);
894 public void trimToSize() {
895 myArrayList
.trimToSize();
898 public void ensureCapacity(int i
) {
899 myArrayList
.ensureCapacity(i
);
903 return myArrayList
.size();
906 public boolean isEmpty() {
907 return myArrayList
.isEmpty();
910 public boolean contains(Object o
) {
911 return myArrayList
.contains(o
);
914 public int indexOf(Object o
) {
915 return myArrayList
.indexOf(o
);
918 public int lastIndexOf(Object o
) {
919 return myArrayList
.lastIndexOf(o
);
922 public Object
clone() {
923 return myArrayList
.clone();
926 public Object
[] toArray() {
927 return myArrayList
.toArray();
930 public Object
[] toArray(Object
[] objects
) {
931 return myArrayList
.toArray(objects
);