151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson/* 251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Copyright (C) 2010 The Android Open Source Project 351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * 451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License"); 551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * you may not use this file except in compliance with the License. 651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * You may obtain a copy of the License at 751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * 851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0 951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * 1051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Unless required by applicable law or agreed to in writing, software 1151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS, 1251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * See the License for the specific language governing permissions and 1451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * limitations under the License. 1551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 1651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 1751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilsonpackage org.json; 1851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 1951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilsonimport java.util.ArrayList; 2051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilsonimport java.util.Arrays; 2151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilsonimport java.util.List; 2251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 2351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson// Note: this class was written without inspecting the non-free org.json sourcecode. 2451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 2551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson/** 2695cf9534436835291927cb4bcb36842731319da8Jesse Wilson * Implements {@link JSONObject#toString} and {@link JSONArray#toString}. Most 27c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * application developers should use those methods directly and disregard this 28c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * API. For example:<pre> 29c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * JSONObject object = ... 30c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * String json = object.toString();</pre> 3151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * 32c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <p>Stringers only encode well-formed JSON strings. In particular: 33c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <ul> 34c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <li>The stringer must have exactly one top-level array or object. 3595cf9534436835291927cb4bcb36842731319da8Jesse Wilson * <li>Lexical scopes must be balanced: every call to {@link #array} must 3695cf9534436835291927cb4bcb36842731319da8Jesse Wilson * have a matching call to {@link #endArray} and every call to {@link 3795cf9534436835291927cb4bcb36842731319da8Jesse Wilson * #object} must have a matching call to {@link #endObject}. 38c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <li>Arrays may not contain keys (property names). 39c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <li>Objects must alternate keys (property names) and values. 40c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <li>Values are inserted with either literal {@link #value(Object) value} 41c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * calls, or by nesting arrays or objects. 42c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * </ul> 43c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Calls that would result in a malformed JSON string will fail with a 44c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * {@link JSONException}. 45c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 46c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <p>This class provides no facility for pretty-printing (ie. indenting) 47c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * output. To encode indented output, use {@link JSONObject#toString(int)} or 48c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * {@link JSONArray#toString(int)}. 49c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 50c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <p>Some implementations of the API support at most 20 levels of nesting. 51c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Attempts to create more than 20 levels of nesting may fail with a {@link 52c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * JSONException}. 53c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 54c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <p>Each stringer may be used to encode a single top level value. Instances of 55c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * this class are not thread safe. Although this class is nonfinal, it was not 56c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * designed for inheritance and should not be subclassed. In particular, 57661054f5a2f7f8f5f3ceffb97e803211b546e7fcJesse Wilson * self-use by overrideable methods is not specified. See <i>Effective Java</i> 58c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Item 17, "Design and Document or inheritance or else prohibit it" for further 59c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * information. 6051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 6151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilsonpublic class JSONStringer { 6251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 6351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** The output data, containing at most one top-level array or object. */ 6451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson final StringBuilder out = new StringBuilder(); 6551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 6651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 6751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Lexical scoping elements within this stringer, necessary to insert the 6851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * appropriate separator characters (ie. commas and colons) and to detect 6951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * nesting errors. 7051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 7151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson enum Scope { 7251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 7351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 7451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * An array with no elements requires no separators or newlines before 7551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * it is closed. 7651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 7751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson EMPTY_ARRAY, 7851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 7951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 8051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * A array with at least one value requires a comma and newline before 8151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * the next element. 8251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 8351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson NONEMPTY_ARRAY, 8451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 8551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 8651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * An object with no keys or values requires no separators or newlines 8751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * before it is closed. 8851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 8951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson EMPTY_OBJECT, 9051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 9151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 9251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * An object whose most recent element is a key. The next element must 9351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * be a value. 9451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 9551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson DANGLING_KEY, 9651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 9751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 9851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * An object with at least one name/value pair requires a comma and 9951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * newline before the next element. 10051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 10151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson NONEMPTY_OBJECT, 10251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 10351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 10451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * A special bracketless array needed by JSONStringer.join() and 10551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * JSONObject.quote() only. Not used for JSON encoding. 10651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 10751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson NULL, 10851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 10951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 11051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 11151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Unlike the original implementation, this stack isn't limited to 20 11251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * levels of nesting. 11351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 11451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson private final List<Scope> stack = new ArrayList<Scope>(); 11551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 11651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 11751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * A string containing a full set of spaces for a single level of 11851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * indentation, or null for no pretty printing. 11951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 12051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson private final String indent; 12151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 12251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson public JSONStringer() { 12351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson indent = null; 12451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 12551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 12651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson JSONStringer(int indentSpaces) { 12751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson char[] indentChars = new char[indentSpaces]; 12851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson Arrays.fill(indentChars, ' '); 12951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson indent = new String(indentChars); 13051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 13151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 132c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson /** 133c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Begins encoding a new array. Each call to this method must be paired with 13495cf9534436835291927cb4bcb36842731319da8Jesse Wilson * a call to {@link #endArray}. 135c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 136c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @return this stringer. 137c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson */ 13851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson public JSONStringer array() throws JSONException { 13951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return open(Scope.EMPTY_ARRAY, "["); 14051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 14151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 142c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson /** 143c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Ends encoding the current array. 144c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 145c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @return this stringer. 146c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson */ 14751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson public JSONStringer endArray() throws JSONException { 14851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return close(Scope.EMPTY_ARRAY, Scope.NONEMPTY_ARRAY, "]"); 14951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 15051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 151c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson /** 152c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Begins encoding a new object. Each call to this method must be paired 15395cf9534436835291927cb4bcb36842731319da8Jesse Wilson * with a call to {@link #endObject}. 154c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 155c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @return this stringer. 156c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson */ 15751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson public JSONStringer object() throws JSONException { 15851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return open(Scope.EMPTY_OBJECT, "{"); 15951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 16051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 161c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson /** 162c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Ends encoding the current object. 163c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 164c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @return this stringer. 165c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson */ 16651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson public JSONStringer endObject() throws JSONException { 16751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}"); 16851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 16951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 17051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 17151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Enters a new scope by appending any necessary whitespace and the given 17251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * bracket. 17351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 17451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson JSONStringer open(Scope empty, String openBracket) throws JSONException { 17551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (stack.isEmpty() && out.length() > 0) { 17651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson throw new JSONException("Nesting problem: multiple top-level roots"); 17751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 17851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson beforeValue(); 17951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson stack.add(empty); 18051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(openBracket); 18151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return this; 18251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 18351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 18451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 18551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Closes the current scope by appending any necessary whitespace and the 18651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * given bracket. 18751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 18851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException { 18951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson Scope context = peek(); 19051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (context != nonempty && context != empty) { 19151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson throw new JSONException("Nesting problem"); 19251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 19351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 19451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson stack.remove(stack.size() - 1); 19551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (context == nonempty) { 19651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson newline(); 19751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 19851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(closeBracket); 19951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return this; 20051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 20151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 20251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 20351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Returns the value on the top of the stack. 20451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 20551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson private Scope peek() throws JSONException { 20651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (stack.isEmpty()) { 20751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson throw new JSONException("Nesting problem"); 20851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 20951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return stack.get(stack.size() - 1); 21051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 21151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 21251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 21351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Replace the value on the top of the stack with the given value. 21451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 21551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson private void replaceTop(Scope topOfStack) { 21651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson stack.set(stack.size() - 1, topOfStack); 21751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 21851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 219c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson /** 220c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Encodes {@code value}. 221c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 222c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, 223c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Integer, Long, Double or null. May not be {@link Double#isNaN() NaNs} 224c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * or {@link Double#isInfinite() infinities}. 225c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @return this stringer. 226c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson */ 22751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson public JSONStringer value(Object value) throws JSONException { 22851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (stack.isEmpty()) { 22951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson throw new JSONException("Nesting problem"); 23051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 23151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 23251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (value instanceof JSONArray) { 23351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson ((JSONArray) value).writeTo(this); 23451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return this; 23551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 23651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } else if (value instanceof JSONObject) { 23751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson ((JSONObject) value).writeTo(this); 23851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return this; 23951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 24051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 24151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson beforeValue(); 24251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 24351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (value == null 24451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson || value instanceof Boolean 24551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson || value == JSONObject.NULL) { 24651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(value); 24751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 24851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } else if (value instanceof Number) { 24951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(JSONObject.numberToString((Number) value)); 25051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 25151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } else { 25251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson string(value.toString()); 25351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 25451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 25551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return this; 25651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 25751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 258c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson /** 259c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Encodes {@code value} to this stringer. 260c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 261c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @return this stringer. 262c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson */ 26351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson public JSONStringer value(boolean value) throws JSONException { 26451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (stack.isEmpty()) { 26551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson throw new JSONException("Nesting problem"); 26651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 26751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson beforeValue(); 26851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(value); 26951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return this; 27051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 27151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 272c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson /** 273c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Encodes {@code value} to this stringer. 274c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 275c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @param value a finite value. May not be {@link Double#isNaN() NaNs} or 276c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * {@link Double#isInfinite() infinities}. 277c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @return this stringer. 278c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson */ 27951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson public JSONStringer value(double value) throws JSONException { 28051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (stack.isEmpty()) { 28151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson throw new JSONException("Nesting problem"); 28251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 28351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson beforeValue(); 28451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(JSONObject.numberToString(value)); 28551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return this; 28651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 28751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 288c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson /** 289c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Encodes {@code value} to this stringer. 290c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 291c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @return this stringer. 292c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson */ 29351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson public JSONStringer value(long value) throws JSONException { 29451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (stack.isEmpty()) { 29551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson throw new JSONException("Nesting problem"); 29651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 29751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson beforeValue(); 29851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(value); 29951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return this; 30051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 30151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 30251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson private void string(String value) { 30351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append("\""); 30451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson for (int i = 0, length = value.length(); i < length; i++) { 30551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson char c = value.charAt(i); 30651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 30751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /* 30851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * From RFC 4627, "All Unicode characters may be placed within the 30951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * quotation marks except for the characters that must be escaped: 31051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * quotation mark, reverse solidus, and the control characters 31151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * (U+0000 through U+001F)." 31251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 31351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson switch (c) { 31451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson case '"': 31551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson case '\\': 31651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson case '/': 31751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append('\\').append(c); 31851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson break; 31951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 32051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson case '\t': 32151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append("\\t"); 32251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson break; 32351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 32451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson case '\b': 32551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append("\\b"); 32651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson break; 32751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 32851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson case '\n': 32951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append("\\n"); 33051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson break; 33151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 33251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson case '\r': 33351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append("\\r"); 33451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson break; 33551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 33651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson case '\f': 33751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append("\\f"); 33851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson break; 33951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 34051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson default: 34151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (c <= 0x1F) { 34251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(String.format("\\u%04x", (int) c)); 34351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } else { 34451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(c); 34551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 34651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson break; 34751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 34851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 34951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 35051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append("\""); 35151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 35251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 35351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson private void newline() { 35451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (indent == null) { 35551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return; 35651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 35751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 35851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append("\n"); 35951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson for (int i = 0; i < stack.size(); i++) { 36051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(indent); 36151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 36251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 36351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 364c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson /** 365c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Encodes the key (property name) to this stringer. 366c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 367c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @param name the name of the forthcoming value. May not be null. 368c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * @return this stringer. 369c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson */ 37051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson public JSONStringer key(String name) throws JSONException { 37151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (name == null) { 37251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson throw new JSONException("Names must be non-null"); 37351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 37451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson beforeKey(); 37551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson string(name); 37651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return this; 37751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 37851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 37951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 38051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Inserts any necessary separators and whitespace before a name. Also 38151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * adjusts the stack to expect the key's value. 38251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 38351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson private void beforeKey() throws JSONException { 38451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson Scope context = peek(); 38551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (context == Scope.NONEMPTY_OBJECT) { // first in object 38651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(','); 38751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } else if (context != Scope.EMPTY_OBJECT) { // not in an object! 38851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson throw new JSONException("Nesting problem"); 38951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 39051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson newline(); 39151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson replaceTop(Scope.DANGLING_KEY); 39251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 39351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 39451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 39551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * Inserts any necessary separators and whitespace before a literal value, 39651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * inline array, or inline object. Also adjusts the stack to expect either a 39751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson * closing bracket or another element. 39851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 39951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson private void beforeValue() throws JSONException { 40051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (stack.isEmpty()) { 40151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return; 40251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 40351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 40451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson Scope context = peek(); 40551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson if (context == Scope.EMPTY_ARRAY) { // first in array 40651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson replaceTop(Scope.NONEMPTY_ARRAY); 40751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson newline(); 40851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } else if (context == Scope.NONEMPTY_ARRAY) { // another in array 40951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(','); 41051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson newline(); 41151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } else if (context == Scope.DANGLING_KEY) { // value for key 41251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson out.append(indent == null ? ":" : ": "); 41351a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson replaceTop(Scope.NONEMPTY_OBJECT); 41451a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } else if (context != Scope.NULL) { 41551a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson throw new JSONException("Nesting problem"); 41651a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 41751a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 41851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson 41951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson /** 420c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * Returns the encoded JSON string. 421c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 422c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <p>If invoked with unterminated arrays or unclosed objects, this method's 423c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * return value is undefined. 424c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * 425c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * <p><strong>Warning:</strong> although it contradicts the general contract 426c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * of {@link Object#toString}, this method returns null if the stringer 427c78ca6700830460f671652ce3efd4cc2c0cf7a6aJesse Wilson * contains no data. 42851a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson */ 42951a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson @Override public String toString() { 43051a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson return out.length() == 0 ? null : out.toString(); 43151a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson } 43251a095f0bc7aadfcc7e6b3873b97c050c523d102Jesse Wilson} 433