JsonWriter.java revision 76d7e20a75d24afb2f5011a7646dbde8aaa52087
176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson/* 276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Copyright (C) 2010 The Android Open Source Project 376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License"); 576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * you may not use this file except in compliance with the License. 676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * You may obtain a copy of the License at 776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0 976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 1076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Unless required by applicable law or agreed to in writing, software 1176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS, 1276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * See the License for the specific language governing permissions and 1476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * limitations under the License. 1576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 1676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 1776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonpackage android.util; 1876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 1976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.io.IOException; 2076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.io.Writer; 2176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.util.ArrayList; 2276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.util.Arrays; 2376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.util.List; 2476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 2576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson/** 2676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Writes a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>) 2776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * encoded value to a stream, one token at a time. The stream includes both 2876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * literal values (strings, numbers, booleans and nulls) as well as the begin 2976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * and end delimiters of objects and arrays. 3076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 3176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <h3>Encoding JSON</h3> 3276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * To encode your data as JSON, create a new {@code JsonWriter}. Each JSON 3376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * document must contain one top-level array or object. Call methods on the 3476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer as you walk the structure's contents, nesting arrays and objects as 3576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * necessary: 3676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <ul> 3776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <li>To write <strong>arrays</strong>, first call {@link #beginArray()}. 3876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Write each of the array's elements with the appropriate {@link #value} 3976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * methods or by nesting other arrays and objects. Finally close the array 4076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * using {@link #endArray()}. 4176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <li>To write <strong>objects</strong>, first call {@link #beginObject()}. 4276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Write each of the object's properties by alternating calls to 4376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * {@link #name} with the property's value. Write property values with the 4476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * appropriate {@link #value} method or by nesting other objects or arrays. 4576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Finally close the object using {@link #endObject()}. 4676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * </ul> 4776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 4876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <h3>Example</h3> 4976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Suppose we'd like to encode a stream of messages such as the following: <pre> {@code 5076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * [ 5176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * { 5276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "id": 912345678901, 5376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "text": "How do I write JSON on Android?", 5476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "geo": null, 5576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "user": { 5676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "name": "android_newb", 5776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "followers_count": 41 5876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 5976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * }, 6076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * { 6176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "id": 912345678902, 6276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "text": "@android_newb just use android.util.JsonWriter!", 6376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "geo": [50.454722, -104.606667], 6476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "user": { 6576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "name": "jesse", 6676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * "followers_count": 2 6776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 6876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 6976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * ]}</pre> 7076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * This code encodes the above structure: <pre> {@code 7176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException { 7276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8")); 7376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.setIndentSpaces(4); 7476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writeMessagesArray(writer, messages); 7576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.close(); 7676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 7776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 7876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException { 7976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.beginArray(); 8076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * for (Message message : messages) { 8176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writeMessage(writer, message); 8276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 8376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.endArray(); 8476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 8576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 8676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * public void writeMessage(JsonWriter writer, Message message) throws IOException { 8776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.beginObject(); 8876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.name("id").value(message.getId()); 8976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.name("text").value(message.getText()); 9076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * if (message.getGeo() != null) { 9176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.name("geo"); 9276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writeDoublesArray(writer, message.getGeo()); 9376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } else { 9476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.name("geo").nullValue(); 9576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 9676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.name("user"); 9776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writeUser(writer, message.getUser()); 9876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.endObject(); 9976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 10076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 10176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * public void writeUser(JsonWriter writer, User user) throws IOException { 10276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.beginObject(); 10376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.name("name").value(user.getName()); 10476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.name("followers_count").value(user.getFollowersCount()); 10576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.endObject(); 10676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 10776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 10876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException { 10976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.beginArray(); 11076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * for (Double value : doubles) { 11176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.value(value); 11276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * } 11376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * writer.endArray(); 11476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * }}</pre> 11576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 11676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * <p>Each {@code JsonWriter} may be used to write a single JSON stream. 11776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Instances of this class are not thread safe. Calls that would result in a 11876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * malformed JSON string will fail with an {@link IllegalStateException}. 11976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 12076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonpublic final class JsonWriter { 12176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 12276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** The output data, containing at most one top-level array or object. */ 12376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private final Writer out; 12476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 12576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private final List<JsonScope> stack = new ArrayList<JsonScope>(); 12676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson { 12776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.add(JsonScope.EMPTY_DOCUMENT); 12876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 12976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 13076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 13176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * A string containing a full set of spaces for a single level of 13276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * indentation, or null for no pretty printing. 13376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 13476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private String indent; 13576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 13676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 13776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * The name/value separator; either ":" or ": ". 13876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 13976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private String separator = ":"; 14076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 14176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 14276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Creates a new instance that writes a JSON-encoded stream to {@code out}. 14376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * For best performance, ensure {@link Writer} is buffered; wrapping in 14476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * {@link java.io.BufferedWriter BufferedWriter} if necessary. 14576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 14676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter(Writer out) { 14776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (out == null) { 14876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new NullPointerException("out == null"); 14976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 15076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson this.out = out; 15176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 15276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 15376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 15476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Sets the number of spaces to indent each line in the encoded document. 15576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * If {@code indent == 0} the encoded document will be compact. If {@code 15676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * indent > 0}, the encoded document will be more human-readable. 15776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 15876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void setIndentSpaces(int indent) { 15976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (indent < 0) { 16076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalArgumentException("indent < 0"); 16176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 16276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 16376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (indent > 0) { 16476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson char[] indentChars = new char[indent]; 16576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson Arrays.fill(indentChars, ' '); 16676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson this.indent = new String(indentChars); 16776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson this.separator = ": "; 16876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else { 16976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson this.indent = null; 17076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson this.separator = ":"; 17176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 17276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 17376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 17476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 17576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Begins encoding a new array. Each call to this method must be paired with 17676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * a call to {@link #endArray}. 17776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 17876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 17976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 18076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter beginArray() throws IOException { 18176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return open(JsonScope.EMPTY_ARRAY, "["); 18276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 18376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 18476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 18576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Ends encoding the current array. 18676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 18776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 18876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 18976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter endArray() throws IOException { 19076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return close(JsonScope.EMPTY_ARRAY, JsonScope.NONEMPTY_ARRAY, "]"); 19176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 19276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 19376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 19476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Begins encoding a new object. Each call to this method must be paired 19576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * with a call to {@link #endObject}. 19676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 19776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 19876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 19976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter beginObject() throws IOException { 20076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return open(JsonScope.EMPTY_OBJECT, "{"); 20176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 20276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 20376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 20476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Ends encoding the current object. 20576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 20676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 20776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 20876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter endObject() throws IOException { 20976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return close(JsonScope.EMPTY_OBJECT, JsonScope.NONEMPTY_OBJECT, "}"); 21076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 21176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 21276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 21376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Enters a new scope by appending any necessary whitespace and the given 21476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * bracket. 21576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 21676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonWriter open(JsonScope empty, String openBracket) throws IOException { 21776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(true); 21876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.add(empty); 21976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(openBracket); 22076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 22176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 22276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 22376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 22476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Closes the current scope by appending any necessary whitespace and the 22576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * given bracket. 22676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 22776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonWriter close(JsonScope empty, JsonScope nonempty, String closeBracket) 22876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throws IOException { 22976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson JsonScope context = peek(); 23076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (context != nonempty && context != empty) { 23176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException("Nesting problem: " + stack); 23276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 23376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 23476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.remove(stack.size() - 1); 23576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (context == nonempty) { 23676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson newline(); 23776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 23876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(closeBracket); 23976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 24076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 24176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 24276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 24376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns the value on the top of the stack. 24476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 24576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonScope peek() { 24676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return stack.get(stack.size() - 1); 24776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 24876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 24976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 25076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Replace the value on the top of the stack with the given value. 25176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 25276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void replaceTop(JsonScope topOfStack) { 25376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.set(stack.size() - 1, topOfStack); 25476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 25576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 25676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 25776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes the property name. 25876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 25976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @param name the name of the forthcoming value. May not be null. 26076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 26176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 26276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter name(String name) throws IOException { 26376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (name == null) { 26476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new NullPointerException("name == null"); 26576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 26676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeName(); 26776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson string(name); 26876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 26976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 27076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 27176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 27276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes {@code value}. 27376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 27476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @param value the literal string value, or null to encode a null literal. 27576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 27676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 27776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter value(String value) throws IOException { 27876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (value == null) { 27976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return nullValue(); 28076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 28176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(false); 28276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson string(value); 28376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 28476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 28576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 28676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 28776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes {@code null}. 28876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 28976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 29076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 29176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter nullValue() throws IOException { 29276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(false); 29376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("null"); 29476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 29576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 29676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 29776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 29876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes {@code value}. 29976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 30076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 30176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 30276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter value(boolean value) throws IOException { 30376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(false); 30476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(value ? "true" : "false"); 30576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 30676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 30776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 30876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 30976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes {@code value}. 31076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 31176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @param value a finite value. May not be {@link Double#isNaN() NaNs} or 31276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * {@link Double#isInfinite() infinities}. 31376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 31476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 31576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter value(double value) throws IOException { 31676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (Double.isNaN(value) || Double.isInfinite(value)) { 31776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalArgumentException("Numeric values must be finite, but was " + value); 31876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 31976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(false); 32076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.append(Double.toString(value)); 32176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 32276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 32376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 32476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 32576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes {@code value}. 32676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 32776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 32876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 32976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter value(long value) throws IOException { 33076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(false); 33176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(Long.toString(value)); 33276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 33376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 33476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 33576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 33676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Ensures all buffered data is written to the underlying {@link Writer} 33776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * and flushes that writer. 33876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 33976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void flush() throws IOException { 34076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.flush(); 34176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 34276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 34376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 34476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Flushes and closes this writer and the underlying {@link Writer}. 34576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 34676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws IOException if the JSON document is incomplete. 34776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 34876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void close() throws IOException { 34976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.close(); 35076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 35176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (peek() != JsonScope.NONEMPTY_DOCUMENT) { 35276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IOException("Incomplete document"); 35376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 35476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 35576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 35676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void string(String value) throws IOException { 35776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\""); 35876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson for (int i = 0, length = value.length(); i < length; i++) { 35976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson char c = value.charAt(i); 36076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 36176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /* 36276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * From RFC 4627, "All Unicode characters may be placed within the 36376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * quotation marks except for the characters that must be escaped: 36476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * quotation mark, reverse solidus, and the control characters 36576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * (U+0000 through U+001F)." 36676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 36776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (c) { 36876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '"': 36976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\\': 37076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '/': 37176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write('\\'); 37276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(c); 37376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 37476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 37576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\t': 37676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\\t"); 37776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 37876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 37976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\b': 38076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\\b"); 38176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 38276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 38376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\n': 38476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\\n"); 38576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 38676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 38776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\r': 38876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\\r"); 38976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 39076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 39176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\f': 39276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\\f"); 39376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 39476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 39576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 39676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (c <= 0x1F) { 39776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(String.format("\\u%04x", (int) c)); 39876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else { 39976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(c); 40076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 40176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 40276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 40376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 40476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 40576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\""); 40676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 40776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 40876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void newline() throws IOException { 40976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (indent == null) { 41076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return; 41176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 41276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 41376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\n"); 41476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson for (int i = 1; i < stack.size(); i++) { 41576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(indent); 41676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 41776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 41876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 41976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 42076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Inserts any necessary separators and whitespace before a name. Also 42176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * adjusts the stack to expect the name's value. 42276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 42376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void beforeName() throws IOException { 42476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson JsonScope context = peek(); 42576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (context == JsonScope.NONEMPTY_OBJECT) { // first in object 42676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(','); 42776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else if (context != JsonScope.EMPTY_OBJECT) { // not in an object! 42876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException("Nesting problem: " + stack); 42976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 43076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson newline(); 43176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.DANGLING_NAME); 43276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 43376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 43476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 43576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Inserts any necessary separators and whitespace before a literal value, 43676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * inline array, or inline object. Also adjusts the stack to expect either a 43776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * closing bracket or another element. 43876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 43976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @param root true if the value is a new array or object, the two values 44076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * permitted as top-level elements. 44176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 44276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void beforeValue(boolean root) throws IOException { 44376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (peek()) { 44476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case EMPTY_DOCUMENT: // first in document 44576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (!root) { 44676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException( 44776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson "JSON must start with an array or an object."); 44876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 44976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.NONEMPTY_DOCUMENT); 45076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 45176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 45276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case EMPTY_ARRAY: // first in array 45376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.NONEMPTY_ARRAY); 45476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson newline(); 45576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 45676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 45776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case NONEMPTY_ARRAY: // another in array 45876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.append(','); 45976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson newline(); 46076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 46176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 46276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case DANGLING_NAME: // value for name 46376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.append(separator); 46476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.NONEMPTY_OBJECT); 46576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 46676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 46776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case NONEMPTY_DOCUMENT: 46876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException( 46976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson "JSON must have only one top-level value."); 47076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 47176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 47276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException("Nesting problem: " + stack); 47376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 47476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 47576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson} 476