Improve lenient mode documentation (#2122)
This commit is contained in:
parent
3f1d4fb65f
commit
cbc0af867b
@ -16,25 +16,6 @@
|
||||
|
||||
package com.google.gson;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicLongArray;
|
||||
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.Excluder;
|
||||
import com.google.gson.internal.GsonBuildConfig;
|
||||
@ -58,6 +39,24 @@ import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.google.gson.stream.MalformedJsonException;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicLongArray;
|
||||
|
||||
/**
|
||||
* This is the main class for using Gson. Gson is typically used by first constructing a
|
||||
@ -97,6 +96,33 @@ import com.google.gson.stream.MalformedJsonException;
|
||||
* <p>See the <a href="https://sites.google.com/site/gson/gson-user-guide">Gson User Guide</a>
|
||||
* for a more complete set of examples.</p>
|
||||
*
|
||||
* <h2>Lenient JSON handling</h2>
|
||||
* For legacy reasons most of the {@code Gson} methods allow JSON data which does not
|
||||
* comply with the JSON specification, regardless of whether {@link GsonBuilder#setLenient()}
|
||||
* is used or not. If this behavior is not desired, the following workarounds can be used:
|
||||
*
|
||||
* <h3>Serialization</h3>
|
||||
* <ol>
|
||||
* <li>Use {@link #getAdapter(Class)} to obtain the adapter for the type to be serialized
|
||||
* <li>When using an existing {@code JsonWriter}, manually apply the writer settings of this
|
||||
* {@code Gson} instance listed by {@link #newJsonWriter(Writer)}.<br>
|
||||
* Otherwise, when not using an existing {@code JsonWriter}, use {@link #newJsonWriter(Writer)}
|
||||
* to construct one.
|
||||
* <li>Call {@link TypeAdapter#write(JsonWriter, Object)}
|
||||
* </ol>
|
||||
*
|
||||
* <h3>Deserialization</h3>
|
||||
* <ol>
|
||||
* <li>Use {@link #getAdapter(Class)} to obtain the adapter for the type to be deserialized
|
||||
* <li>When using an existing {@code JsonReader}, manually apply the reader settings of this
|
||||
* {@code Gson} instance listed by {@link #newJsonReader(Reader)}.<br>
|
||||
* Otherwise, when not using an existing {@code JsonReader}, use {@link #newJsonReader(Reader)}
|
||||
* to construct one.
|
||||
* <li>Call {@link TypeAdapter#read(JsonReader)}
|
||||
* <li>Call {@link JsonReader#peek()} and verify that the result is {@link JsonToken#END_DOCUMENT}
|
||||
* to make sure there is no trailing data
|
||||
* </ol>
|
||||
*
|
||||
* @see com.google.gson.reflect.TypeToken
|
||||
*
|
||||
* @author Inderjeet Singh
|
||||
@ -736,6 +762,15 @@ public final class Gson {
|
||||
/**
|
||||
* Writes the JSON representation of {@code src} of type {@code typeOfSrc} to
|
||||
* {@code writer}.
|
||||
*
|
||||
* <p>The JSON data is written in {@linkplain JsonWriter#setLenient(boolean) lenient mode},
|
||||
* regardless of the lenient mode setting of the provided writer. The lenient mode setting
|
||||
* of the writer is restored once this method returns.
|
||||
*
|
||||
* <p>The 'HTML-safe' and 'serialize {@code null}' settings of this {@code Gson} instance
|
||||
* (configured by the {@link GsonBuilder}) are applied, and the original settings of the
|
||||
* writer are restored once this method returns.
|
||||
*
|
||||
* @throws JsonIOException if there was a problem writing to the writer
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -834,6 +869,15 @@ public final class Gson {
|
||||
|
||||
/**
|
||||
* Writes the JSON for {@code jsonElement} to {@code writer}.
|
||||
*
|
||||
* <p>The JSON data is written in {@linkplain JsonWriter#setLenient(boolean) lenient mode},
|
||||
* regardless of the lenient mode setting of the provided writer. The lenient mode setting
|
||||
* of the writer is restored once this method returns.
|
||||
*
|
||||
* <p>The 'HTML-safe' and 'serialize {@code null}' settings of this {@code Gson} instance
|
||||
* (configured by the {@link GsonBuilder}) are applied, and the original settings of the
|
||||
* writer are restored once this method returns.
|
||||
*
|
||||
* @throws JsonIOException if there was a problem writing to the writer
|
||||
*/
|
||||
public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOException {
|
||||
@ -868,6 +912,9 @@ public final class Gson {
|
||||
* {@link #fromJson(String, Type)}. If you have the Json in a {@link Reader} instead of
|
||||
* a String, use {@link #fromJson(Reader, Class)} instead.
|
||||
*
|
||||
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements,
|
||||
* or if there is trailing data.
|
||||
*
|
||||
* @param <T> the type of the desired object
|
||||
* @param json the string from which the object is to be deserialized
|
||||
* @param classOfT the class of T
|
||||
@ -887,6 +934,9 @@ public final class Gson {
|
||||
* {@link #fromJson(String, Class)} instead. If you have the Json in a {@link Reader} instead of
|
||||
* a String, use {@link #fromJson(Reader, Type)} instead.
|
||||
*
|
||||
* <p>An exception is thrown if the JSON string has multiple top-level JSON elements,
|
||||
* or if there is trailing data.
|
||||
*
|
||||
* @param <T> the type of the desired object
|
||||
* @param json the string from which the object is to be deserialized
|
||||
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
|
||||
@ -920,6 +970,9 @@ public final class Gson {
|
||||
* invoke {@link #fromJson(Reader, Type)}. If you have the Json in a String form instead of a
|
||||
* {@link Reader}, use {@link #fromJson(String, Class)} instead.
|
||||
*
|
||||
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements,
|
||||
* or if there is trailing data.
|
||||
*
|
||||
* @param <T> the type of the desired object
|
||||
* @param json the reader producing the Json from which the object is to be deserialized.
|
||||
* @param classOfT the class of T
|
||||
@ -941,6 +994,9 @@ public final class Gson {
|
||||
* non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the Json in a
|
||||
* String form instead of a {@link Reader}, use {@link #fromJson(String, Type)} instead.
|
||||
*
|
||||
* <p>An exception is thrown if the JSON data has multiple top-level JSON elements,
|
||||
* or if there is trailing data.
|
||||
*
|
||||
* @param <T> the type of the desired object
|
||||
* @param json the reader producing Json from which the object is to be deserialized
|
||||
* @param typeOfT The specific genericized type of src. You can obtain this type by using the
|
||||
@ -965,7 +1021,7 @@ public final class Gson {
|
||||
private static void assertFullConsumption(Object obj, JsonReader reader) {
|
||||
try {
|
||||
if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) {
|
||||
throw new JsonIOException("JSON document was not fully consumed.");
|
||||
throw new JsonSyntaxException("JSON document was not fully consumed.");
|
||||
}
|
||||
} catch (MalformedJsonException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
@ -977,7 +1033,14 @@ public final class Gson {
|
||||
/**
|
||||
* Reads the next JSON value from {@code reader} and convert it to an object
|
||||
* of type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF.
|
||||
* Since Type is not parameterized by T, this method is type unsafe and should be used carefully
|
||||
* Since Type is not parameterized by T, this method is type unsafe and should be used carefully.
|
||||
*
|
||||
* <p>Unlike the other {@code fromJson} methods, no exception is thrown if the JSON data has
|
||||
* multiple top-level JSON elements, or if there is trailing data.
|
||||
*
|
||||
* <p>The JSON data is parsed in {@linkplain JsonReader#setLenient(boolean) lenient mode},
|
||||
* regardless of the lenient mode setting of the provided reader. The lenient mode setting
|
||||
* of the reader is restored once this method returns.
|
||||
*
|
||||
* @throws JsonIOException if there was a problem writing to the Reader
|
||||
* @throws JsonSyntaxException if json is not a valid representation for an object of type
|
||||
|
@ -34,6 +34,7 @@ import com.google.gson.internal.bind.TypeAdapters;
|
||||
import com.google.gson.internal.sql.SqlTypesSupport;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import static com.google.gson.Gson.DEFAULT_COMPLEX_MAP_KEYS;
|
||||
import static com.google.gson.Gson.DEFAULT_DATE_PATTERN;
|
||||
@ -425,12 +426,14 @@ public final class GsonBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, Gson is strict and only accepts JSON as specified by
|
||||
* <a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. This option makes the parser
|
||||
* liberal in what it accepts.
|
||||
* Configures Gson to allow JSON data which does not strictly comply with the JSON specification.
|
||||
*
|
||||
* <p>Note: Due to legacy reasons most methods of Gson are always lenient, regardless of
|
||||
* whether this builder method is used.
|
||||
*
|
||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||
* @see JsonReader#setLenient(boolean)
|
||||
* @see JsonWriter#setLenient(boolean)
|
||||
*/
|
||||
public GsonBuilder setLenient() {
|
||||
lenient = true;
|
||||
|
@ -15,17 +15,16 @@
|
||||
*/
|
||||
package com.google.gson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.MalformedJsonException;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* A parser to parse Json into a parse tree of {@link JsonElement}s
|
||||
* A parser to parse JSON into a parse tree of {@link JsonElement}s.
|
||||
*
|
||||
* @author Inderjeet Singh
|
||||
* @author Joel Leitch
|
||||
@ -37,7 +36,11 @@ public final class JsonParser {
|
||||
public JsonParser() {}
|
||||
|
||||
/**
|
||||
* Parses the specified JSON string into a parse tree
|
||||
* Parses the specified JSON string into a parse tree.
|
||||
* An exception is thrown if the JSON string has multiple top-level JSON elements,
|
||||
* or if there is trailing data.
|
||||
*
|
||||
* <p>The JSON string is parsed in {@linkplain JsonReader#setLenient(boolean) lenient mode}.
|
||||
*
|
||||
* @param json JSON text
|
||||
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
|
||||
@ -48,11 +51,16 @@ public final class JsonParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified JSON string into a parse tree
|
||||
* Parses the complete JSON string provided by the reader into a parse tree.
|
||||
* An exception is thrown if the JSON string has multiple top-level JSON elements,
|
||||
* or if there is trailing data.
|
||||
*
|
||||
* <p>The JSON data is parsed in {@linkplain JsonReader#setLenient(boolean) lenient mode}.
|
||||
*
|
||||
* @param reader JSON text
|
||||
* @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
|
||||
* @throws JsonParseException if the specified text is not valid JSON
|
||||
* @throws JsonParseException if there is an IOException or if the specified
|
||||
* text is not valid JSON
|
||||
*/
|
||||
public static JsonElement parseReader(Reader reader) throws JsonIOException, JsonSyntaxException {
|
||||
try {
|
||||
@ -73,6 +81,12 @@ public final class JsonParser {
|
||||
|
||||
/**
|
||||
* Returns the next value from the JSON stream as a parse tree.
|
||||
* Unlike the other {@code parse} methods, no exception is thrown if the JSON data has
|
||||
* multiple top-level JSON elements, or if there is trailing data.
|
||||
*
|
||||
* <p>The JSON data is parsed in {@linkplain JsonReader#setLenient(boolean) lenient mode},
|
||||
* regardless of the lenient mode setting of the provided reader. The lenient mode setting
|
||||
* of the reader is restored once this method returns.
|
||||
*
|
||||
* @throws JsonParseException if there is an IOException or if the specified
|
||||
* text is not valid JSON
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
package com.google.gson;
|
||||
|
||||
import com.google.gson.internal.bind.JsonTreeWriter;
|
||||
import com.google.gson.internal.bind.JsonTreeReader;
|
||||
import com.google.gson.internal.bind.JsonTreeWriter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
@ -252,6 +252,9 @@ public abstract class TypeAdapter<T> {
|
||||
* read is strict. Create a {@link JsonReader#setLenient(boolean) lenient}
|
||||
* {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading.
|
||||
*
|
||||
* <p>No exception is thrown if the JSON data has multiple top-level JSON elements,
|
||||
* or if there is trailing data.
|
||||
*
|
||||
* @return the converted Java object. May be null.
|
||||
* @since 2.2
|
||||
*/
|
||||
@ -266,6 +269,9 @@ public abstract class TypeAdapter<T> {
|
||||
* strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code
|
||||
* JsonReader} and call {@link #read(JsonReader)} for lenient reading.
|
||||
*
|
||||
* <p>No exception is thrown if the JSON data has multiple top-level JSON elements,
|
||||
* or if there is trailing data.
|
||||
*
|
||||
* @return the converted Java object. May be null.
|
||||
* @since 2.2
|
||||
*/
|
||||
@ -276,7 +282,8 @@ public abstract class TypeAdapter<T> {
|
||||
/**
|
||||
* Converts {@code jsonTree} to a Java object.
|
||||
*
|
||||
* @param jsonTree the Java object to convert. May be {@link JsonNull}.
|
||||
* @param jsonTree the JSON element to convert. May be {@link JsonNull}.
|
||||
* @return the converted Java object. May be null.
|
||||
* @since 2.2
|
||||
*/
|
||||
public final T fromJsonTree(JsonElement jsonTree) {
|
||||
|
@ -304,8 +304,6 @@ public class JsonReader implements Closeable {
|
||||
* prefix</a>, <code>")]}'\n"</code>.
|
||||
* <li>Streams that include multiple top-level values. With strict parsing,
|
||||
* each stream must contain exactly one top-level value.
|
||||
* <li>Top-level values of any type. With strict parsing, the top-level
|
||||
* value must be an object or an array.
|
||||
* <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
|
||||
* Double#isInfinite() infinities}.
|
||||
* <li>End of line comments starting with {@code //} or {@code #} and
|
||||
@ -321,6 +319,18 @@ public class JsonReader implements Closeable {
|
||||
* {@code :}.
|
||||
* <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note: Even in strict mode there are slight derivations from the JSON
|
||||
* specification:
|
||||
* <ul>
|
||||
* <li>JsonReader allows the literals {@code true}, {@code false} and {@code null}
|
||||
* to have any capitalization, for example {@code fAlSe}
|
||||
* <li>JsonReader supports the escape sequence {@code \'}, representing a {@code '}
|
||||
* <li>JsonReader supports the escape sequence <code>\<i>LF</i></code> (with {@code LF}
|
||||
* being the Unicode character U+000A), resulting in a {@code LF} within the
|
||||
* read JSON string
|
||||
* <li>JsonReader allows unescaped control characters (U+0000 through U+001F)
|
||||
* </ul>
|
||||
*/
|
||||
public final void setLenient(boolean lenient) {
|
||||
this.lenient = lenient;
|
||||
|
52
gson/src/test/java/com/google/gson/TypeAdapterTest.java
Normal file
52
gson/src/test/java/com/google/gson/TypeAdapterTest.java
Normal file
@ -0,0 +1,52 @@
|
||||
package com.google.gson;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TypeAdapterTest {
|
||||
@Test
|
||||
public void testNullSafe() throws IOException {
|
||||
TypeAdapter<String> adapter = new TypeAdapter<String>() {
|
||||
@Override public void write(JsonWriter out, String value) {
|
||||
throw new AssertionError("unexpected call");
|
||||
}
|
||||
|
||||
@Override public String read(JsonReader in) {
|
||||
throw new AssertionError("unexpected call");
|
||||
}
|
||||
}.nullSafe();
|
||||
|
||||
assertEquals("null", adapter.toJson(null));
|
||||
assertNull(adapter.fromJson("null"));
|
||||
}
|
||||
|
||||
private static final TypeAdapter<String> adapter = new TypeAdapter<String>() {
|
||||
@Override public void write(JsonWriter out, String value) throws IOException {
|
||||
out.value(value);
|
||||
}
|
||||
|
||||
@Override public String read(JsonReader in) throws IOException {
|
||||
return in.nextString();
|
||||
}
|
||||
};
|
||||
|
||||
// Note: This test just verifies the current behavior; it is a bit questionable
|
||||
// whether that behavior is actually desired
|
||||
@Test
|
||||
public void testFromJson_Reader_TrailingData() throws IOException {
|
||||
assertEquals("a", adapter.fromJson(new StringReader("\"a\"1")));
|
||||
}
|
||||
|
||||
// Note: This test just verifies the current behavior; it is a bit questionable
|
||||
// whether that behavior is actually desired
|
||||
@Test
|
||||
public void testFromJson_String_TrailingData() throws IOException {
|
||||
assertEquals("a", adapter.fromJson("\"a\"1"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user