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 191ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilsonimport java.io.Closeable; 2076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.io.IOException; 2176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.io.Writer; 2276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilsonimport java.util.ArrayList; 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")); 733312b297600ec97f28f979dfbdf6c95e164a304fJesse Wilson * writer.setIndent(" "); 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 */ 1201ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilsonpublic final class JsonWriter implements Closeable { 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 141eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson private boolean lenient; 142eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson 14376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 14476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Creates a new instance that writes a JSON-encoded stream to {@code out}. 14576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * For best performance, ensure {@link Writer} is buffered; wrapping in 14676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * {@link java.io.BufferedWriter BufferedWriter} if necessary. 14776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 14876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter(Writer out) { 14976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (out == null) { 15076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new NullPointerException("out == null"); 15176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 15276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson this.out = out; 15376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 15476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 15576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 1561ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * Sets the indentation string to be repeated for each level of indentation 1571ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * in the encoded document. If {@code indent.isEmpty()} the encoded document 1581ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * will be compact. Otherwise the encoded document will be more 1591ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * human-readable. 1601ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * 1611ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson * @param indent a string containing only whitespace. 16276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 1631ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson public void setIndent(String indent) { 1641ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson if (indent.isEmpty()) { 16576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson this.indent = null; 16676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson this.separator = ":"; 1671ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson } else { 1681ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson this.indent = indent; 1691ba417140554dbd57602a1f4b453fc42363cf394Jesse Wilson this.separator = ": "; 17076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 17176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 17276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 17376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 174eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * Configure this writer to relax its syntax rules. By default, this writer 175eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * only emits well-formed JSON as specified by <a 176eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the writer 177eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * to lenient permits the following: 178eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * <ul> 179eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * <li>Top-level values of any type. With strict writing, the top-level 180eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * value must be an object or an array. 181eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * <li>Numbers may be {@link Double#isNaN() NaNs} or {@link 182eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * Double#isInfinite() infinities}. 183eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * </ul> 184eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson */ 185eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson public void setLenient(boolean lenient) { 186eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson this.lenient = lenient; 187eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson } 188eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson 189eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson /** 190eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * Returns true if this writer has relaxed syntax rules. 191eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson */ 192eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson public boolean isLenient() { 193eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson return lenient; 194eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson } 195eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson 196eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson /** 19776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Begins encoding a new array. Each call to this method must be paired with 19876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * a call to {@link #endArray}. 19976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 20076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 20176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 20276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter beginArray() throws IOException { 20376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return open(JsonScope.EMPTY_ARRAY, "["); 20476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 20576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 20676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 20776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Ends encoding the current array. 20876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 20976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 21076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 21176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter endArray() throws IOException { 21276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return close(JsonScope.EMPTY_ARRAY, JsonScope.NONEMPTY_ARRAY, "]"); 21376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 21476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 21576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 21676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Begins encoding a new object. Each call to this method must be paired 21776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * with a call to {@link #endObject}. 21876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 21976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 22076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 22176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter beginObject() throws IOException { 22276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return open(JsonScope.EMPTY_OBJECT, "{"); 22376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 22476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 22576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 22676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Ends encoding the current object. 22776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 22876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 22976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 23076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter endObject() throws IOException { 23176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return close(JsonScope.EMPTY_OBJECT, JsonScope.NONEMPTY_OBJECT, "}"); 23276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 23376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 23476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 23576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Enters a new scope by appending any necessary whitespace and the given 23676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * bracket. 23776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 23876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonWriter open(JsonScope empty, String openBracket) throws IOException { 23976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(true); 24076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.add(empty); 24176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(openBracket); 24276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 24376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 24476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 24576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 24676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Closes the current scope by appending any necessary whitespace and the 24776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * given bracket. 24876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 24976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonWriter close(JsonScope empty, JsonScope nonempty, String closeBracket) 25076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throws IOException { 25176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson JsonScope context = peek(); 25276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (context != nonempty && context != empty) { 25376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException("Nesting problem: " + stack); 25476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 25576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 25676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.remove(stack.size() - 1); 25776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (context == nonempty) { 25876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson newline(); 25976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 26076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(closeBracket); 26176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 26276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 26376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 26476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 26576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Returns the value on the top of the stack. 26676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 26776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private JsonScope peek() { 26876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return stack.get(stack.size() - 1); 26976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 27076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 27176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 27276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Replace the value on the top of the stack with the given value. 27376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 27476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void replaceTop(JsonScope topOfStack) { 27576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson stack.set(stack.size() - 1, topOfStack); 27676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 27776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 27876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 27976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes the property name. 28076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 28176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @param name the name of the forthcoming value. May not be null. 28276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 28376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 28476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter name(String name) throws IOException { 28576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (name == null) { 28676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new NullPointerException("name == null"); 28776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 28876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeName(); 28976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson string(name); 29076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 29176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 29276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 29376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 29476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes {@code value}. 29576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 29676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @param value the literal string value, or null to encode a null literal. 29776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 29876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 29976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter value(String value) throws IOException { 30076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (value == null) { 30176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return nullValue(); 30276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 30376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(false); 30476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson string(value); 30576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 30676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 30776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 30876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 30976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes {@code null}. 31076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 31176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 31276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 31376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter nullValue() throws IOException { 31476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(false); 31576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("null"); 31676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 31776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 31876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 31976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 32076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes {@code value}. 32176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 32276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 32376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 32476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter value(boolean value) throws IOException { 32576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(false); 32676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(value ? "true" : "false"); 32776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 32876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 32976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 33076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 33176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes {@code value}. 33276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 33376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @param value a finite value. May not be {@link Double#isNaN() NaNs} or 334eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * {@link Double#isInfinite() infinities} unless this writer is lenient. 33576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 33676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 33776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter value(double value) throws IOException { 338eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson if (!lenient && (Double.isNaN(value) || Double.isInfinite(value))) { 33976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalArgumentException("Numeric values must be finite, but was " + value); 34076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 34176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(false); 34276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.append(Double.toString(value)); 34376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 34476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 34576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 34676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 34776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Encodes {@code value}. 34876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 34976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @return this writer. 35076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 35176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public JsonWriter value(long value) throws IOException { 35276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson beforeValue(false); 35376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(Long.toString(value)); 35476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return this; 35576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 35676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 35776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 358eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * Encodes {@code value}. 359eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * 360eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * @param value a finite value. May not be {@link Double#isNaN() NaNs} or 361eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * {@link Double#isInfinite() infinities} unless this writer is lenient. 362eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson * @return this writer. 363eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson */ 364eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson public JsonWriter value(Number value) throws IOException { 365eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson if (value == null) { 366eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson return nullValue(); 367eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson } 368eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson 369eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson String string = value.toString(); 370eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson if (!lenient && 371eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) { 372eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson throw new IllegalArgumentException("Numeric values must be finite, but was " + value); 373eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson } 374eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson beforeValue(false); 375eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson out.append(string); 376eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson return this; 377eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson } 378eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson 379eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson /** 38076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Ensures all buffered data is written to the underlying {@link Writer} 38176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * and flushes that writer. 38276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 38376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void flush() throws IOException { 38476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.flush(); 38576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 38676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 38776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 38876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Flushes and closes this writer and the underlying {@link Writer}. 38976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 39076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @throws IOException if the JSON document is incomplete. 39176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 39276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson public void close() throws IOException { 39376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.close(); 39476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 39576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (peek() != JsonScope.NONEMPTY_DOCUMENT) { 39676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IOException("Incomplete document"); 39776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 39876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 39976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 40076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void string(String value) throws IOException { 40176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\""); 40276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson for (int i = 0, length = value.length(); i < length; i++) { 40376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson char c = value.charAt(i); 40476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 40576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /* 40676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * From RFC 4627, "All Unicode characters may be placed within the 40776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * quotation marks except for the characters that must be escaped: 40876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * quotation mark, reverse solidus, and the control characters 40976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * (U+0000 through U+001F)." 4100fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson * 4110fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson * We also escape '\u2028' and '\u2029', which JavaScript interprets 4120fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson * as newline characters. This prevents eval() from failing with a 4130fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson * syntax error. 4140fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson * http://code.google.com/p/google-gson/issues/detail?id=341 41576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 41676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (c) { 41776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '"': 41876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\\': 41976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write('\\'); 42076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(c); 42176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 42276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 42376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\t': 42476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\\t"); 42576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 42676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 42776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\b': 42876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\\b"); 42976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 43076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 43176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\n': 43276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\\n"); 43376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 43476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 43576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\r': 43676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\\r"); 43776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 43876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 43976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case '\f': 44076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\\f"); 44176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 44276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 4430fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson case '\u2028': 4440fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson case '\u2029': 4450fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson out.write(String.format("\\u%04x", (int) c)); 4460fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson break; 4470fc0edfd273f9d92b9f9fe4f49382b11104f55f0Jesse Wilson 44876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 44976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (c <= 0x1F) { 45076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(String.format("\\u%04x", (int) c)); 45176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else { 45276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(c); 45376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 45476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 45576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 45676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 45776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 45876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\""); 45976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 46076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 46176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void newline() throws IOException { 46276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (indent == null) { 46376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson return; 46476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 46576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 46676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write("\n"); 46776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson for (int i = 1; i < stack.size(); i++) { 46876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(indent); 46976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 47076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 47176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 47276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 47376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Inserts any necessary separators and whitespace before a name. Also 47476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * adjusts the stack to expect the name's value. 47576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 47676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void beforeName() throws IOException { 47776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson JsonScope context = peek(); 47876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson if (context == JsonScope.NONEMPTY_OBJECT) { // first in object 47976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.write(','); 48076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } else if (context != JsonScope.EMPTY_OBJECT) { // not in an object! 48176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException("Nesting problem: " + stack); 48276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 48376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson newline(); 48476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.DANGLING_NAME); 48576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 48676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 48776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson /** 48876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * Inserts any necessary separators and whitespace before a literal value, 48976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * inline array, or inline object. Also adjusts the stack to expect either a 49076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * closing bracket or another element. 49176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * 49276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * @param root true if the value is a new array or object, the two values 49376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson * permitted as top-level elements. 49476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson */ 49576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson private void beforeValue(boolean root) throws IOException { 49676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson switch (peek()) { 49776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case EMPTY_DOCUMENT: // first in document 498eb97c0ddc063176c26065fc6855188edf0c16e03Jesse Wilson if (!lenient && !root) { 49976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException( 50076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson "JSON must start with an array or an object."); 50176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 50276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.NONEMPTY_DOCUMENT); 50376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 50476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 50576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case EMPTY_ARRAY: // first in array 50676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.NONEMPTY_ARRAY); 50776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson newline(); 50876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 50976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 51076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case NONEMPTY_ARRAY: // another in array 51176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.append(','); 51276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson newline(); 51376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 51476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 51576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case DANGLING_NAME: // value for name 51676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson out.append(separator); 51776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson replaceTop(JsonScope.NONEMPTY_OBJECT); 51876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson break; 51976d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 52076d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson case NONEMPTY_DOCUMENT: 52176d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException( 52276d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson "JSON must have only one top-level value."); 52376d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson 52476d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson default: 52576d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson throw new IllegalStateException("Nesting problem: " + stack); 52676d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 52776d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson } 52876d7e20a75d24afb2f5011a7646dbde8aaa52087Jesse Wilson} 529