Patch new JSON APIs with changes informed by GSON.

Change-Id: I86c12a123080cc06ab23d11d1563bb52c5902517
This commit is contained in:
Jesse Wilson
2011-01-09 16:05:03 -08:00
parent b3fbd7e0fd
commit eb97c0ddc0
6 changed files with 262 additions and 40 deletions

View File

@ -198616,6 +198616,17 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
<method name="isLenient"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="nextBoolean"
return="boolean"
abstract="false"
@ -198746,21 +198757,6 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
<method name="syntaxError"
return="java.io.IOException"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="message" type="java.lang.String">
</parameter>
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
</class>
<class name="JsonToken"
extends="java.lang.Enum"
@ -198893,6 +198889,17 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
<method name="isLenient"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="name"
return="android.util.JsonWriter"
abstract="false"
@ -198934,6 +198941,19 @@
<parameter name="indent" type="java.lang.String">
</parameter>
</method>
<method name="setLenient"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="lenient" type="boolean">
</parameter>
</method>
<method name="value"
return="android.util.JsonWriter"
abstract="false"
@ -198994,6 +199014,21 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
<method name="value"
return="android.util.JsonWriter"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="value" type="java.lang.Number">
</parameter>
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
</class>
<class name="Log"
extends="java.lang.Object"
@ -199373,6 +199408,25 @@
</parameter>
</method>
</class>
<class name="MalformedJsonException"
extends="java.io.IOException"
abstract="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
<constructor name="MalformedJsonException"
type="android.util.MalformedJsonException"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="message" type="java.lang.String">
</parameter>
</constructor>
</class>
<class name="MonthDisplayHelper"
extends="java.lang.Object"
abstract="false"
@ -258003,7 +258057,7 @@
deprecated="not deprecated"
visibility="public"
>
<parameter name="t" type="T">
<parameter name="arg0" type="T">
</parameter>
</method>
</interface>

View File

@ -16,6 +16,7 @@
package android.util;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
import java.io.Closeable;
@ -248,6 +249,13 @@ public final class JsonReader implements Closeable {
this.lenient = lenient;
}
/**
* Returns true if this parser is liberal in what it accepts.
*/
public boolean isLenient() {
return lenient;
}
/**
* Consumes the next token from the JSON stream and asserts that it is the
* beginning of a new array.
@ -311,7 +319,7 @@ public final class JsonReader implements Closeable {
case EMPTY_DOCUMENT:
replaceTop(JsonScope.NONEMPTY_DOCUMENT);
JsonToken firstToken = nextValue();
if (token != JsonToken.BEGIN_ARRAY && token != JsonToken.BEGIN_OBJECT) {
if (!lenient && token != JsonToken.BEGIN_ARRAY && token != JsonToken.BEGIN_OBJECT) {
throw new IOException(
"Expected JSON document to start with '[' or '{' but was " + token);
}
@ -327,7 +335,15 @@ public final class JsonReader implements Closeable {
case NONEMPTY_OBJECT:
return nextInObject(false);
case NONEMPTY_DOCUMENT:
return token = JsonToken.END_DOCUMENT;
try {
JsonToken token = nextValue();
if (lenient) {
return token;
}
throw syntaxError("Expected EOF");
} catch (EOFException e) {
return token = JsonToken.END_DOCUMENT; // TODO: avoid throwing here?
}
case CLOSED:
throw new IllegalStateException("JsonReader is closed");
default:
@ -758,7 +774,7 @@ public final class JsonReader implements Closeable {
}
}
throw syntaxError("End of input");
throw new EOFException("End of input");
}
private void checkLenient() throws IOException {
@ -1030,8 +1046,6 @@ public final class JsonReader implements Closeable {
* form -12.34e+56. Fractional and exponential parts are optional. Leading
* zeroes are not allowed in the value or exponential part, but are allowed
* in the fraction.
*
* <p>This has a side effect of setting isInteger.
*/
private JsonToken decodeNumber(char[] chars, int offset, int length) {
int i = offset;
@ -1085,8 +1099,8 @@ public final class JsonReader implements Closeable {
* Throws a new IO exception with the given message and a context snippet
* with this reader's content.
*/
public IOException syntaxError(String message) throws IOException {
throw new JsonSyntaxException(message + " near " + getSnippet());
private IOException syntaxError(String message) throws IOException {
throw new MalformedJsonException(message + " near " + getSnippet());
}
private CharSequence getSnippet() {
@ -1097,10 +1111,4 @@ public final class JsonReader implements Closeable {
snippet.append(buffer, pos, afterPos);
return snippet;
}
private static class JsonSyntaxException extends IOException {
private JsonSyntaxException(String s) {
super(s);
}
}
}

View File

@ -138,6 +138,8 @@ public final class JsonWriter implements Closeable {
*/
private String separator = ":";
private boolean lenient;
/**
* Creates a new instance that writes a JSON-encoded stream to {@code out}.
* For best performance, ensure {@link Writer} is buffered; wrapping in
@ -168,6 +170,29 @@ public final class JsonWriter implements Closeable {
}
}
/**
* Configure this writer to relax its syntax rules. By default, this writer
* only emits well-formed JSON as specified by <a
* href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the writer
* to lenient permits the following:
* <ul>
* <li>Top-level values of any type. With strict writing, the top-level
* value must be an object or an array.
* <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
* Double#isInfinite() infinities}.
* </ul>
*/
public void setLenient(boolean lenient) {
this.lenient = lenient;
}
/**
* Returns true if this writer has relaxed syntax rules.
*/
public boolean isLenient() {
return lenient;
}
/**
* Begins encoding a new array. Each call to this method must be paired with
* a call to {@link #endArray}.
@ -306,11 +331,11 @@ public final class JsonWriter implements Closeable {
* Encodes {@code value}.
*
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or
* {@link Double#isInfinite() infinities}.
* {@link Double#isInfinite() infinities} unless this writer is lenient.
* @return this writer.
*/
public JsonWriter value(double value) throws IOException {
if (Double.isNaN(value) || Double.isInfinite(value)) {
if (!lenient && (Double.isNaN(value) || Double.isInfinite(value))) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
}
beforeValue(false);
@ -329,6 +354,28 @@ public final class JsonWriter implements Closeable {
return this;
}
/**
* Encodes {@code value}.
*
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or
* {@link Double#isInfinite() infinities} unless this writer is lenient.
* @return this writer.
*/
public JsonWriter value(Number value) throws IOException {
if (value == null) {
return nullValue();
}
String string = value.toString();
if (!lenient &&
(string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
}
beforeValue(false);
out.append(string);
return this;
}
/**
* Ensures all buffered data is written to the underlying {@link Writer}
* and flushes that writer.
@ -364,7 +411,6 @@ public final class JsonWriter implements Closeable {
switch (c) {
case '"':
case '\\':
case '/':
out.write('\\');
out.write(c);
break;
@ -439,7 +485,7 @@ public final class JsonWriter implements Closeable {
private void beforeValue(boolean root) throws IOException {
switch (peek()) {
case EMPTY_DOCUMENT: // first in document
if (!root) {
if (!lenient && !root) {
throw new IllegalStateException(
"JSON must start with an array or an object.");
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util;
import java.io.IOException;
/**
* Thrown when a reader encounters malformed JSON. Some syntax errors can be
* ignored by calling {@link JsonReader#setLenient(boolean)}.
*/
public final class MalformedJsonException extends IOException {
private static final long serialVersionUID = 1L;
public MalformedJsonException(String message) {
super(message);
}
}

View File

@ -16,11 +16,10 @@
package android.util;
import java.util.Arrays;
import junit.framework.TestCase;
import java.io.IOException;
import java.io.StringReader;
import java.util.Arrays;
import junit.framework.TestCase;
public final class JsonReaderTest extends TestCase {
@ -677,7 +676,7 @@ public final class JsonReaderTest extends TestCase {
try {
reader.nextString();
fail();
} catch (IOException expected) {
} catch (MalformedJsonException expected) {
}
}
@ -811,4 +810,50 @@ public final class JsonReaderTest extends TestCase {
reader.nextNull();
reader.endArray();
}
public void testStrictMultipleTopLevelValues() throws IOException {
JsonReader reader = new JsonReader(new StringReader("[] []"));
reader.beginArray();
reader.endArray();
try {
reader.peek();
fail();
} catch (IOException expected) {
}
}
public void testLenientMultipleTopLevelValues() throws IOException {
JsonReader reader = new JsonReader(new StringReader("[] true {}"));
reader.setLenient(true);
reader.beginArray();
reader.endArray();
assertEquals(true, reader.nextBoolean());
reader.beginObject();
reader.endObject();
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
}
public void testStrictTopLevelValueType() {
JsonReader reader = new JsonReader(new StringReader("true"));
try {
reader.nextBoolean();
fail();
} catch (IOException expected) {
}
}
public void testLenientTopLevelValueType() throws IOException {
JsonReader reader = new JsonReader(new StringReader("true"));
reader.setLenient(true);
assertEquals(true, reader.nextBoolean());
}
public void testStrictNonExecutePrefix() {
JsonReader reader = new JsonReader(new StringReader(")]}'\n []"));
try {
reader.beginArray();
fail();
} catch (IOException expected) {
}
}
}

View File

@ -16,10 +16,11 @@
package android.util;
import junit.framework.TestCase;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import junit.framework.TestCase;
public final class JsonWriterTest extends TestCase {
@ -119,7 +120,7 @@ public final class JsonWriterTest extends TestCase {
JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.beginObject();
jsonWriter.name("a");
jsonWriter.value(null);
jsonWriter.value((String) null);
jsonWriter.endObject();
assertEquals("{\"a\":null}", stringWriter.toString());
}
@ -145,6 +146,27 @@ public final class JsonWriterTest extends TestCase {
}
}
public void testNonFiniteBoxedDoubles() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.beginArray();
try {
jsonWriter.value(new Double(Double.NaN));
fail();
} catch (IllegalArgumentException expected) {
}
try {
jsonWriter.value(new Double(Double.NEGATIVE_INFINITY));
fail();
} catch (IllegalArgumentException expected) {
}
try {
jsonWriter.value(new Double(Double.POSITIVE_INFINITY));
fail();
} catch (IllegalArgumentException expected) {
}
}
public void testDoubles() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);
@ -189,6 +211,22 @@ public final class JsonWriterTest extends TestCase {
+ "9223372036854775807]", stringWriter.toString());
}
public void testNumbers() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.beginArray();
jsonWriter.value(new BigInteger("0"));
jsonWriter.value(new BigInteger("9223372036854775808"));
jsonWriter.value(new BigInteger("-9223372036854775809"));
jsonWriter.value(new BigDecimal("3.141592653589793238462643383"));
jsonWriter.endArray();
jsonWriter.close();
assertEquals("[0,"
+ "9223372036854775808,"
+ "-9223372036854775809,"
+ "3.141592653589793238462643383]", stringWriter.toString());
}
public void testBooleans() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);