From dc4e61ac7b2ef7750d5ef8a564514290cf65df18 Mon Sep 17 00:00:00 2001 From: JFronny Date: Tue, 17 May 2022 21:20:10 +0200 Subject: [PATCH] Several breaking changes --- .gitignore | 1 + README.md | 14 ++- gson/src/main/java/com/google/gson/Gson.java | 60 +++++------ .../java/com/google/gson/GsonBuilder.java | 26 ++--- .../google/gson/internal/DefaultConfig.java | 23 +++++ .../gson/internal/bind/ArrayTypeAdapter.java | 9 +- .../bind/CollectionTypeAdapterFactory.java | 15 ++- .../com/google/gson/stream/JsonReader.java | 99 +++++++++++-------- .../com/google/gson/stream/JsonWriter.java | 27 ++++- .../test/java/com/google/gson/GsonTest.java | 8 +- .../java/com/google/gson/MixedStreamTest.java | 2 +- .../com/google/gson/ToNumberPolicyTest.java | 8 +- .../com/google/gson/functional/ArrayTest.java | 6 +- .../gson/functional/CollectionTest.java | 6 +- .../gson/functional/ConcurrencyTest.java | 4 +- .../functional/CustomDeserializerTest.java | 47 +++------ .../functional/CustomTypeAdaptersTest.java | 15 +-- .../functional/DefaultTypeAdaptersTest.java | 6 +- .../functional/EnumWithObfuscatedTest.java | 58 ----------- .../gson/functional/ExposeFieldsTest.java | 1 + .../gson/functional/InstanceCreatorTest.java | 27 ++--- .../functional/InternationalizationTest.java | 2 +- .../JavaUtilConcurrentAtomicTest.java | 4 +- .../google/gson/functional/JavaUtilTest.java | 4 +- .../JsonAdapterAnnotationOnClassesTest.java | 4 +- .../JsonAdapterAnnotationOnFieldsTest.java | 3 +- .../gson/functional/JsonParserTest.java | 14 +-- .../google/gson/functional/LeniencyTest.java | 2 +- .../functional/MapAsArrayTypeAdapterTest.java | 2 +- .../com/google/gson/functional/MapTest.java | 2 +- .../functional/NullObjectAndFieldTest.java | 10 +- .../google/gson/functional/ObjectTest.java | 2 +- .../functional/PrimitiveCharacterTest.java | 3 +- .../google/gson/functional/PrimitiveTest.java | 26 +++-- .../google/gson/functional/SecurityTest.java | 2 +- .../functional/StreamingTypeAdaptersTest.java | 4 +- .../gson/functional/TreeTypeAdaptersTest.java | 1 + .../functional/TypeAdapterPrecedenceTest.java | 9 +- .../gson/internal/sql/SqlTypesGsonTest.java | 6 +- .../google/gson/{ => jf}/CommentsTest.java | 5 +- .../java/com/google/gson/jf/ExtraAssert.java | 28 ++++++ .../java/com/google/gson/jf/Json5Test.java | 49 +++++++++ .../gson/jf/LenientCollectionCommaTest.java | 22 +++++ .../google/gson/jf/LenientCollectionTest.java | 45 +++++++++ .../com/google/gson/jf/OmitQuotesTest.java | 33 +++++++ .../regression/JsonAdapterNullSafeTest.java | 2 +- .../google/gson/stream/JsonReaderTest.java | 36 +++---- 47 files changed, 472 insertions(+), 310 deletions(-) create mode 100644 gson/src/main/java/com/google/gson/internal/DefaultConfig.java delete mode 100644 gson/src/test/java/com/google/gson/functional/EnumWithObfuscatedTest.java rename gson/src/test/java/com/google/gson/{ => jf}/CommentsTest.java (86%) create mode 100644 gson/src/test/java/com/google/gson/jf/ExtraAssert.java create mode 100644 gson/src/test/java/com/google/gson/jf/Json5Test.java create mode 100644 gson/src/test/java/com/google/gson/jf/LenientCollectionCommaTest.java create mode 100644 gson/src/test/java/com/google/gson/jf/LenientCollectionTest.java create mode 100644 gson/src/test/java/com/google/gson/jf/OmitQuotesTest.java diff --git a/.gitignore b/.gitignore index 2fc591bd..855570ff 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ build .DS_Store examples/android-proguard-example/gen +.attach_pid* \ No newline at end of file diff --git a/README.md b/README.md index bf3f486b..96160336 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,17 @@ # About this fork -This fork of Gson adds support for writing comments in JsonWriter. -To do this, JsonWriter and CommentsTest were adjusted. +This fork of Gson adds several features which were originally denied due to Gson's maintenance status. +Among them are: +- support for writing comments +- collections with single entries without array syntax (`"single entry"` can become a `List` or array) +- stricter leniency enforcement from the main Gson class (`toJson`, `fromJson` will respect the choice) +- slightly better error messages +- skipping empty entries in arrays (`["one",, "two",]`) +- optionally omitting quotes around entry names like in Json5 +- support for hexadecimal integers + +This means the only thing lacking for Json5 support is trailing commas in objects + Please be aware that I also increased the minimum java version to 11 To use this, add the following: ```groovy diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index b6e714da..95938a97 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -35,12 +35,7 @@ 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; -import com.google.gson.internal.LazilyParsedNumber; -import com.google.gson.internal.Primitives; -import com.google.gson.internal.Streams; +import com.google.gson.internal.*; import com.google.gson.internal.bind.ArrayTypeAdapter; import com.google.gson.internal.bind.CollectionTypeAdapterFactory; import com.google.gson.internal.bind.DateTypeAdapter; @@ -104,23 +99,6 @@ import com.google.gson.stream.MalformedJsonException; * @author Jesse Wilson */ public final class Gson { - static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; - static final boolean DEFAULT_LENIENT = false; - static final boolean DEFAULT_PRETTY_PRINT = false; - static final boolean DEFAULT_ESCAPE_HTML = true; - static final boolean DEFAULT_SERIALIZE_NULLS = false; - static final boolean DEFAULT_COMPLEX_MAP_KEYS = false; - static final boolean DEFAULT_DUPLICATE_MAP_KEYS = false; - static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false; - static final boolean DEFAULT_USE_JDK_UNSAFE = true; - static final String DEFAULT_DATE_PATTERN = null; - static final FieldNamingStrategy DEFAULT_FIELD_NAMING_STRATEGY = FieldNamingPolicy.IDENTITY; - static final ToNumberStrategy DEFAULT_OBJECT_TO_NUMBER_STRATEGY = ToNumberPolicy.DOUBLE; - static final ToNumberStrategy DEFAULT_NUMBER_TO_NUMBER_STRATEGY = ToNumberPolicy.LAZILY_PARSED_NUMBER; - - private static final TypeToken NULL_KEY_SURROGATE = TypeToken.get(Object.class); - private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; - /** * This thread local guards against reentrant calls to getAdapter(). In * certain object graphs, creating an adapter for a type may recursively @@ -148,6 +126,7 @@ public final class Gson { final boolean htmlSafe; final boolean prettyPrinting; final boolean lenient; + final boolean omitQuotes; final boolean serializeSpecialFloatingPointValues; final boolean useJdkUnsafe; final String datePattern; @@ -195,21 +174,21 @@ public final class Gson { * */ public Gson() { - this(Excluder.DEFAULT, DEFAULT_FIELD_NAMING_STRATEGY, - Collections.>emptyMap(), DEFAULT_SERIALIZE_NULLS, - DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_DUPLICATE_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML, - DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES, - DEFAULT_USE_JDK_UNSAFE, - LongSerializationPolicy.DEFAULT, DEFAULT_DATE_PATTERN, DateFormat.DEFAULT, DateFormat.DEFAULT, + this(Excluder.DEFAULT, DefaultConfig.DEFAULT_FIELD_NAMING_STRATEGY, + Collections.>emptyMap(), DefaultConfig.DEFAULT_SERIALIZE_NULLS, + DefaultConfig.DEFAULT_COMPLEX_MAP_KEYS, DefaultConfig.DEFAULT_DUPLICATE_MAP_KEYS, DefaultConfig.DEFAULT_JSON_NON_EXECUTABLE, DefaultConfig.DEFAULT_ESCAPE_HTML, + DefaultConfig.DEFAULT_PRETTY_PRINT, true, DefaultConfig.DEFAULT_OMIT_QUOTES, DefaultConfig.DEFAULT_SPECIALIZE_FLOAT_VALUES, + DefaultConfig.DEFAULT_USE_JDK_UNSAFE, + LongSerializationPolicy.DEFAULT, DefaultConfig.DEFAULT_DATE_PATTERN, DateFormat.DEFAULT, DateFormat.DEFAULT, Collections.emptyList(), Collections.emptyList(), - Collections.emptyList(), DEFAULT_OBJECT_TO_NUMBER_STRATEGY, DEFAULT_NUMBER_TO_NUMBER_STRATEGY, + Collections.emptyList(), DefaultConfig.DEFAULT_OBJECT_TO_NUMBER_STRATEGY, DefaultConfig.DEFAULT_NUMBER_TO_NUMBER_STRATEGY, Collections.emptyList()); } Gson(Excluder excluder, FieldNamingStrategy fieldNamingStrategy, Map> instanceCreators, boolean serializeNulls, boolean complexMapKeySerialization, boolean duplicateMapKeyDeserialization, boolean generateNonExecutableGson, boolean htmlSafe, - boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues, + boolean prettyPrinting, boolean lenient, boolean omitQuotes, boolean serializeSpecialFloatingPointValues, boolean useJdkUnsafe, LongSerializationPolicy longSerializationPolicy, String datePattern, int dateStyle, int timeStyle, List builderFactories, @@ -228,6 +207,7 @@ public final class Gson { this.htmlSafe = htmlSafe; this.prettyPrinting = prettyPrinting; this.lenient = lenient; + this.omitQuotes = omitQuotes; this.serializeSpecialFloatingPointValues = serializeSpecialFloatingPointValues; this.useJdkUnsafe = useJdkUnsafe; this.longSerializationPolicy = longSerializationPolicy; @@ -481,7 +461,7 @@ public final class Gson { */ @SuppressWarnings("unchecked") public TypeAdapter getAdapter(TypeToken type) { - TypeAdapter cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type); + TypeAdapter cached = typeTokenCache.get(type == null ? DefaultConfig.NULL_KEY_SURROGATE : type); if (cached != null) { return (TypeAdapter) cached; } @@ -745,7 +725,9 @@ public final class Gson { public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException { TypeAdapter adapter = getAdapter(TypeToken.get(typeOfSrc)); boolean oldLenient = writer.isLenient(); - writer.setLenient(true); + writer.setLenient(lenient); + boolean oldOmitQuotes = writer.getOmitQuotes(); + writer.setOmitQuotes(omitQuotes); boolean oldHtmlSafe = writer.isHtmlSafe(); writer.setHtmlSafe(htmlSafe); boolean oldSerializeNulls = writer.getSerializeNulls(); @@ -760,6 +742,7 @@ public final class Gson { throw error; } finally { writer.setLenient(oldLenient); + writer.setOmitQuotes(oldOmitQuotes); writer.setHtmlSafe(oldHtmlSafe); writer.setSerializeNulls(oldSerializeNulls); } @@ -804,12 +787,13 @@ public final class Gson { *
  • {@link GsonBuilder#generateNonExecutableJson()}
  • *
  • {@link GsonBuilder#serializeNulls()}
  • *
  • {@link GsonBuilder#setLenient()}
  • + *
  • {@link GsonBuilder#setOmitQuotes()}
  • *
  • {@link GsonBuilder#setPrettyPrinting()}
  • * */ public JsonWriter newJsonWriter(Writer writer) throws IOException { if (generateNonExecutableJson) { - writer.write(JSON_NON_EXECUTABLE_PREFIX); + writer.write(DefaultConfig.JSON_NON_EXECUTABLE_PREFIX); } JsonWriter jsonWriter = new JsonWriter(writer); if (prettyPrinting) { @@ -817,6 +801,7 @@ public final class Gson { } jsonWriter.setHtmlSafe(htmlSafe); jsonWriter.setLenient(lenient); + jsonWriter.setOmitQuotes(omitQuotes); jsonWriter.setSerializeNulls(serializeNulls); return jsonWriter; } @@ -841,7 +826,9 @@ public final class Gson { */ public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOException { boolean oldLenient = writer.isLenient(); - writer.setLenient(true); + writer.setLenient(lenient); + boolean oldOmitQuotes = writer.getOmitQuotes(); + writer.setOmitQuotes(omitQuotes); boolean oldHtmlSafe = writer.isHtmlSafe(); writer.setHtmlSafe(htmlSafe); boolean oldSerializeNulls = writer.getSerializeNulls(); @@ -856,6 +843,7 @@ public final class Gson { throw error; } finally { writer.setLenient(oldLenient); + writer.setOmitQuotes(oldOmitQuotes); writer.setHtmlSafe(oldHtmlSafe); writer.setSerializeNulls(oldSerializeNulls); } @@ -989,7 +977,7 @@ public final class Gson { public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { boolean isEmpty = true; boolean oldLenient = reader.isLenient(); - reader.setLenient(true); + reader.setLenient(lenient); try { reader.peek(); isEmpty = false; diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index bc97aec5..432f7b2b 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -35,18 +35,7 @@ import com.google.gson.internal.sql.SqlTypesSupport; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; -import static com.google.gson.Gson.DEFAULT_COMPLEX_MAP_KEYS; -import static com.google.gson.Gson.DEFAULT_DATE_PATTERN; -import static com.google.gson.Gson.DEFAULT_DUPLICATE_MAP_KEYS; -import static com.google.gson.Gson.DEFAULT_ESCAPE_HTML; -import static com.google.gson.Gson.DEFAULT_JSON_NON_EXECUTABLE; -import static com.google.gson.Gson.DEFAULT_LENIENT; -import static com.google.gson.Gson.DEFAULT_NUMBER_TO_NUMBER_STRATEGY; -import static com.google.gson.Gson.DEFAULT_OBJECT_TO_NUMBER_STRATEGY; -import static com.google.gson.Gson.DEFAULT_PRETTY_PRINT; -import static com.google.gson.Gson.DEFAULT_SERIALIZE_NULLS; -import static com.google.gson.Gson.DEFAULT_SPECIALIZE_FLOAT_VALUES; -import static com.google.gson.Gson.DEFAULT_USE_JDK_UNSAFE; +import static com.google.gson.internal.DefaultConfig.*; /** *

    Use this builder to construct a {@link Gson} instance when you need to set configuration @@ -100,6 +89,7 @@ public final class GsonBuilder { private boolean prettyPrinting = DEFAULT_PRETTY_PRINT; private boolean generateNonExecutableJson = DEFAULT_JSON_NON_EXECUTABLE; private boolean lenient = DEFAULT_LENIENT; + private boolean omitQuotes = DEFAULT_OMIT_QUOTES; private boolean useJdkUnsafe = DEFAULT_USE_JDK_UNSAFE; private ToNumberStrategy objectToNumberStrategy = DEFAULT_OBJECT_TO_NUMBER_STRATEGY; private ToNumberStrategy numberToNumberStrategy = DEFAULT_NUMBER_TO_NUMBER_STRATEGY; @@ -131,6 +121,7 @@ public final class GsonBuilder { this.escapeHtmlChars = gson.htmlSafe; this.prettyPrinting = gson.prettyPrinting; this.lenient = gson.lenient; + this.omitQuotes = gson.omitQuotes; this.serializeSpecialFloatingPointValues = gson.serializeSpecialFloatingPointValues; this.longSerializationPolicy = gson.longSerializationPolicy; this.datePattern = gson.datePattern; @@ -456,6 +447,15 @@ public final class GsonBuilder { return this; } + /** + * By default, Gson always writes quotes around entry names. + * This option allows omitting quotes if the name is alphanumeric. + */ + public GsonBuilder setOmitQuotes() { + omitQuotes = true; + return this; + } + /** * By default, Gson escapes HTML characters such as < > etc. Use this option to configure * Gson to pass-through HTML characters as is. @@ -694,7 +694,7 @@ public final class GsonBuilder { return new Gson(excluder, fieldNamingPolicy, new HashMap<>(instanceCreators), serializeNulls, complexMapKeySerialization, duplicateMapKeyDeserialization, - generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient, + generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient, omitQuotes, serializeSpecialFloatingPointValues, useJdkUnsafe, longSerializationPolicy, datePattern, dateStyle, timeStyle, new ArrayList<>(this.factories), new ArrayList<>(this.hierarchyFactories), factories, diff --git a/gson/src/main/java/com/google/gson/internal/DefaultConfig.java b/gson/src/main/java/com/google/gson/internal/DefaultConfig.java new file mode 100644 index 00000000..c2eb3140 --- /dev/null +++ b/gson/src/main/java/com/google/gson/internal/DefaultConfig.java @@ -0,0 +1,23 @@ +package com.google.gson.internal; + +import com.google.gson.*; +import com.google.gson.reflect.*; + +public class DefaultConfig { + public static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; + public static final boolean DEFAULT_LENIENT = false; + public static final boolean DEFAULT_OMIT_QUOTES = false; + public static final boolean DEFAULT_PRETTY_PRINT = false; + public static final boolean DEFAULT_ESCAPE_HTML = true; + public static final boolean DEFAULT_SERIALIZE_NULLS = false; + public static final boolean DEFAULT_COMPLEX_MAP_KEYS = false; + public static final boolean DEFAULT_DUPLICATE_MAP_KEYS = false; + public static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false; + public static final boolean DEFAULT_USE_JDK_UNSAFE = true; + public static final String DEFAULT_DATE_PATTERN = null; + public static final FieldNamingStrategy DEFAULT_FIELD_NAMING_STRATEGY = FieldNamingPolicy.IDENTITY; + public static final ToNumberStrategy DEFAULT_OBJECT_TO_NUMBER_STRATEGY = ToNumberPolicy.DOUBLE; + public static final ToNumberStrategy DEFAULT_NUMBER_TO_NUMBER_STRATEGY = ToNumberPolicy.LAZILY_PARSED_NUMBER; + public static final TypeToken NULL_KEY_SURROGATE = TypeToken.get(Object.class); + public static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; +} diff --git a/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java index efaa834f..be5b42ed 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java @@ -65,6 +65,13 @@ public final class ArrayTypeAdapter extends TypeAdapter { in.nextNull(); return null; } + Object array; + if (in.isLenient() && in.peek() != JsonToken.BEGIN_ARRAY) { + // Coerce + array = Array.newInstance(componentType, 1); + Array.set(array, 0, componentTypeAdapter.read(in)); + return array; + } List list = new ArrayList<>(); in.beginArray(); @@ -75,7 +82,7 @@ public final class ArrayTypeAdapter extends TypeAdapter { in.endArray(); int size = list.size(); - Object array = Array.newInstance(componentType, size); + array = Array.newInstance(componentType, size); for (int i = 0; i < size; i++) { Array.set(array, i, list.get(i)); } diff --git a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java index 8c65491a..1d7b90f2 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java @@ -77,12 +77,17 @@ public final class CollectionTypeAdapterFactory implements TypeAdapterFactory { } Collection collection = constructor.construct(); - in.beginArray(); - while (in.hasNext()) { - E instance = elementTypeAdapter.read(in); - collection.add(instance); + if (!in.isLenient() || in.peek() == JsonToken.BEGIN_ARRAY) { + in.beginArray(); + while (in.hasNext()) { + E instance = elementTypeAdapter.read(in); + collection.add(instance); + } + in.endArray(); + } else { + // Coerce + collection.add(elementTypeAdapter.read(in)); } - in.endArray(); return collection; } 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 2984d19c..fe990704 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonReader.java +++ b/gson/src/main/java/com/google/gson/stream/JsonReader.java @@ -16,7 +16,7 @@ package com.google.gson.stream; -import com.google.gson.internal.JsonReaderInternalAccess; +import com.google.gson.internal.*; import com.google.gson.internal.bind.JsonTreeReader; import java.io.Closeable; import java.io.EOFException; @@ -226,7 +226,7 @@ public class JsonReader implements Closeable { private final Reader in; /** True to accept non-spec compliant JSON */ - private boolean lenient = false; + private boolean lenient = DefaultConfig.DEFAULT_LENIENT; static final int BUFFER_SIZE = 1024; /** @@ -315,8 +315,7 @@ public class JsonReader implements Closeable { *
  • Names that are unquoted or {@code 'single quoted'}. *
  • Strings that are unquoted or {@code 'single quoted'}. *
  • Array elements separated by {@code ;} instead of {@code ,}. - *
  • Unnecessary array separators. These are interpreted as if null - * was the omitted value. + *
  • Unnecessary array separators. These are ignored. *
  • Names and values separated by {@code =} or {@code =>} instead of * {@code :}. *
  • Name/value pairs separated by {@code ;} instead of {@code ,}. @@ -473,7 +472,7 @@ public class JsonReader implements Closeable { case ',': break; default: - throw syntaxError("Unterminated array"); + throw syntaxError("Unterminated array at " + (char)c); } } else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) { stack[stackSize - 1] = JsonScope.DANGLING_NAME; @@ -488,7 +487,7 @@ public class JsonReader implements Closeable { case ',': break; default: - throw syntaxError("Unterminated object"); + throw syntaxError("Unterminated object at " + (char)c); } } int c = nextNonWhitespace(true); @@ -546,35 +545,8 @@ public class JsonReader implements Closeable { throw new IllegalStateException("JsonReader is closed"); } - int c = nextNonWhitespace(true); - switch (c) { - case ']': - if (peekStack == JsonScope.EMPTY_ARRAY) { - return peeked = PEEKED_END_ARRAY; - } - // fall-through to handle ",]" - case ';': - case ',': - // In lenient mode, a 0-length literal in an array means 'null'. - if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) { - checkLenient(); - pos--; - return peeked = PEEKED_NULL; - } else { - throw syntaxError("Unexpected value"); - } - case '\'': - checkLenient(); - return peeked = PEEKED_SINGLE_QUOTED; - case '"': - return peeked = PEEKED_DOUBLE_QUOTED; - case '[': - return peeked = PEEKED_BEGIN_ARRAY; - case '{': - return peeked = PEEKED_BEGIN_OBJECT; - default: - pos--; // Don't consume the first character in a literal value. - } + if (checkNextNonWhitespace(peekStack)) + return peeked; int result = peekKeyword(); if (result != PEEKED_NONE) { @@ -594,6 +566,45 @@ public class JsonReader implements Closeable { return peeked = PEEKED_UNQUOTED; } + private boolean checkNextNonWhitespace(int peekStack) throws IOException { + int c = nextNonWhitespace(true); + switch (c) { + case ']': + if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) { + peeked = PEEKED_END_ARRAY; + return true; + } + throw syntaxError("Unexpected value"); + case ';': + case ',': + // In lenient mode, a 0-length literal in an array should be skipped. + if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) { + checkLenient(); + //pos--; + //peeked = PEEKED_NULL; + return checkNextNonWhitespace(peekStack); + } else { + throw syntaxError("Unexpected value"); + } + case '\'': + checkLenient(); + peeked = PEEKED_SINGLE_QUOTED; + return true; + case '"': + peeked = PEEKED_DOUBLE_QUOTED; + return true; + case '[': + peeked = PEEKED_BEGIN_ARRAY; + return true; + case '{': + peeked = PEEKED_BEGIN_OBJECT; + return true; + default: + pos--; // Don't consume the first character in a literal value. + } + return false; + } + private int peekKeyword() throws IOException { // Figure out which keyword we're matching against by its first character. char c = buffer[pos]; @@ -1198,10 +1209,14 @@ public class JsonReader implements Closeable { } peeked = PEEKED_BUFFERED; - double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException. - result = (int) asDouble; - if (result != asDouble) { // Make sure no precision was lost casting to 'int'. - throw new NumberFormatException("Expected an int but was " + peekedString + locationString()); + if (peekedString.startsWith("0x")) { + result = Integer.decode(peekedString); + } else { + double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException. + result = (int) asDouble; + if (result != asDouble) { // Make sure no precision was lost casting to 'int'. + throw new NumberFormatException("Expected an int but was " + peekedString + locationString()); + } } peekedString = null; peeked = PEEKED_NONE; @@ -1254,7 +1269,8 @@ public class JsonReader implements Closeable { pos += peekedNumberLength; } peeked = PEEKED_NONE; - } while (count != 0); + } while (count > 0); + if (count < 0) throw new IllegalStateException("Attempt to skip led outside its parent"); pathIndices[stackSize - 1]++; pathNames[stackSize - 1] = "null"; @@ -1452,7 +1468,8 @@ public class JsonReader implements Closeable { String locationString() { int line = lineNumber + 1; int column = pos - lineStart + 1; - return " at line " + line + " column " + column + " path " + getPath(); + String charInterjection = pos < buffer.length ? " (char '" + buffer[pos] + "')" : ""; + return " at line " + line + " column " + column + charInterjection + " path " + getPath(); } private String getPath(boolean usePreviousPath) { diff --git a/gson/src/main/java/com/google/gson/stream/JsonWriter.java b/gson/src/main/java/com/google/gson/stream/JsonWriter.java index dafc3f1e..dbbe1629 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonWriter.java +++ b/gson/src/main/java/com/google/gson/stream/JsonWriter.java @@ -16,6 +16,8 @@ package com.google.gson.stream; +import com.google.gson.internal.*; + import java.io.Closeable; import java.io.Flushable; import java.io.IOException; @@ -190,7 +192,9 @@ public class JsonWriter implements Closeable, Flushable { */ private String separator = ":"; - private boolean lenient; + private boolean lenient = DefaultConfig.DEFAULT_LENIENT; + + private boolean omitQuotes = DefaultConfig.DEFAULT_OMIT_QUOTES; private boolean htmlSafe; @@ -286,11 +290,25 @@ public class JsonWriter implements Closeable, Flushable { return serializeNulls; } + /** + * Sets whether serialized entry names may omit quotes (like in json5) + * The default is false + */ + public final void setOmitQuotes(boolean omitQuotes) { + this.omitQuotes = omitQuotes; + } + + public final boolean getOmitQuotes() { + return omitQuotes; + } + /** * Insert a comment at the current location. * May create a new line. + * This writer MUST be lenient to use this */ public JsonWriter comment(String comment) throws IOException { + if (!lenient) throw new MalformedJsonException("Cannot write comment in non-lenient JsonWriter."); if (comment == null || comment.isBlank()) return this; String[] parts = comment.split("\n"); if (indent == null) { @@ -424,7 +442,12 @@ public class JsonWriter implements Closeable, Flushable { private void writeDeferredName() throws IOException { if (deferredName != null) { beforeName(); - string(deferredName); + if (omitQuotes && deferredName.matches("[a-zA-Z_$][\\w$]*")) { + out.write(deferredName); + } + else { + string(deferredName); + } deferredName = null; } } diff --git a/gson/src/test/java/com/google/gson/GsonTest.java b/gson/src/test/java/com/google/gson/GsonTest.java index 3e002369..a7d2ec28 100644 --- a/gson/src/test/java/com/google/gson/GsonTest.java +++ b/gson/src/test/java/com/google/gson/GsonTest.java @@ -54,7 +54,7 @@ public final class GsonTest extends TestCase { public void testOverridesDefaultExcluder() { Gson gson = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, new HashMap>(), true, false, false, true, false, - true, true, false, true, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, + true, true, false, false, true, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, new ArrayList(), new ArrayList(), new ArrayList(), CUSTOM_OBJECT_TO_NUMBER_STRATEGY, CUSTOM_NUMBER_TO_NUMBER_STRATEGY, @@ -69,7 +69,7 @@ public final class GsonTest extends TestCase { public void testClonedTypeAdapterFactoryListsAreIndependent() { Gson original = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, new HashMap>(), true, false, false, true, false, - true, true, false, true, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, + true, true, false, false, true, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, new ArrayList(), new ArrayList(), new ArrayList(), CUSTOM_OBJECT_TO_NUMBER_STRATEGY, CUSTOM_NUMBER_TO_NUMBER_STRATEGY, @@ -91,7 +91,7 @@ public final class GsonTest extends TestCase { public void testNewJsonWriter_Default() throws IOException { StringWriter writer = new StringWriter(); - JsonWriter jsonWriter = new Gson().newJsonWriter(writer); + JsonWriter jsonWriter = new GsonBuilder().create().newJsonWriter(writer); jsonWriter.beginObject(); jsonWriter.name("test"); jsonWriter.nullValue(); @@ -137,7 +137,7 @@ public final class GsonTest extends TestCase { public void testNewJsonReader_Default() throws IOException { String json = "test"; // String without quotes - JsonReader jsonReader = new Gson().newJsonReader(new StringReader(json)); + JsonReader jsonReader = new GsonBuilder().create().newJsonReader(new StringReader(json)); try { jsonReader.nextString(); fail(); diff --git a/gson/src/test/java/com/google/gson/MixedStreamTest.java b/gson/src/test/java/com/google/gson/MixedStreamTest.java index 00eb4bc8..1d7c85c4 100644 --- a/gson/src/test/java/com/google/gson/MixedStreamTest.java +++ b/gson/src/test/java/com/google/gson/MixedStreamTest.java @@ -203,7 +203,7 @@ public final class MixedStreamTest extends TestCase { StringWriter writer = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(writer); - new GsonBuilder().serializeSpecialFloatingPointValues().create() + new GsonBuilder().setLenient().serializeSpecialFloatingPointValues().create() .toJson(doubles, type, jsonWriter); assertEquals("[NaN,-Infinity,Infinity,-0.0,0.5,0.0]", writer.toString()); diff --git a/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java b/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java index db9898d4..066a1521 100644 --- a/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java +++ b/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java @@ -33,7 +33,7 @@ public class ToNumberPolicyTest extends TestCase { strategy.readNumber(fromString("1e400")); fail(); } catch (MalformedJsonException expected) { - assertEquals("JSON forbids NaN and infinities: Infinity at line 1 column 6 path $", expected.getMessage()); + assertEquals("JSON forbids NaN and infinities: Infinity at line 1 column 6 (char '\0') path $", expected.getMessage()); } try { strategy.readNumber(fromString("\"not-a-number\"")); @@ -74,19 +74,19 @@ public class ToNumberPolicyTest extends TestCase { strategy.readNumber(fromString("NaN")); fail(); } catch (MalformedJsonException expected) { - assertEquals("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $", expected.getMessage()); + assertEquals("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 (char 'N') path $", expected.getMessage()); } try { strategy.readNumber(fromString("Infinity")); fail(); } catch (MalformedJsonException expected) { - assertEquals("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $", expected.getMessage()); + assertEquals("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 (char 'I') path $", expected.getMessage()); } try { strategy.readNumber(fromString("-Infinity")); fail(); } catch (MalformedJsonException expected) { - assertEquals("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $", expected.getMessage()); + assertEquals("Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 (char '-') path $", expected.getMessage()); } } diff --git a/gson/src/test/java/com/google/gson/functional/ArrayTest.java b/gson/src/test/java/com/google/gson/functional/ArrayTest.java index da8be85b..f8213b78 100644 --- a/gson/src/test/java/com/google/gson/functional/ArrayTest.java +++ b/gson/src/test/java/com/google/gson/functional/ArrayTest.java @@ -186,7 +186,7 @@ public class ArrayTest extends TestCase { } public void testArrayOfPrimitivesAsObjectsDeserialization() throws Exception { - String json = "[1,'abc',0.3,1.1,5]"; + String json = "[1,\"abc\",0.3,1.1,5]"; Object[] objs = gson.fromJson(json, Object[].class); assertEquals(1, ((Number)objs[0]).intValue()); assertEquals("abc", objs[1]); @@ -249,8 +249,8 @@ public class ArrayTest extends TestCase { * Regression tests for Issue 272 */ public void testMultidimenstionalArraysDeserialization() { - String json = "[['3m Co','71.72','0.02','0.03','4/2 12:00am','Manufacturing']," - + "['Alcoa Inc','29.01','0.42','1.47','4/1 12:00am','Manufacturing']]"; + String json = "[[\"3m Co\",\"71.72\",\"0.02\",\"0.03\",\"4/2 12:00am\",\"Manufacturing\"]," + + "[\"Alcoa Inc\",\"29.01\",\"0.42\",\"1.47\",\"4/1 12:00am\",\"Manufacturing\"]]"; String[][] items = gson.fromJson(json, String[][].class); assertEquals("3m Co", items[0][0]); assertEquals("Manufacturing", items[1][5]); diff --git a/gson/src/test/java/com/google/gson/functional/CollectionTest.java b/gson/src/test/java/com/google/gson/functional/CollectionTest.java index f113b850..5fa194bb 100644 --- a/gson/src/test/java/com/google/gson/functional/CollectionTest.java +++ b/gson/src/test/java/com/google/gson/functional/CollectionTest.java @@ -101,7 +101,7 @@ public class CollectionTest extends TestCase { } public void testLinkedListDeserialization() { - String json = "['a1','a2']"; + String json = "[\"a1\",\"a2\"]"; Type linkedListType = new TypeToken>() {}.getType(); List list = gson.fromJson(json, linkedListType); assertEquals("a1", list.get(0)); @@ -119,7 +119,7 @@ public class CollectionTest extends TestCase { } public void testQueueDeserialization() { - String json = "['a1','a2']"; + String json = "[\"a1\",\"a2\"]"; Type queueType = new TypeToken>() {}.getType(); Queue queue = gson.fromJson(json, queueType); assertEquals("a1", queue.element()); @@ -385,7 +385,7 @@ public class CollectionTest extends TestCase { assertTrue(json.contains("2")); } public void testSetDeserialization() { - String json = "[{value:1},{value:2}]"; + String json = "[{\"value\":1},{\"value\":2}]"; Type type = new TypeToken>() {}.getType(); Set set = gson.fromJson(json, type); assertEquals(2, set.size()); diff --git a/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java b/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java index 318c6ac3..99963b63 100644 --- a/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java +++ b/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java @@ -56,7 +56,7 @@ public class ConcurrencyTest extends TestCase { */ public void testSingleThreadDeserialization() { for (int i = 0; i < 10; i++) { - gson.fromJson("{'a':'hello','b':'world','i':1}", MyObject.class); + gson.fromJson("{\"a\":\"hello\",\"b\":\"world\",\"i\":1}", MyObject.class); } } @@ -106,7 +106,7 @@ public class ConcurrencyTest extends TestCase { try { startLatch.await(); for (int i = 0; i < 10; i++) { - gson.fromJson("{'a':'hello','b':'world','i':1}", MyObject.class); + gson.fromJson("{\"a\":\"hello\",\"b\":\"world\",\"i\":1}", MyObject.class); } } catch (Throwable t) { failed.set(true); diff --git a/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java b/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java index 8134ae2d..ca0ba1ee 100644 --- a/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java +++ b/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java @@ -112,12 +112,11 @@ public class CustomDeserializerTest extends TestCase { public void testJsonTypeFieldBasedDeserialization() { String json = "{field1:'abc',field2:'def',__type__:'SUB_TYPE1'}"; - Gson gson = new GsonBuilder().registerTypeAdapter(MyBase.class, new JsonDeserializer() { - @Override public MyBase deserialize(JsonElement json, Type pojoType, - JsonDeserializationContext context) throws JsonParseException { - String type = json.getAsJsonObject().get(MyBase.TYPE_ACCESS).getAsString(); - return context.deserialize(json, SubTypes.valueOf(type).getSubclass()); - } + Gson gson = new GsonBuilder() + .setLenient() + .registerTypeAdapter(MyBase.class, (JsonDeserializer) (json1, pojoType, context) -> { + String type = json1.getAsJsonObject().get(MyBase.TYPE_ACCESS).getAsString(); + return context.deserialize(json1, SubTypes.valueOf(type).getSubclass()); }).create(); SubType1 target = (SubType1) gson.fromJson(json, MyBase.class); assertEquals("abc", target.field1); @@ -150,13 +149,8 @@ public class CustomDeserializerTest extends TestCase { public void testCustomDeserializerReturnsNullForTopLevelObject() { Gson gson = new GsonBuilder() - .registerTypeAdapter(Base.class, new JsonDeserializer() { - @Override - public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - return null; - } - }).create(); + .setLenient() + .registerTypeAdapter(Base.class, (JsonDeserializer) (json, typeOfT, context) -> null).create(); String json = "{baseName:'Base',subName:'SubRevised'}"; Base target = gson.fromJson(json, Base.class); assertNull(target); @@ -164,13 +158,8 @@ public class CustomDeserializerTest extends TestCase { public void testCustomDeserializerReturnsNull() { Gson gson = new GsonBuilder() - .registerTypeAdapter(Base.class, new JsonDeserializer() { - @Override - public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - return null; - } - }).create(); + .setLenient() + .registerTypeAdapter(Base.class, (JsonDeserializer) (json, typeOfT, context) -> null).create(); String json = "{base:{baseName:'Base',subName:'SubRevised'}}"; ClassWithBaseField target = gson.fromJson(json, ClassWithBaseField.class); assertNull(target.base); @@ -178,13 +167,8 @@ public class CustomDeserializerTest extends TestCase { public void testCustomDeserializerReturnsNullForArrayElements() { Gson gson = new GsonBuilder() - .registerTypeAdapter(Base.class, new JsonDeserializer() { - @Override - public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - return null; - } - }).create(); + .setLenient() + .registerTypeAdapter(Base.class, (JsonDeserializer) (json, typeOfT, context) -> null).create(); String json = "[{baseName:'Base'},{baseName:'Base'}]"; Base[] target = gson.fromJson(json, Base[].class); assertNull(target[0]); @@ -193,13 +177,8 @@ public class CustomDeserializerTest extends TestCase { public void testCustomDeserializerReturnsNullForArrayElementsForArrayField() { Gson gson = new GsonBuilder() - .registerTypeAdapter(Base.class, new JsonDeserializer() { - @Override - public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - return null; - } - }).create(); + .setLenient() + .registerTypeAdapter(Base.class, (JsonDeserializer) (json, typeOfT, context) -> null).create(); String json = "{bases:[{baseName:'Base'},{baseName:'Base'}]}"; ClassWithBaseArray target = gson.fromJson(json, ClassWithBaseArray.class); assertNull(target.bases[0]); diff --git a/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java index b14ed52e..4bf78c2a 100644 --- a/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java @@ -264,7 +264,7 @@ public class CustomTypeAdaptersTest extends TestCase { } }); Gson gson = gsonBuilder.create(); - String json = "'0123456789'"; + String json = "\"0123456789\""; byte[] actual = gson.fromJson(json, byte[].class); byte[] expected = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; for (int i = 0; i < actual.length; ++i) { @@ -338,7 +338,7 @@ public class CustomTypeAdaptersTest extends TestCase { .registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter()) .create(); Type setType = new TypeToken>() {}.getType(); - Set setOfHolders = gson.fromJson("['Jacob:Tomaw']", setType); + Set setOfHolders = gson.fromJson("[\"Jacob:Tomaw\"]", setType); assertEquals(1, setOfHolders.size()); StringHolder foo = setOfHolders.iterator().next(); assertEquals("Jacob", foo.part1); @@ -376,7 +376,7 @@ public class CustomTypeAdaptersTest extends TestCase { .registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter()) .create(); Type mapType = new TypeToken>() {}.getType(); - Map mapOfFoo = gson.fromJson("{'foo':'Jacob:Tomaw'}", mapType); + Map mapOfFoo = gson.fromJson("{\"foo\":\"Jacob:Tomaw\"}", mapType); assertEquals(1, mapOfFoo.size()); StringHolder foo = mapOfFoo.get("foo"); assertEquals("Jacob", foo.part1); @@ -394,8 +394,9 @@ public class CustomTypeAdaptersTest extends TestCase { public void testEnsureCustomDeserializerNotInvokedForNullValues() { Gson gson = new GsonBuilder() - .registerTypeAdapter(DataHolder.class, new DataHolderDeserializer()) - .create(); + .setLenient() + .registerTypeAdapter(DataHolder.class, new DataHolderDeserializer()) + .create(); String json = "{wrappedData:null}"; DataHolderWrapper actual = gson.fromJson(json, DataHolderWrapper.class); assertNull(actual.wrappedData); @@ -404,8 +405,8 @@ public class CustomTypeAdaptersTest extends TestCase { // Test created from Issue 352 public void testRegisterHierarchyAdapterForDate() { Gson gson = new GsonBuilder() - .registerTypeHierarchyAdapter(Date.class, new DateTypeAdapter()) - .create(); + .registerTypeHierarchyAdapter(Date.class, new DateTypeAdapter()) + .create(); assertEquals("0", gson.toJson(new Date(0))); assertEquals("0", gson.toJson(new java.sql.Date(0))); assertEquals(new Date(0), gson.fromJson("0", Date.class)); diff --git a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java index 91a4639e..5de95a3e 100644 --- a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java @@ -104,7 +104,7 @@ public class DefaultTypeAdaptersTest extends TestCase { } catch (UnsupportedOperationException expected) { } // Override with a custom type adapter for class. - gson = new GsonBuilder().registerTypeAdapter(Class.class, new MyClassTypeAdapter()).create(); + gson = new GsonBuilder().setLenient().registerTypeAdapter(Class.class, new MyClassTypeAdapter()).create(); assertEquals(String.class, gson.fromJson("java.lang.String", Class.class)); } @@ -410,7 +410,7 @@ public class DefaultTypeAdaptersTest extends TestCase { } public void testDefaultCalendarDeserialization() throws Exception { - Gson gson = new GsonBuilder().create(); + Gson gson = new Gson(); String json = "{year:2009,month:2,dayOfMonth:11,hourOfDay:14,minute:29,second:23}"; Calendar cal = gson.fromJson(json, Calendar.class); assertEquals(2009, cal.get(Calendar.YEAR)); @@ -434,7 +434,7 @@ public class DefaultTypeAdaptersTest extends TestCase { } public void testDefaultGregorianCalendarDeserialization() throws Exception { - Gson gson = new GsonBuilder().create(); + Gson gson = new Gson(); String json = "{year:2009,month:2,dayOfMonth:11,hourOfDay:14,minute:29,second:23}"; GregorianCalendar cal = gson.fromJson(json, GregorianCalendar.class); assertEquals(2009, cal.get(Calendar.YEAR)); diff --git a/gson/src/test/java/com/google/gson/functional/EnumWithObfuscatedTest.java b/gson/src/test/java/com/google/gson/functional/EnumWithObfuscatedTest.java deleted file mode 100644 index 080b81fa..00000000 --- a/gson/src/test/java/com/google/gson/functional/EnumWithObfuscatedTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * 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 com.google.gson.functional; - -import com.google.gson.Gson; -import com.google.gson.annotations.SerializedName; - -import junit.framework.TestCase; - -/** - * Functional tests for enums with Proguard. - * - * @author Young Cha - */ -public class EnumWithObfuscatedTest extends TestCase { - private Gson gson; - - @Override - protected void setUp() throws Exception { - super.setUp(); - gson = new Gson(); - } - - public enum Gender { - @SerializedName("MAIL") - MALE, - - @SerializedName("FEMAIL") - FEMALE - } - - public void testEnumClassWithObfuscated() { - for (Gender enumConstant: Gender.class.getEnumConstants()) { - try { - Gender.class.getField(enumConstant.name()); - fail("Enum is not obfuscated"); - } catch (NoSuchFieldException ignore) { - } - } - - assertEquals(Gender.MALE, gson.fromJson("\"MAIL\"", Gender.class)); - assertEquals("\"MAIL\"", gson.toJson(Gender.MALE, Gender.class)); - } -} diff --git a/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java b/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java index 0430ba49..dd233625 100644 --- a/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java +++ b/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java @@ -38,6 +38,7 @@ public class ExposeFieldsTest extends TestCase { protected void setUp() throws Exception { super.setUp(); gson = new GsonBuilder() + .setLenient() .excludeFieldsWithoutExposeAnnotation() .registerTypeAdapter(SomeInterface.class, new SomeInterfaceInstanceCreator()) .create(); diff --git a/gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java b/gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java index 95e3e3ef..aa49ddaa 100644 --- a/gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java +++ b/gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java @@ -42,12 +42,9 @@ public class InstanceCreatorTest extends TestCase { public void testInstanceCreatorReturnsBaseType() { Gson gson = new GsonBuilder() - .registerTypeAdapter(Base.class, new InstanceCreator() { - @Override public Base createInstance(Type type) { - return new Base(); - } - }) - .create(); + .setLenient() + .registerTypeAdapter(Base.class, (InstanceCreator) type -> new Base()) + .create(); String json = "{baseName:'BaseRevised',subName:'Sub'}"; Base base = gson.fromJson(json, Base.class); assertEquals("BaseRevised", base.baseName); @@ -55,12 +52,9 @@ public class InstanceCreatorTest extends TestCase { public void testInstanceCreatorReturnsSubTypeForTopLevelObject() { Gson gson = new GsonBuilder() - .registerTypeAdapter(Base.class, new InstanceCreator() { - @Override public Base createInstance(Type type) { - return new Sub(); - } - }) - .create(); + .setLenient() + .registerTypeAdapter(Base.class, (InstanceCreator) type -> new Sub()) + .create(); String json = "{baseName:'Base',subName:'SubRevised'}"; Base base = gson.fromJson(json, Base.class); @@ -73,12 +67,9 @@ public class InstanceCreatorTest extends TestCase { public void testInstanceCreatorReturnsSubTypeForField() { Gson gson = new GsonBuilder() - .registerTypeAdapter(Base.class, new InstanceCreator() { - @Override public Base createInstance(Type type) { - return new Sub(); - } - }) - .create(); + .setLenient() + .registerTypeAdapter(Base.class, (InstanceCreator) type -> new Sub()) + .create(); String json = "{base:{baseName:'Base',subName:'SubRevised'}}"; ClassWithBaseField target = gson.fromJson(json, ClassWithBaseField.class); assertTrue(target.base instanceof Sub); diff --git a/gson/src/test/java/com/google/gson/functional/InternationalizationTest.java b/gson/src/test/java/com/google/gson/functional/InternationalizationTest.java index 169c37a5..0597f652 100644 --- a/gson/src/test/java/com/google/gson/functional/InternationalizationTest.java +++ b/gson/src/test/java/com/google/gson/functional/InternationalizationTest.java @@ -65,7 +65,7 @@ public class InternationalizationTest extends TestCase { } public void testStringsWithUnicodeChineseCharactersEscapedDeserialization() throws Exception { - String actual = gson.fromJson("'\\u597d\\u597d\\u597d'", String.class); + String actual = gson.fromJson("\"\\u597d\\u597d\\u597d\"", String.class); assertEquals("\u597d\u597d\u597d", actual); } } diff --git a/gson/src/test/java/com/google/gson/functional/JavaUtilConcurrentAtomicTest.java b/gson/src/test/java/com/google/gson/functional/JavaUtilConcurrentAtomicTest.java index 464892a3..e5b175b3 100644 --- a/gson/src/test/java/com/google/gson/functional/JavaUtilConcurrentAtomicTest.java +++ b/gson/src/test/java/com/google/gson/functional/JavaUtilConcurrentAtomicTest.java @@ -65,7 +65,7 @@ public class JavaUtilConcurrentAtomicTest extends TestCase { Gson gson = new GsonBuilder() .setLongSerializationPolicy(LongSerializationPolicy.STRING) .create(); - AtomicLongHolder target = gson.fromJson("{'value':'10'}", AtomicLongHolder.class); + AtomicLongHolder target = gson.fromJson("{\"value\":\"10\"}", AtomicLongHolder.class); assertEquals(10, target.value.get()); String json = gson.toJson(target); assertEquals("{\"value\":\"10\"}", json); @@ -95,7 +95,7 @@ public class JavaUtilConcurrentAtomicTest extends TestCase { Gson gson = new GsonBuilder() .setLongSerializationPolicy(LongSerializationPolicy.STRING) .create(); - AtomicLongArray target = gson.fromJson("['10', '13', '14']", AtomicLongArray.class); + AtomicLongArray target = gson.fromJson("[\"10\", \"13\", \"14\"]", AtomicLongArray.class); assertEquals(3, target.length()); assertEquals(10, target.get(0)); assertEquals(13, target.get(1)); diff --git a/gson/src/test/java/com/google/gson/functional/JavaUtilTest.java b/gson/src/test/java/com/google/gson/functional/JavaUtilTest.java index 05209653..9ed17bb8 100644 --- a/gson/src/test/java/com/google/gson/functional/JavaUtilTest.java +++ b/gson/src/test/java/com/google/gson/functional/JavaUtilTest.java @@ -36,7 +36,7 @@ public class JavaUtilTest extends TestCase { } public void testCurrency() throws Exception { - CurrencyHolder target = gson.fromJson("{'value':'USD'}", CurrencyHolder.class); + CurrencyHolder target = gson.fromJson("{\"value\":\"USD\"}", CurrencyHolder.class); assertEquals("USD", target.value.getCurrencyCode()); String json = gson.toJson(target); assertEquals("{\"value\":\"USD\"}", json); @@ -52,7 +52,7 @@ public class JavaUtilTest extends TestCase { } public void testProperties() { - Properties props = gson.fromJson("{'a':'v1','b':'v2'}", Properties.class); + Properties props = gson.fromJson("{\"a\":\"v1\",\"b\":\"v2\"}", Properties.class); assertEquals("v1", props.getProperty("a")); assertEquals("v2", props.getProperty("b")); String json = gson.toJson(props); diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java index db939dcd..736ede33 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java @@ -49,7 +49,7 @@ public final class JsonAdapterAnnotationOnClassesTest extends TestCase { // Also invoke the JsonAdapter javadoc sample json = gson.toJson(new User("Inderjeet", "Singh")); assertEquals("{\"name\":\"Inderjeet Singh\"}", json); - User user = gson.fromJson("{'name':'Joel Leitch'}", User.class); + User user = gson.fromJson("{\"name\":\"Joel Leitch\"}", User.class); assertEquals("Joel", user.firstName); assertEquals("Leitch", user.lastName); @@ -94,6 +94,7 @@ public final class JsonAdapterAnnotationOnClassesTest extends TestCase { } }; Gson gson = new GsonBuilder() + .setLenient() .registerTypeAdapter(A.class, serializer) .create(); String json = gson.toJson(new A("abcd")); @@ -113,6 +114,7 @@ public final class JsonAdapterAnnotationOnClassesTest extends TestCase { } }; Gson gson = new GsonBuilder() + .setLenient() .registerTypeAdapter(A.class, deserializer) .create(); String json = gson.toJson(new A("abcd")); diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java index 706fe60f..e6996947 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java @@ -57,12 +57,13 @@ public final class JsonAdapterAnnotationOnFieldsTest extends TestCase { .create(); String json = gson.toJson(new Computer(new User("Inderjeet Singh"))); assertEquals("{\"user\":\"RegisteredUserAdapter\"}", json); - Computer computer = gson.fromJson("{'user':'Inderjeet Singh'}", Computer.class); + Computer computer = gson.fromJson("{\"user\":\"Inderjeet Singh\"}", Computer.class); assertEquals("RegisteredUserAdapter", computer.user.name); } public void testFieldAnnotationTakesPrecedenceOverRegisteredTypeAdapter() { Gson gson = new GsonBuilder() + .setLenient() .registerTypeAdapter(Part.class, new TypeAdapter() { @Override public void write(JsonWriter out, Part part) throws IOException { throw new AssertionError(); diff --git a/gson/src/test/java/com/google/gson/functional/JsonParserTest.java b/gson/src/test/java/com/google/gson/functional/JsonParserTest.java index 965140ba..d87c8981 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonParserTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonParserTest.java @@ -16,13 +16,7 @@ package com.google.gson.functional; -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSyntaxException; +import com.google.gson.*; import com.google.gson.common.TestTypes.BagOfPrimitives; import com.google.gson.common.TestTypes.Nested; import com.google.gson.reflect.TypeToken; @@ -123,9 +117,9 @@ public class JsonParserTest extends TestCase { public void testExtraCommasInArrays() { Type type = new TypeToken>() {}.getType(); - assertEquals(Arrays.asList("a", null, "b", null, null), gson.fromJson("[a,,b,,]", type)); - assertEquals(Arrays.asList(null, null), gson.fromJson("[,]", type)); - assertEquals(Arrays.asList("a", null), gson.fromJson("[a,]", type)); + assertEquals(Arrays.asList("a", "b"), gson.fromJson("[a,,b,,]", type)); + assertEquals(Arrays.asList(), gson.fromJson("[,]", type)); + assertEquals(Arrays.asList("a"), gson.fromJson("[a,]", type)); } public void testExtraCommasInMaps() { diff --git a/gson/src/test/java/com/google/gson/functional/LeniencyTest.java b/gson/src/test/java/com/google/gson/functional/LeniencyTest.java index 6b5375e4..cd662d44 100644 --- a/gson/src/test/java/com/google/gson/functional/LeniencyTest.java +++ b/gson/src/test/java/com/google/gson/functional/LeniencyTest.java @@ -33,7 +33,7 @@ public class LeniencyTest extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); - gson = new GsonBuilder().setLenient().create(); + gson = new Gson(); } public void testLenientFromJson() { diff --git a/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java b/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java index 114c94ec..65ab8bdd 100644 --- a/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java @@ -106,7 +106,7 @@ public class MapAsArrayTypeAdapterTest extends TestCase { } public void testMapWithTypeVariableDeserialization() { - Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create(); + Gson gson = new GsonBuilder().setLenient().enableComplexMapKeySerialization().create(); String json = "{map:[[{x:2,y:3},{x:4,y:5}]]}"; Type type = new TypeToken>(){}.getType(); PointWithProperty map = gson.fromJson(json, type); diff --git a/gson/src/test/java/com/google/gson/functional/MapTest.java b/gson/src/test/java/com/google/gson/functional/MapTest.java index b921bd4f..7adfc549 100644 --- a/gson/src/test/java/com/google/gson/functional/MapTest.java +++ b/gson/src/test/java/com/google/gson/functional/MapTest.java @@ -155,7 +155,7 @@ public class MapTest extends TestCase { assertEquals(123, map.get("null").intValue()); assertNull(map.get(null)); - map = gson.fromJson("{null:123}", typeOfMap); + map = gson.fromJson("{'null':123}", typeOfMap); assertEquals(1, map.size()); assertEquals(123, map.get("null").intValue()); assertNull(map.get(null)); diff --git a/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java index a9b77f1d..49cb18a6 100644 --- a/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java +++ b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java @@ -99,7 +99,7 @@ public class NullObjectAndFieldTest extends TestCase { */ public void testNullWrappedPrimitiveMemberDeserialization() { Gson gson = gsonBuilder.create(); - String json = "{'value':null}"; + String json = "{\"value\":null}"; ClassWithNullWrappedPrimitive target = gson.fromJson(json, ClassWithNullWrappedPrimitive.class); assertNull(target.value); } @@ -223,12 +223,8 @@ public class NullObjectAndFieldTest extends TestCase { public void testCustomTypeAdapterPassesNullDesrialization() { Gson gson = new GsonBuilder() - .registerTypeAdapter(ObjectWithField.class, new JsonDeserializer() { - @Override public ObjectWithField deserialize(JsonElement json, Type type, - JsonDeserializationContext context) { - return context.deserialize(null, type); - } - }).create(); + .setLenient() + .registerTypeAdapter(ObjectWithField.class, (JsonDeserializer) (json, type, context) -> context.deserialize(null, type)).create(); String json = "{value:'value1'}"; ObjectWithField target = gson.fromJson(json, ObjectWithField.class); assertNull(target); diff --git a/gson/src/test/java/com/google/gson/functional/ObjectTest.java b/gson/src/test/java/com/google/gson/functional/ObjectTest.java index e9aa15b2..4abf0805 100644 --- a/gson/src/test/java/com/google/gson/functional/ObjectTest.java +++ b/gson/src/test/java/com/google/gson/functional/ObjectTest.java @@ -346,7 +346,7 @@ public class ObjectTest extends TestCase { return p.new Child(); } }).create(); - String json = "{'value2':3}"; + String json = "{\"value2\":3}"; Parent.Child c = gson.fromJson(json, Parent.Child.class); assertEquals(3, c.value2); } diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java index 69ff1f3f..2a7e1582 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java @@ -16,10 +16,9 @@ package com.google.gson.functional; +import com.google.gson.*; import junit.framework.TestCase; -import com.google.gson.Gson; - /** * Functional tests for Java Character values. * diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java index 6d74cc29..97aca51d 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java @@ -18,11 +18,7 @@ package com.google.gson.functional; import static org.junit.Assert.assertArrayEquals; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSyntaxException; -import com.google.gson.LongSerializationPolicy; +import com.google.gson.*; import com.google.gson.internal.LazilyParsedNumber; import com.google.gson.reflect.TypeToken; import java.io.Serializable; @@ -95,7 +91,7 @@ public class PrimitiveTest extends TestCase { gson.fromJson("2147483648", byte.class); fail(); } catch (JsonSyntaxException e) { - assertEquals("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $", e.getMessage()); + assertEquals("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 (char '\0') path $", e.getMessage()); } } @@ -133,7 +129,7 @@ public class PrimitiveTest extends TestCase { gson.fromJson("2147483648", short.class); fail(); } catch (JsonSyntaxException e) { - assertEquals("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $", e.getMessage()); + assertEquals("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 (char '\0') path $", e.getMessage()); } } @@ -435,7 +431,7 @@ public class PrimitiveTest extends TestCase { } public void testDoubleNaNSerialization() { - Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create(); + Gson gson = new GsonBuilder().setLenient().serializeSpecialFloatingPointValues().create(); double nan = Double.NaN; assertEquals("NaN", gson.toJson(nan)); assertEquals("NaN", gson.toJson(Double.NaN)); @@ -461,7 +457,7 @@ public class PrimitiveTest extends TestCase { } public void testFloatNaNSerialization() { - Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create(); + Gson gson = new GsonBuilder().setLenient().serializeSpecialFloatingPointValues().create(); float nan = Float.NaN; assertEquals("NaN", gson.toJson(nan)); assertEquals("NaN", gson.toJson(Float.NaN)); @@ -495,7 +491,7 @@ public class PrimitiveTest extends TestCase { } public void testDoubleInfinitySerialization() { - Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create(); + Gson gson = new GsonBuilder().setLenient().serializeSpecialFloatingPointValues().create(); double infinity = Double.POSITIVE_INFINITY; assertEquals("Infinity", gson.toJson(infinity)); assertEquals("Infinity", gson.toJson(Double.POSITIVE_INFINITY)); @@ -521,7 +517,7 @@ public class PrimitiveTest extends TestCase { } public void testFloatInfinitySerialization() { - Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create(); + Gson gson = new GsonBuilder().setLenient().serializeSpecialFloatingPointValues().create(); float infinity = Float.POSITIVE_INFINITY; assertEquals("Infinity", gson.toJson(infinity)); assertEquals("Infinity", gson.toJson(Float.POSITIVE_INFINITY)); @@ -555,7 +551,7 @@ public class PrimitiveTest extends TestCase { } public void testNegativeInfinitySerialization() { - Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create(); + Gson gson = new GsonBuilder().setLenient().serializeSpecialFloatingPointValues().create(); double negativeInfinity = Double.NEGATIVE_INFINITY; assertEquals("-Infinity", gson.toJson(negativeInfinity)); assertEquals("-Infinity", gson.toJson(Double.NEGATIVE_INFINITY)); @@ -581,7 +577,7 @@ public class PrimitiveTest extends TestCase { } public void testNegativeInfinityFloatSerialization() { - Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create(); + Gson gson = new GsonBuilder().setLenient().serializeSpecialFloatingPointValues().create(); float negativeInfinity = Float.NEGATIVE_INFINITY; assertEquals("-Infinity", gson.toJson(negativeInfinity)); assertEquals("-Infinity", gson.toJson(Float.NEGATIVE_INFINITY)); @@ -633,9 +629,9 @@ public class PrimitiveTest extends TestCase { String value = "String Blah Blah Blah...1, 2, 3"; try { - gson.fromJson(value, String.class); + new Gson().fromJson(value, String.class); fail(); - } catch (JsonSyntaxException expected) { } + } catch (JsonIOException expected) { } } public void testHtmlCharacterSerialization() throws Exception { diff --git a/gson/src/test/java/com/google/gson/functional/SecurityTest.java b/gson/src/test/java/com/google/gson/functional/SecurityTest.java index aa1c2d45..3c7f63f7 100644 --- a/gson/src/test/java/com/google/gson/functional/SecurityTest.java +++ b/gson/src/test/java/com/google/gson/functional/SecurityTest.java @@ -38,7 +38,7 @@ public class SecurityTest extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); - gsonBuilder = new GsonBuilder(); + gsonBuilder = new GsonBuilder().setLenient(); } public void testNonExecutableJsonSerialization() { diff --git a/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java index 68844120..c516ec87 100644 --- a/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java @@ -159,7 +159,7 @@ public final class StreamingTypeAdaptersTest extends TestCase { out.value(person.name + "," + person.age); } }; - Gson gson = new GsonBuilder().registerTypeAdapter( + Gson gson = new GsonBuilder().setLenient().registerTypeAdapter( Person.class, typeAdapter).create(); Truck truck = new Truck(); truck.horsePower = 1.0D; @@ -175,7 +175,7 @@ public final class StreamingTypeAdaptersTest extends TestCase { gson.fromJson(json, Truck.class); fail(); } catch (JsonSyntaxException expected) {} - gson = new GsonBuilder().registerTypeAdapter(Person.class, typeAdapter.nullSafe()).create(); + gson = new GsonBuilder().setLenient().registerTypeAdapter(Person.class, typeAdapter.nullSafe()).create(); assertEquals("{\"horsePower\":1.0,\"passengers\":[null,\"jesse,30\"]}", gson.toJson(truck, Truck.class)); truck = gson.fromJson(json, Truck.class); diff --git a/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java index f82d92e4..6d5a1058 100644 --- a/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java @@ -54,6 +54,7 @@ public class TreeTypeAdaptersTest extends TestCase { @Override protected void setUp() { gson = new GsonBuilder() + .setLenient() .registerTypeAdapter(Id.class, new IdTreeTypeAdapter()) .create(); course = new Course<>(COURSE_ID, 4, diff --git a/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java b/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java index 5f881530..c87d9f2d 100644 --- a/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java +++ b/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java @@ -34,6 +34,7 @@ import junit.framework.TestCase; public final class TypeAdapterPrecedenceTest extends TestCase { public void testNonstreamingFollowedByNonstreaming() { Gson gson = new GsonBuilder() + .setLenient() .registerTypeAdapter(Foo.class, newSerializer("serializer 1")) .registerTypeAdapter(Foo.class, newSerializer("serializer 2")) .registerTypeAdapter(Foo.class, newDeserializer("deserializer 1")) @@ -45,6 +46,7 @@ public final class TypeAdapterPrecedenceTest extends TestCase { public void testStreamingFollowedByStreaming() { Gson gson = new GsonBuilder() + .setLenient() .registerTypeAdapter(Foo.class, newTypeAdapter("type adapter 1")) .registerTypeAdapter(Foo.class, newTypeAdapter("type adapter 2")) .create(); @@ -54,6 +56,7 @@ public final class TypeAdapterPrecedenceTest extends TestCase { public void testSerializeNonstreamingTypeAdapterFollowedByStreamingTypeAdapter() { Gson gson = new GsonBuilder() + .setLenient() .registerTypeAdapter(Foo.class, newSerializer("serializer")) .registerTypeAdapter(Foo.class, newDeserializer("deserializer")) .registerTypeAdapter(Foo.class, newTypeAdapter("type adapter")) @@ -64,6 +67,7 @@ public final class TypeAdapterPrecedenceTest extends TestCase { public void testStreamingFollowedByNonstreaming() { Gson gson = new GsonBuilder() + .setLenient() .registerTypeAdapter(Foo.class, newTypeAdapter("type adapter")) .registerTypeAdapter(Foo.class, newSerializer("serializer")) .registerTypeAdapter(Foo.class, newDeserializer("deserializer")) @@ -74,6 +78,7 @@ public final class TypeAdapterPrecedenceTest extends TestCase { public void testStreamingHierarchicalFollowedByNonstreaming() { Gson gson = new GsonBuilder() + .setLenient() .registerTypeHierarchyAdapter(Foo.class, newTypeAdapter("type adapter")) .registerTypeAdapter(Foo.class, newSerializer("serializer")) .registerTypeAdapter(Foo.class, newDeserializer("deserializer")) @@ -89,11 +94,12 @@ public final class TypeAdapterPrecedenceTest extends TestCase { .registerTypeHierarchyAdapter(Foo.class, newDeserializer("deserializer")) .create(); assertEquals("\"foo via type adapter\"", gson.toJson(new Foo("foo"))); - assertEquals("foo via type adapter", gson.fromJson("foo", Foo.class).name); + assertEquals("foo via type adapter", gson.fromJson("\"foo\"", Foo.class).name); } public void testStreamingHierarchicalFollowedByNonstreamingHierarchical() { Gson gson = new GsonBuilder() + .setLenient() .registerTypeHierarchyAdapter(Foo.class, newSerializer("serializer")) .registerTypeHierarchyAdapter(Foo.class, newDeserializer("deserializer")) .registerTypeHierarchyAdapter(Foo.class, newTypeAdapter("type adapter")) @@ -104,6 +110,7 @@ public final class TypeAdapterPrecedenceTest extends TestCase { public void testNonstreamingHierarchicalFollowedByNonstreaming() { Gson gson = new GsonBuilder() + .setLenient() .registerTypeHierarchyAdapter(Foo.class, newSerializer("hierarchical")) .registerTypeHierarchyAdapter(Foo.class, newDeserializer("hierarchical")) .registerTypeAdapter(Foo.class, newSerializer("non hierarchical")) diff --git a/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java b/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java index 03e21855..5553fdd0 100644 --- a/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java +++ b/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java @@ -52,7 +52,7 @@ public class SqlTypesGsonTest extends TestCase { } public void testDefaultSqlDateDeserialization() { - String json = "'Dec 3, 2009'"; + String json = "\"Dec 3, 2009\""; java.sql.Date extracted = gson.fromJson(json, java.sql.Date.class); DefaultTypeAdaptersTest.assertEqualsDate(extracted, 2009, 11, 3); } @@ -82,7 +82,7 @@ public class SqlTypesGsonTest extends TestCase { } public void testDefaultSqlTimeDeserialization() { - String json = "'1:18:02 PM'"; + String json = "\"1:18:02 PM\""; Time extracted = gson.fromJson(json, Time.class); DefaultTypeAdaptersTest.assertEqualsTime(extracted, 13, 18, 2); } @@ -98,7 +98,7 @@ public class SqlTypesGsonTest extends TestCase { } public void testDefaultSqlTimestampDeserialization() { - String json = "'Dec 3, 2009 1:18:02 PM'"; + String json = "\"Dec 3, 2009 1:18:02 PM\""; Timestamp extracted = gson.fromJson(json, Timestamp.class); DefaultTypeAdaptersTest.assertEqualsDate(extracted, 2009, 11, 3); DefaultTypeAdaptersTest.assertEqualsTime(extracted, 13, 18, 2); diff --git a/gson/src/test/java/com/google/gson/CommentsTest.java b/gson/src/test/java/com/google/gson/jf/CommentsTest.java similarity index 86% rename from gson/src/test/java/com/google/gson/CommentsTest.java rename to gson/src/test/java/com/google/gson/jf/CommentsTest.java index 306e5aff..874812b7 100644 --- a/gson/src/test/java/com/google/gson/CommentsTest.java +++ b/gson/src/test/java/com/google/gson/jf/CommentsTest.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.google.gson; +package com.google.gson.jf; +import com.google.gson.*; import com.google.gson.reflect.TypeToken; import java.util.Arrays; import java.util.List; @@ -39,7 +40,7 @@ public final class CommentsTest extends TestCase { + " \"c\"\n" + "]"; - List abc = new Gson().fromJson(json, new TypeToken>() {}.getType()); + List abc = new GsonBuilder().setLenient().create().fromJson(json, new TypeToken>() {}.getType()); assertEquals(Arrays.asList("a", "b", "c"), abc); } } diff --git a/gson/src/test/java/com/google/gson/jf/ExtraAssert.java b/gson/src/test/java/com/google/gson/jf/ExtraAssert.java new file mode 100644 index 00000000..320f80f3 --- /dev/null +++ b/gson/src/test/java/com/google/gson/jf/ExtraAssert.java @@ -0,0 +1,28 @@ +package com.google.gson.jf; + +import junit.framework.*; + +import java.util.*; + +public class ExtraAssert { + public static void assertListEquals(List expecteds, List actuals) { + TestCase.assertNotNull(expecteds); + TestCase.assertNotNull(actuals); + TestCase.assertEquals(expecteds.size(), actuals.size()); + for (int i = 0; i < expecteds.size(); i++) { + TestCase.assertEquals(expecteds.get(i), actuals.get(i)); + } + } + + public static class StringArrayBox { + public String[] collection; + } + + public static class StringListBox { + public List collection; + } + + public static class IntListBox { + public List collection; + } +} diff --git a/gson/src/test/java/com/google/gson/jf/Json5Test.java b/gson/src/test/java/com/google/gson/jf/Json5Test.java new file mode 100644 index 00000000..7b828d10 --- /dev/null +++ b/gson/src/test/java/com/google/gson/jf/Json5Test.java @@ -0,0 +1,49 @@ +package com.google.gson.jf; + +import com.google.gson.*; +import junit.framework.*; + +public class Json5Test extends TestCase { + public void testShortExample() { + // Taken from the official example + assertTrue(new Gson().fromJson("{\n" + + " // comments\n" + + " unquoted: 'and you can quote me on that',\n" + + " singleQuotes: 'I can use \"double quotes\" here',\n" + + " lineBreaks: \"Look, Mom! \\\n" + + "No \\\\n's!\",\n" + + " hexadecimal: 0xdecaf,\n" + + " leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n" + + " positiveSign: +1,\n" + + " trailingComma: 'in objects', andIn: ['arrays',],\n" + + " \"backwardsCompatible\": \"with JSON\",\n" + + "}", ExampleModel.class).isDefault()); + } + + public static class ExampleModel { + public String unquoted; + public String singleQuotes; + public String lineBreaks; + public Integer hexadecimal; + public Double leadingDecimalPoint; + public Double andTrailing; + public Integer positiveSign; + public String trailingComma; + public String[] andIn; + public String backwardsCompatible; + + public boolean isDefault() { + return unquoted.equals("and you can quote me on that") + && singleQuotes.equals("I can use \"double quotes\" here") + && lineBreaks.equals("Look, Mom!\nNo \\n's!") + && hexadecimal.equals(0xdecaf) + && leadingDecimalPoint.equals(.8675309) + && andTrailing.equals(8675309.) + && positiveSign.equals(1) + && trailingComma.equals("in objects") + && andIn.length == 1 && andIn[0].equals("arrays") + && backwardsCompatible.equals("with JSON"); + + } + } +} diff --git a/gson/src/test/java/com/google/gson/jf/LenientCollectionCommaTest.java b/gson/src/test/java/com/google/gson/jf/LenientCollectionCommaTest.java new file mode 100644 index 00000000..b8515e4b --- /dev/null +++ b/gson/src/test/java/com/google/gson/jf/LenientCollectionCommaTest.java @@ -0,0 +1,22 @@ +package com.google.gson.jf; + +import com.google.gson.*; +import junit.framework.TestCase; +import java.util.*; + +import static com.google.gson.jf.ExtraAssert.*; + +public class LenientCollectionCommaTest extends TestCase { + private Gson lenient; + private static final String json = "{\"collection\": [1, 2,, 3, , , 4, 5,]}"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + lenient = new GsonBuilder().setLenient().create(); + } + + public void testElementSkipping() { + assertListEquals(List.of(1, 2, 3, 4, 5), lenient.fromJson(json, IntListBox.class).collection); + } +} diff --git a/gson/src/test/java/com/google/gson/jf/LenientCollectionTest.java b/gson/src/test/java/com/google/gson/jf/LenientCollectionTest.java new file mode 100644 index 00000000..c3730bf6 --- /dev/null +++ b/gson/src/test/java/com/google/gson/jf/LenientCollectionTest.java @@ -0,0 +1,45 @@ +package com.google.gson.jf; + +import com.google.gson.*; +import junit.framework.TestCase; +import java.util.*; + +import static org.junit.Assert.*; +import static com.google.gson.jf.ExtraAssert.*; + +public class LenientCollectionTest extends TestCase { + private Gson lenient; + private Gson strict; + private static final String json = "{\"collection\": \"example\"}"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + lenient = new GsonBuilder().setLenient().create(); + strict = new GsonBuilder().create(); + } + + public void testLenientArray() { + assertArrayEquals(new String[] {"example"}, lenient.fromJson(json, StringArrayBox.class).collection); + } + + public void testLenientList() { + assertListEquals(List.of("example"), lenient.fromJson(json, StringListBox.class).collection); + } + + public void testStrictArray() { + try { + strict.fromJson(json, StringArrayBox.class); + fail("Strict Gson should not deserialize array without brackets"); + } catch (JsonParseException expected) { + } + } + + public void testStrictList() { + try { + strict.fromJson(json, StringListBox.class); + fail("Strict Gson should not deserialize list without brackets"); + } catch (JsonParseException expected) { + } + } +} diff --git a/gson/src/test/java/com/google/gson/jf/OmitQuotesTest.java b/gson/src/test/java/com/google/gson/jf/OmitQuotesTest.java new file mode 100644 index 00000000..bcba20df --- /dev/null +++ b/gson/src/test/java/com/google/gson/jf/OmitQuotesTest.java @@ -0,0 +1,33 @@ +package com.google.gson.jf; + +import com.google.gson.*; +import junit.framework.*; + +import java.util.*; + +import static com.google.gson.jf.ExtraAssert.*; + +public class OmitQuotesTest extends TestCase { + private Gson lenient; + private Gson strict; + private static final String lenientJson = "{collection:[\"example\"]}"; + private static final String strictJson = "{\"collection\":[\"example\"]}"; + private StringListBox slb; + + @Override + protected void setUp() throws Exception { + super.setUp(); + lenient = new GsonBuilder().setOmitQuotes().create(); + strict = new GsonBuilder().create(); + slb = new StringListBox(); + slb.collection = List.of("example"); + } + + public void testLenient() { + assertEquals(lenientJson, lenient.toJson(slb)); + } + + public void testStrict() { + assertEquals(strictJson, strict.toJson(slb)); + } +} diff --git a/gson/src/test/java/com/google/gson/regression/JsonAdapterNullSafeTest.java b/gson/src/test/java/com/google/gson/regression/JsonAdapterNullSafeTest.java index e327895c..0c63fe8d 100644 --- a/gson/src/test/java/com/google/gson/regression/JsonAdapterNullSafeTest.java +++ b/gson/src/test/java/com/google/gson/regression/JsonAdapterNullSafeTest.java @@ -32,7 +32,7 @@ public class JsonAdapterNullSafeTest extends TestCase { } public void testNullSafeBugDeserialize() throws Exception { - Device device = gson.fromJson("{'id':'ec57803e2'}", Device.class); + Device device = gson.fromJson("{\"id\":\"ec57803e2\"}", Device.class); assertEquals("ec57803e2", device.id); } diff --git a/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java b/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java index 7ec5e462..17ce4fdf 100644 --- a/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java +++ b/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java @@ -1172,7 +1172,7 @@ public final class JsonReaderTest extends TestCase { try { reader.nextNull(); fail(); - } catch (IOException expected) { + } catch (IllegalStateException expected) { } reader = new JsonReader(reader("[,]")); @@ -1189,14 +1189,14 @@ public final class JsonReaderTest extends TestCase { reader.setLenient(true); reader.beginArray(); assertEquals(true, reader.nextBoolean()); - reader.nextNull(); + //reader.nextNull(); assertEquals(true, reader.nextBoolean()); reader.endArray(); reader = new JsonReader(reader("[,true]")); reader.setLenient(true); reader.beginArray(); - reader.nextNull(); + //reader.nextNull(); assertEquals(true, reader.nextBoolean()); reader.endArray(); @@ -1204,14 +1204,14 @@ public final class JsonReaderTest extends TestCase { reader.setLenient(true); reader.beginArray(); assertEquals(true, reader.nextBoolean()); - reader.nextNull(); + //reader.nextNull(); reader.endArray(); reader = new JsonReader(reader("[,]")); reader.setLenient(true); reader.beginArray(); - reader.nextNull(); - reader.nextNull(); + //reader.nextNull(); + //reader.nextNull(); reader.endArray(); } @@ -1239,7 +1239,7 @@ public final class JsonReaderTest extends TestCase { try { reader.skipValue(); fail(); - } catch (IOException expected) { + } catch (IllegalStateException expected) { } reader = new JsonReader(reader("[,]")); @@ -1379,46 +1379,46 @@ public final class JsonReaderTest extends TestCase { } public void testFailWithPosition() throws IOException { - testFailWithPosition("Expected value at line 6 column 5 path $[1]", + testFailWithPosition("Expected value at line 6 column 5 (char '}') path $[1]", "[\n\n\n\n\n\"a\",}]"); } public void testFailWithPositionGreaterThanBufferSize() throws IOException { String spaces = repeat(' ', 8192); - testFailWithPosition("Expected value at line 6 column 5 path $[1]", + testFailWithPosition("Expected value at line 6 column 5 (char '}') path $[1]", "[\n\n" + spaces + "\n\n\n\"a\",}]"); } public void testFailWithPositionOverSlashSlashEndOfLineComment() throws IOException { - testFailWithPosition("Expected value at line 5 column 6 path $[1]", + testFailWithPosition("Expected value at line 5 column 6 (char '}') path $[1]", "\n// foo\n\n//bar\r\n[\"a\",}"); } public void testFailWithPositionOverHashEndOfLineComment() throws IOException { - testFailWithPosition("Expected value at line 5 column 6 path $[1]", + testFailWithPosition("Expected value at line 5 column 6 (char '}') path $[1]", "\n# foo\n\n#bar\r\n[\"a\",}"); } public void testFailWithPositionOverCStyleComment() throws IOException { - testFailWithPosition("Expected value at line 6 column 12 path $[1]", + testFailWithPosition("Expected value at line 6 column 12 (char '}') path $[1]", "\n\n/* foo\n*\n*\r\nbar */[\"a\",}"); } public void testFailWithPositionOverQuotedString() throws IOException { - testFailWithPosition("Expected value at line 5 column 3 path $[1]", + testFailWithPosition("Expected value at line 5 column 3 (char '}') path $[1]", "[\"foo\nbar\r\nbaz\n\",\n }"); } public void testFailWithPositionOverUnquotedString() throws IOException { - testFailWithPosition("Expected value at line 5 column 2 path $[1]", "[\n\nabcd\n\n,}"); + testFailWithPosition("Expected value at line 5 column 2 (char '}') path $[1]", "[\n\nabcd\n\n,}"); } public void testFailWithEscapedNewlineCharacter() throws IOException { - testFailWithPosition("Expected value at line 5 column 3 path $[1]", "[\n\n\"\\\n\n\",}"); + testFailWithPosition("Expected value at line 5 column 3 (char '}') path $[1]", "[\n\n\"\\\n\n\",}"); } public void testFailWithPositionIsOffsetByBom() throws IOException { - testFailWithPosition("Expected value at line 1 column 6 path $[1]", + testFailWithPosition("Expected value at line 1 column 6 (char '}') path $[1]", "\ufeff[\"a\",}]"); } @@ -1461,7 +1461,7 @@ public final class JsonReaderTest extends TestCase { reader.peek(); fail(); } catch (IOException expected) { - assertEquals("Expected value at line 1 column 14 path $[1].a[2]", expected.getMessage()); + assertEquals("Expected value at line 1 column 14 (char '}') path $[1].a[2]", expected.getMessage()); } } @@ -1728,7 +1728,7 @@ public final class JsonReaderTest extends TestCase { assertDocument("{\"name\"=>\"string\",", BEGIN_OBJECT, NAME, STRING, IOException.class); assertDocument("{\"name\"=>\"string\",\"name\"", BEGIN_OBJECT, NAME, STRING, NAME); assertDocument("[}", BEGIN_ARRAY, IOException.class); - assertDocument("[,]", BEGIN_ARRAY, NULL, NULL, END_ARRAY); + assertDocument("[,]", BEGIN_ARRAY, END_ARRAY); assertDocument("{", BEGIN_OBJECT, IOException.class); assertDocument("{\"name\"", BEGIN_OBJECT, NAME, IOException.class); assertDocument("{\"name\",", BEGIN_OBJECT, NAME, IOException.class);