diff --git a/Troubleshooting.md b/Troubleshooting.md index acdb8be6d2..525df0154e 100644 --- a/Troubleshooting.md +++ b/Troubleshooting.md @@ -127,7 +127,7 @@ For example, let's assume you want to deserialize the following JSON data: } ``` -This will fail with an exception similar to this one: `MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 5 column 4 path $.languages[2]` +This will fail with an exception similar to this one: `MalformedJsonException: Use JsonReader.setStrictness(Strictness.LENIENT) to accept malformed JSON at line 5 column 4 path $.languages[2]` The problem here is the trailing comma (`,`) after `"French"`, trailing commas are not allowed by the JSON specification. The location information "line 5 column 4" points to the `]` in the JSON data (with some slight inaccuracies) because Gson expected another value after `,` instead of the closing `]`. The JSONPath `$.languages[2]` in the exception message also points there: `$.` refers to the root object, `languages` refers to its member of that name and `[2]` refers to the (missing) third value in the JSON array value of that member (numbering starts at 0, so it is `[2]` instead of `[3]`). The proper solution here is to fix the malformed JSON data. diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 5460417de8..b73552c569 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -107,12 +107,11 @@ * *

JSON Strictness handling

* For legacy reasons most of the {@code Gson} methods allow JSON data which does not - * comply with the JSON specification when the strictness is set to {@code null} (the default value). - * To specify the {@linkplain Strictness strictness} of a {@code Gson} instance, you should set it through - * {@link GsonBuilder#setStrictness(Strictness)}. If the strictness of a {@code Gson} instance is set to a not-null - * value, the strictness will always be enforced. + * comply with the JSON specification when no explicit {@linkplain Strictness strictness} is set (the default). + * To specify the strictness of a {@code Gson} instance, you should set it through + * {@link GsonBuilder#setStrictness(Strictness)}. * - *

For older Gson versions which don't have the {@link Strictness} mode API the following + *

For older Gson versions, which don't have the strictness mode API, the following * workarounds can be used: * *

Serialization

@@ -145,6 +144,7 @@ */ public final class Gson { static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; + // Strictness of `null` is the legacy mode where some Gson APIs are always lenient static final Strictness DEFAULT_STRICTNESS = null; static final FormattingStyle DEFAULT_FORMATTING_STYLE = FormattingStyle.COMPACT; static final boolean DEFAULT_ESCAPE_HTML = true; @@ -236,7 +236,8 @@ public final class Gson { *
  • By default, Gson excludes transient or static fields from * consideration for serialization and deserialization. You can change this behavior through * {@link GsonBuilder#excludeFieldsWithModifiers(int...)}.
  • - *
  • The strictness is set to {@code null}.
  • + *
  • No explicit strictness is set. You can change this by calling + * {@link GsonBuilder#setStrictness(Strictness)}.
  • * */ public Gson() { @@ -808,7 +809,7 @@ public void toJson(Object src, Appendable writer) throws JsonIOException { *
        * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
        * 
    - * @param writer Writer to which the JSON representation of src needs to be written. + * @param writer Writer to which the JSON representation of src needs to be written * @throws JsonIOException if there was a problem writing to the writer * @since 1.2 * @@ -828,19 +829,20 @@ public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOE * Writes the JSON representation of {@code src} of type {@code typeOfSrc} to * {@code writer}. * -

    If the {@code Gson} instance has a not-null strictness setting, this setting will be used for reading the JSON - * regardless of the {@linkplain JsonReader#getStrictness() strictness} of the provided {@link JsonReader}. For legacy - * reasons, if the {@code Gson} instance has {@code null} as its strictness setting and the provided {@link JsonReader} - * has a strictness of {@link Strictness#LEGACY_STRICT}, the JSON will be read in {@linkplain Strictness#LENIENT} - * mode. Note that in both cases the old strictness value of the reader will be restored when this method returns. + *

    If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness setting}, + * this setting will be used for writing the JSON regardless of the {@linkplain JsonWriter#getStrictness() strictness} + * of the provided {@link JsonWriter}. For legacy reasons, if the {@code Gson} instance has no explicit strictness setting + * and the writer does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link Strictness#LENIENT} + * mode.
    + * Note that in all cases the old strictness setting of the writer will be restored when this method returns. * *

    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. * - * @param src the object to be written. - * @param typeOfSrc the type of the object to be written. - * @param writer the {@link JsonWriter} writer to which the provided object will be written. + * @param src the object for which JSON representation is to be created + * @param typeOfSrc the type of the object to be written + * @param writer Writer to which the JSON representation of src needs to be written * * @throws JsonIOException if there was a problem writing to the writer */ @@ -851,7 +853,7 @@ public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOE Strictness oldStrictness = writer.getStrictness(); if (this.strictness != null) { writer.setStrictness(this.strictness); - } else if (writer.getStrictness() == Strictness.LEGACY_STRICT){ + } else if (writer.getStrictness() != Strictness.STRICT) { writer.setStrictness(Strictness.LENIENT); } @@ -911,9 +913,10 @@ public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOExce *

  • {@link GsonBuilder#disableHtmlEscaping()}
  • *
  • {@link GsonBuilder#generateNonExecutableJson()}
  • *
  • {@link GsonBuilder#serializeNulls()}
  • - *
  • {@link GsonBuilder#setStrictness(Strictness)}. If the strictness of this {@code Gson} instance - * is set to {@code null}, the created writer will have a strictness of {@link Strictness#LEGACY_STRICT}. - * If the strictness is set to a non-null value, this strictness will be used for the created writer.
  • + *
  • {@link GsonBuilder#setStrictness(Strictness)}. If no + * {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness has been set} the created + * writer will have a strictness of {@link Strictness#LEGACY_STRICT}. Otherwise, this strictness of + * the {@code Gson} instance will be used for the created writer.
  • *
  • {@link GsonBuilder#setPrettyPrinting()}
  • *
  • {@link GsonBuilder#setFormattingStyle(FormattingStyle)}
  • * @@ -935,9 +938,10 @@ public JsonWriter newJsonWriter(Writer writer) throws IOException { * *

    The following settings are considered: *

    */ public JsonReader newJsonReader(Reader reader) { @@ -949,18 +953,19 @@ public JsonReader newJsonReader(Reader reader) { /** * Writes the JSON for {@code jsonElement} to {@code writer}. * - *

    If the {@code Gson} instance has a not-null strictness setting, this setting will be used for writing the JSON - * regardless of the {@linkplain JsonWriter#getStrictness() strictness} of the provided {@link JsonWriter}. For legacy - * reasons, if the {@code Gson} instance has {@code null} as its strictness setting and the provided {@link JsonWriter} - * has a strictness of {@link Strictness#LEGACY_STRICT}, the JSON will be written in {@linkplain Strictness#LENIENT} - * mode. Note that in both cases the old strictness value of the writer will be restored when this method returns. + *

    If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness setting}, + * this setting will be used for writing the JSON regardless of the {@linkplain JsonWriter#getStrictness() strictness} + * of the provided {@link JsonWriter}. For legacy reasons, if the {@code Gson} instance has no explicit strictness setting + * and the writer does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link Strictness#LENIENT} + * mode.
    + * Note that in all cases the old strictness setting of the writer will be restored when this method returns. * *

    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. * - * @param jsonElement the JSON element to be written. - * @param writer the JSON writer to which the provided element will be written. + * @param jsonElement the JSON element to be written + * @param writer the JSON writer to which the provided element will be written * @throws JsonIOException if there was a problem writing to the writer */ public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOException { @@ -973,7 +978,7 @@ public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOExce if (this.strictness != null) { writer.setStrictness(this.strictness); - } else if (writer.getStrictness() == Strictness.LEGACY_STRICT) { + } else if (writer.getStrictness() != Strictness.STRICT) { writer.setStrictness(Strictness.LENIENT); } @@ -1203,9 +1208,12 @@ private static void assertFullConsumption(Object obj, JsonReader reader) { *

    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. * - *

    The JSON data is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}, - * regardless of the strictness setting of the provided reader. The strictness setting - * of the reader is restored once this method returns. + *

    If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness setting}, + * this setting will be used for reading the JSON regardless of the {@linkplain JsonReader#getStrictness() strictness} + * of the provided {@link JsonReader}. For legacy reasons, if the {@code Gson} instance has no explicit strictness setting + * and the reader does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link Strictness#LENIENT} + * mode.
    + * Note that in all cases the old strictness setting of the reader will be restored when this method returns. * * @param the type of the desired object * @param reader the reader whose next JSON value should be deserialized @@ -1232,11 +1240,12 @@ public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, J *

    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. * - *

    If the {@code Gson} instance has a not-null strictness setting, this setting will be used for reading the JSON - * regardless of the {@linkplain JsonReader#getStrictness() strictness} of the provided {@link JsonReader}. For legacy - * reasons, if the {@code Gson} instance has {@code null} as its strictness setting and the provided {@link JsonReader} - * has a strictness of {@link Strictness#LEGACY_STRICT}, the JSON will be read in {@linkplain Strictness#LENIENT} - * mode. Note that in both cases the old strictness value of the reader will be restored when this method returns. + *

    If the {@code Gson} instance has an {@linkplain GsonBuilder#setStrictness(Strictness) explicit strictness setting}, + * this setting will be used for reading the JSON regardless of the {@linkplain JsonReader#getStrictness() strictness} + * of the provided {@link JsonReader}. For legacy reasons, if the {@code Gson} instance has no explicit strictness setting + * and the reader does not have the strictness {@link Strictness#STRICT}, the JSON will be written in {@link Strictness#LENIENT} + * mode.
    + * Note that in all cases the old strictness setting of the reader will be restored when this method returns. * * @param the type of the desired object * @param reader the reader whose next JSON value should be deserialized @@ -1260,7 +1269,7 @@ public T fromJson(JsonReader reader, TypeToken typeOfT) throws JsonIOExce if (this.strictness != null) { reader.setStrictness(this.strictness); - } else if (reader.getStrictness() == Strictness.LEGACY_STRICT){ + } else if (reader.getStrictness() != Strictness.STRICT) { reader.setStrictness(Strictness.LENIENT); } diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 143c03ed6a..217beebb6e 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -29,6 +29,7 @@ import static com.google.gson.Gson.DEFAULT_USE_JDK_UNSAFE; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.errorprone.annotations.InlineMe; import com.google.gson.annotations.Since; import com.google.gson.annotations.Until; import com.google.gson.internal.$Gson$Preconditions; @@ -51,7 +52,6 @@ import java.util.Map; import java.util.Objects; - /** *

    Use this builder to construct a {@link Gson} instance when you need to set configuration * options other than the default. For {@link Gson} with default configuration, it is simpler to @@ -73,12 +73,16 @@ * .create(); * * - *

    NOTES: + *

    Notes: *

    * * @author Inderjeet Singh @@ -525,18 +529,19 @@ public GsonBuilder setFormattingStyle(FormattingStyle formattingStyle) { /** * Sets the strictness of this builder to {@link Strictness#LENIENT}. * - *

    This method has been deprecated. Please use {@link GsonBuilder#setStrictness(Strictness)} instead. - * Calling this method is equivalent to {@code setStrictness(Strictness.LENIENT)}

    + * @deprecated This method is equivalent to calling {@link #setStrictness(Strictness)} with + * {@link Strictness#LENIENT}: {@code setStrictness(Strictness.LENIENT)} * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern. * @see JsonReader#setStrictness(Strictness) * @see JsonWriter#setStrictness(Strictness) * @see #setStrictness(Strictness) */ + @Deprecated + @InlineMe(replacement = "this.setStrictness(Strictness.LENIENT)", imports = "com.google.gson.Strictness") @CanIgnoreReturnValue public GsonBuilder setLenient() { - strictness = Strictness.LENIENT; - return this; + return setStrictness(Strictness.LENIENT); } /** @@ -549,10 +554,11 @@ public GsonBuilder setLenient() { * * @param strictness the new strictness mode. May not be {@code null}. * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern. - * @see JsonWriter#setStrictness(Strictness) * @see JsonReader#setStrictness(Strictness) + * @see JsonWriter#setStrictness(Strictness) * @since $next-version$ */ + @CanIgnoreReturnValue public GsonBuilder setStrictness(Strictness strictness) { this.strictness = Objects.requireNonNull(strictness); return this; diff --git a/gson/src/main/java/com/google/gson/JsonElement.java b/gson/src/main/java/com/google/gson/JsonElement.java index 23e5654aac..1b440d0532 100644 --- a/gson/src/main/java/com/google/gson/JsonElement.java +++ b/gson/src/main/java/com/google/gson/JsonElement.java @@ -321,7 +321,8 @@ public String toString() { try { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); - jsonWriter.setLenient(true); + // Make writer lenient because toString() must not fail, even if for example JsonPrimitive contains NaN + jsonWriter.setStrictness(Strictness.LENIENT); Streams.write(this, jsonWriter); return stringWriter.toString(); } catch (IOException e) { diff --git a/gson/src/main/java/com/google/gson/JsonParser.java b/gson/src/main/java/com/google/gson/JsonParser.java index 20d3750cea..557d00c8e0 100644 --- a/gson/src/main/java/com/google/gson/JsonParser.java +++ b/gson/src/main/java/com/google/gson/JsonParser.java @@ -41,7 +41,7 @@ public JsonParser() {} * An exception is thrown if the JSON string has multiple top-level JSON elements, * or if there is trailing data. * - *

    The JSON string is parsed in {@linkplain JsonReader#setLenient(boolean) lenient mode}. + *

    The JSON string is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}. * * @param json JSON text * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON @@ -57,7 +57,7 @@ public static JsonElement parseString(String json) throws JsonSyntaxException { * An exception is thrown if the JSON string has multiple top-level JSON elements, * or if there is trailing data. * - *

    The JSON data is parsed in {@linkplain JsonReader#setLenient(boolean) lenient mode}. + *

    The JSON data is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}. * * @param reader JSON text * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON @@ -87,8 +87,8 @@ public static JsonElement parseReader(Reader reader) throws JsonIOException, Jso * 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. * - *

    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 + *

    The JSON data is parsed in {@linkplain JsonReader#setStrictness(Strictness) lenient mode}, + * regardless of the strictness setting of the provided reader. The strictness setting * of the reader is restored once this method returns. * * @throws JsonParseException if there is an IOException or if the specified @@ -97,8 +97,8 @@ public static JsonElement parseReader(Reader reader) throws JsonIOException, Jso */ public static JsonElement parseReader(JsonReader reader) throws JsonIOException, JsonSyntaxException { - boolean lenient = reader.isLenient(); - reader.setLenient(true); + Strictness strictness = reader.getStrictness(); + reader.setStrictness(Strictness.LENIENT); try { return Streams.parse(reader); } catch (StackOverflowError e) { @@ -106,7 +106,7 @@ public static JsonElement parseReader(JsonReader reader) } catch (OutOfMemoryError e) { throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e); } finally { - reader.setLenient(lenient); + reader.setStrictness(strictness); } } diff --git a/gson/src/main/java/com/google/gson/JsonStreamParser.java b/gson/src/main/java/com/google/gson/JsonStreamParser.java index cbc2883ca8..7d2629368b 100644 --- a/gson/src/main/java/com/google/gson/JsonStreamParser.java +++ b/gson/src/main/java/com/google/gson/JsonStreamParser.java @@ -28,7 +28,7 @@ /** * A streaming parser that allows reading of multiple {@link JsonElement}s from the specified reader * asynchronously. The JSON data is parsed in lenient mode, see also - * {@link JsonReader#setLenient(boolean)}. + * {@link JsonReader#setStrictness(Strictness)}. * *

    This class is conditionally thread-safe (see Item 70, Effective Java second edition). To * properly use this class across multiple threads, you will need to add some external @@ -66,7 +66,7 @@ public JsonStreamParser(String json) { */ public JsonStreamParser(Reader reader) { parser = new JsonReader(reader); - parser.setLenient(true); + parser.setStrictness(Strictness.LENIENT); lock = new Object(); } diff --git a/gson/src/main/java/com/google/gson/Strictness.java b/gson/src/main/java/com/google/gson/Strictness.java index daf086a05f..f3bd3fe08f 100644 --- a/gson/src/main/java/com/google/gson/Strictness.java +++ b/gson/src/main/java/com/google/gson/Strictness.java @@ -1,17 +1,17 @@ package com.google.gson; -import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; /** * Modes that indicate how strictly a JSON {@linkplain JsonReader reader} or * {@linkplain JsonWriter writer} follows the syntax laid out in the * RFC 8259 JSON specification. * - *

    You can look at {@link JsonWriter#setStrictness(Strictness)} to see how the strictness - * affects the {@link JsonWriter} and you can look at - * {@link JsonReader#setStrictness(Strictness)} to see how the strictness - * affects the {@link JsonReader}.

    + *

    You can look at {@link JsonReader#setStrictness(Strictness)} to see how the strictness + * affects the {@link JsonReader} and you can look at + * {@link JsonWriter#setStrictness(Strictness)} to see how the strictness + * affects the {@link JsonWriter}.

    * * @see JsonReader#setStrictness(Strictness) * @see JsonWriter#setStrictness(Strictness) diff --git a/gson/src/main/java/com/google/gson/TypeAdapter.java b/gson/src/main/java/com/google/gson/TypeAdapter.java index 5fdea225a5..d15b1e080c 100644 --- a/gson/src/main/java/com/google/gson/TypeAdapter.java +++ b/gson/src/main/java/com/google/gson/TypeAdapter.java @@ -134,10 +134,10 @@ public TypeAdapter() { /** * Converts {@code value} to a JSON document and writes it to {@code out}. - * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson} - * method, this write is strict. Create a {@link - * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call - * {@link #write(JsonWriter, Object)} for lenient writing. + * The strictness {@link Strictness#LEGACY_STRICT} is used for writing the JSON data. + * To use a different strictness setting create a {@link JsonWriter}, call its + * {@link JsonWriter#setStrictness(Strictness)} method and then use + * {@link #write(JsonWriter, Object)} for writing. * * @param value the Java object to convert. May be null. * @since 2.2 @@ -207,10 +207,11 @@ public final TypeAdapter nullSafe() { } /** - * Converts {@code value} to a JSON document. Unlike Gson's similar {@link - * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link - * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call - * {@link #write(JsonWriter, Object)} for lenient writing. + * Converts {@code value} to a JSON document. + * The strictness {@link Strictness#LEGACY_STRICT} is used for writing the JSON data. + * To use a different strictness setting create a {@link JsonWriter}, call its + * {@link JsonWriter#setStrictness(Strictness)} method and then use + * {@link #write(JsonWriter, Object)} for writing. * * @throws JsonIOException wrapping {@code IOException}s thrown by {@link #write(JsonWriter, Object)} * @param value the Java object to convert. May be null. @@ -253,10 +254,10 @@ public final JsonElement toJsonTree(T value) { public abstract T read(JsonReader in) throws IOException; /** - * Converts the JSON document in {@code in} to a Java object. Unlike Gson's - * similar {@link Gson#fromJson(Reader, Class) fromJson} method, this - * read is strict. Create a {@link JsonReader#setLenient(boolean) lenient} - * {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading. + * Converts the JSON document in {@code in} to a Java object. The strictness + * {@link Strictness#LEGACY_STRICT} is used for reading the JSON data. To use a different + * strictness setting create a {@link JsonReader}, call its {@link JsonReader#setStrictness(Strictness)} + * method and then use {@link #read(JsonReader)} for reading. * *

    No exception is thrown if the JSON data has multiple top-level JSON elements, * or if there is trailing data. @@ -270,10 +271,10 @@ public final T fromJson(Reader in) throws IOException { } /** - * Converts the JSON document in {@code json} to a Java object. Unlike Gson's - * similar {@link Gson#fromJson(String, Class) fromJson} method, this read is - * strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code - * JsonReader} and call {@link #read(JsonReader)} for lenient reading. + * Converts the JSON document in {@code json} to a Java object. The strictness + * {@link Strictness#LEGACY_STRICT} is used for reading the JSON data. To use a different + * strictness setting create a {@link JsonReader}, call its {@link JsonReader#setStrictness(Strictness)} + * method and then use {@link #read(JsonReader)} for reading. * *

    No exception is thrown if the JSON data has multiple top-level JSON elements, * or if there is trailing data. diff --git a/gson/src/main/java/com/google/gson/stream/JsonReader.java b/gson/src/main/java/com/google/gson/stream/JsonReader.java index 8678c8b291..dfa4b6e8f4 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonReader.java +++ b/gson/src/main/java/com/google/gson/stream/JsonReader.java @@ -182,7 +182,7 @@ *

    Prefixing JSON files with ")]}'\n" makes them non-executable * by {@code