aboutsummaryrefslogtreecommitdiffstats
path: root/gson/src/test/java
diff options
context:
space:
mode:
Diffstat (limited to 'gson/src/test/java')
-rw-r--r--gson/src/test/java/com/google/gson/CommentsTest.java45
-rw-r--r--gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java163
-rw-r--r--gson/src/test/java/com/google/gson/DefaultInetAddressTypeAdapterTest.java45
-rw-r--r--gson/src/test/java/com/google/gson/DefaultMapJsonSerializerTest.java64
-rw-r--r--gson/src/test/java/com/google/gson/ExposeAnnotationExclusionStrategyTest.java89
-rw-r--r--gson/src/test/java/com/google/gson/FieldAttributesTest.java82
-rw-r--r--gson/src/test/java/com/google/gson/GenericArrayTypeTest.java57
-rwxr-xr-xgson/src/test/java/com/google/gson/GsonBuilderTest.java87
-rw-r--r--gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java149
-rw-r--r--gson/src/test/java/com/google/gson/InnerClassExclusionStrategyTest.java58
-rw-r--r--gson/src/test/java/com/google/gson/JavaSerializationTest.java75
-rw-r--r--gson/src/test/java/com/google/gson/JsonArrayTest.java102
-rw-r--r--gson/src/test/java/com/google/gson/JsonNullTest.java40
-rw-r--r--gson/src/test/java/com/google/gson/JsonObjectTest.java172
-rw-r--r--gson/src/test/java/com/google/gson/JsonParserTest.java126
-rw-r--r--gson/src/test/java/com/google/gson/JsonPrimitiveTest.java247
-rw-r--r--gson/src/test/java/com/google/gson/JsonStreamParserTest.java74
-rw-r--r--gson/src/test/java/com/google/gson/LongSerializationPolicyTest.java64
-rw-r--r--gson/src/test/java/com/google/gson/MixedStreamTest.java239
-rw-r--r--gson/src/test/java/com/google/gson/MockExclusionStrategy.java41
-rw-r--r--gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java63
-rw-r--r--gson/src/test/java/com/google/gson/OverrideCoreTypeAdaptersTest.java77
-rw-r--r--gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java177
-rw-r--r--gson/src/test/java/com/google/gson/ParameterizedTypeTest.java57
-rw-r--r--gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java73
-rw-r--r--gson/src/test/java/com/google/gson/VersionExclusionStrategyTest.java55
-rw-r--r--gson/src/test/java/com/google/gson/common/MoreAsserts.java73
-rw-r--r--gson/src/test/java/com/google/gson/common/TestTypes.java418
-rw-r--r--gson/src/test/java/com/google/gson/functional/ArrayTest.java268
-rw-r--r--gson/src/test/java/com/google/gson/functional/CircularReferenceTest.java126
-rw-r--r--gson/src/test/java/com/google/gson/functional/CollectionTest.java360
-rwxr-xr-xgson/src/test/java/com/google/gson/functional/ConcurrencyTest.java140
-rw-r--r--gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java207
-rw-r--r--gson/src/test/java/com/google/gson/functional/CustomSerializerTest.java102
-rw-r--r--gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java456
-rw-r--r--gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java724
-rw-r--r--gson/src/test/java/com/google/gson/functional/DelegateTypeAdapterTest.java92
-rw-r--r--gson/src/test/java/com/google/gson/functional/EnumTest.java202
-rw-r--r--gson/src/test/java/com/google/gson/functional/EscapingTest.java92
-rw-r--r--gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java202
-rw-r--r--gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java169
-rw-r--r--gson/src/test/java/com/google/gson/functional/FieldExclusionTest.java92
-rw-r--r--gson/src/test/java/com/google/gson/functional/FieldNamingTest.java89
-rw-r--r--gson/src/test/java/com/google/gson/functional/InheritanceTest.java277
-rw-r--r--gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java127
-rw-r--r--gson/src/test/java/com/google/gson/functional/InterfaceTest.java72
-rw-r--r--gson/src/test/java/com/google/gson/functional/InternationalizationTest.java71
-rw-r--r--gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java229
-rw-r--r--gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java203
-rw-r--r--gson/src/test/java/com/google/gson/functional/JsonArrayTest.java162
-rw-r--r--gson/src/test/java/com/google/gson/functional/JsonParserTest.java143
-rw-r--r--gson/src/test/java/com/google/gson/functional/JsonTreeTest.java89
-rw-r--r--gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java141
-rwxr-xr-xgson/src/test/java/com/google/gson/functional/MapTest.java582
-rw-r--r--gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java178
-rw-r--r--gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java188
-rwxr-xr-xgson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java240
-rw-r--r--gson/src/test/java/com/google/gson/functional/ObjectTest.java501
-rw-r--r--gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java503
-rw-r--r--gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java123
-rw-r--r--gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java55
-rw-r--r--gson/src/test/java/com/google/gson/functional/PrimitiveTest.java821
-rw-r--r--gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java84
-rw-r--r--gson/src/test/java/com/google/gson/functional/RawSerializationTest.java101
-rw-r--r--gson/src/test/java/com/google/gson/functional/ReadersWritersTest.java135
-rw-r--r--gson/src/test/java/com/google/gson/functional/RuntimeTypeAdapterFactoryFunctionalTest.java205
-rw-r--r--gson/src/test/java/com/google/gson/functional/SecurityTest.java86
-rw-r--r--gson/src/test/java/com/google/gson/functional/SerializedNameTest.java54
-rw-r--r--gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java261
-rw-r--r--gson/src/test/java/com/google/gson/functional/StringTest.java140
-rw-r--r--gson/src/test/java/com/google/gson/functional/ThrowableFunctionalTest.java65
-rw-r--r--gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java176
-rw-r--r--gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java149
-rw-r--r--gson/src/test/java/com/google/gson/functional/TypeHierarchyAdapterTest.java221
-rw-r--r--gson/src/test/java/com/google/gson/functional/TypeVariableTest.java140
-rw-r--r--gson/src/test/java/com/google/gson/functional/UncategorizedTest.java134
-rw-r--r--gson/src/test/java/com/google/gson/functional/VersioningTest.java170
-rw-r--r--gson/src/test/java/com/google/gson/internal/GsonTypesTest.java74
-rw-r--r--gson/src/test/java/com/google/gson/internal/LazilyParsedNumberTest.java32
-rw-r--r--gson/src/test/java/com/google/gson/internal/LinkedHashTreeMapTest.java290
-rw-r--r--gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java150
-rw-r--r--gson/src/test/java/com/google/gson/internal/bind/JsonElementReaderTest.java312
-rw-r--r--gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java175
-rw-r--r--gson/src/test/java/com/google/gson/metrics/PerformanceTest.java346
-rw-r--r--gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java82
-rw-r--r--gson/src/test/java/com/google/gson/stream/JsonReaderPathTest.java217
-rw-r--r--gson/src/test/java/com/google/gson/stream/JsonReaderTest.java1775
-rw-r--r--gson/src/test/java/com/google/gson/stream/JsonWriterTest.java579
88 files changed, 17191 insertions, 0 deletions
diff --git a/gson/src/test/java/com/google/gson/CommentsTest.java b/gson/src/test/java/com/google/gson/CommentsTest.java
new file mode 100644
index 00000000..306e5aff
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/CommentsTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 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;
+
+import com.google.gson.reflect.TypeToken;
+import java.util.Arrays;
+import java.util.List;
+import junit.framework.TestCase;
+
+/**
+ * @author Jesse Wilson
+ */
+public final class CommentsTest extends TestCase {
+
+ /**
+ * Test for issue 212.
+ */
+ public void testParseComments() {
+ String json = "[\n"
+ + " // this is a comment\n"
+ + " \"a\",\n"
+ + " /* this is another comment */\n"
+ + " \"b\",\n"
+ + " # this is yet another comment\n"
+ + " \"c\"\n"
+ + "]";
+
+ List<String> abc = new Gson().fromJson(json, new TypeToken<List<String>>() {}.getType());
+ assertEquals(Arrays.asList("a", "b", "c"), abc);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java b/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java
new file mode 100644
index 00000000..966a8c18
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import junit.framework.TestCase;
+
+/**
+ * A simple unit test for the {@link DefaultDateTypeAdapter} class.
+ *
+ * @author Joel Leitch
+ */
+public class DefaultDateTypeAdapterTest extends TestCase {
+
+ public void testFormattingInEnUs() {
+ assertFormattingAlwaysEmitsUsLocale(Locale.US);
+ }
+
+ public void testFormattingInFr() {
+ assertFormattingAlwaysEmitsUsLocale(Locale.FRANCE);
+ }
+
+ private void assertFormattingAlwaysEmitsUsLocale(Locale locale) {
+ TimeZone defaultTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ Locale defaultLocale = Locale.getDefault();
+ Locale.setDefault(locale);
+ try {
+ assertFormatted("Jan 1, 1970 12:00:00 AM", new DefaultDateTypeAdapter());
+ assertFormatted("1/1/70", new DefaultDateTypeAdapter(DateFormat.SHORT));
+ assertFormatted("Jan 1, 1970", new DefaultDateTypeAdapter(DateFormat.MEDIUM));
+ assertFormatted("January 1, 1970", new DefaultDateTypeAdapter(DateFormat.LONG));
+ assertFormatted("1/1/70 12:00 AM",
+ new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT));
+ assertFormatted("Jan 1, 1970 12:00:00 AM",
+ new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM));
+ assertFormatted("January 1, 1970 12:00:00 AM UTC",
+ new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG));
+ assertFormatted("Thursday, January 1, 1970 12:00:00 AM UTC",
+ new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL));
+ } finally {
+ TimeZone.setDefault(defaultTimeZone);
+ Locale.setDefault(defaultLocale);
+ }
+ }
+
+ public void testParsingDatesFormattedWithSystemLocale() {
+ TimeZone defaultTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ Locale defaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.FRANCE);
+ try {
+ assertParsed("1 janv. 1970 00:00:00", new DefaultDateTypeAdapter());
+ assertParsed("01/01/70", new DefaultDateTypeAdapter(DateFormat.SHORT));
+ assertParsed("1 janv. 1970", new DefaultDateTypeAdapter(DateFormat.MEDIUM));
+ assertParsed("1 janvier 1970", new DefaultDateTypeAdapter(DateFormat.LONG));
+ assertParsed("01/01/70 00:00",
+ new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT));
+ assertParsed("1 janv. 1970 00:00:00",
+ new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM));
+ assertParsed("1 janvier 1970 00:00:00 UTC",
+ new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG));
+ assertParsed("jeudi 1 janvier 1970 00 h 00 UTC",
+ new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL));
+ } finally {
+ TimeZone.setDefault(defaultTimeZone);
+ Locale.setDefault(defaultLocale);
+ }
+ }
+
+ public void testParsingDatesFormattedWithUsLocale() {
+ TimeZone defaultTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ Locale defaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.US);
+ try {
+ assertParsed("Jan 1, 1970 0:00:00 AM", new DefaultDateTypeAdapter());
+ assertParsed("1/1/70", new DefaultDateTypeAdapter(DateFormat.SHORT));
+ assertParsed("Jan 1, 1970", new DefaultDateTypeAdapter(DateFormat.MEDIUM));
+ assertParsed("January 1, 1970", new DefaultDateTypeAdapter(DateFormat.LONG));
+ assertParsed("1/1/70 0:00 AM",
+ new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT));
+ assertParsed("Jan 1, 1970 0:00:00 AM",
+ new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM));
+ assertParsed("January 1, 1970 0:00:00 AM UTC",
+ new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG));
+ assertParsed("Thursday, January 1, 1970 0:00:00 AM UTC",
+ new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL));
+ } finally {
+ TimeZone.setDefault(defaultTimeZone);
+ Locale.setDefault(defaultLocale);
+ }
+ }
+
+ public void testFormatUsesDefaultTimezone() {
+ TimeZone defaultTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
+ Locale defaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.US);
+ try {
+ assertFormatted("Dec 31, 1969 4:00:00 PM", new DefaultDateTypeAdapter());
+ assertParsed("Dec 31, 1969 4:00:00 PM", new DefaultDateTypeAdapter());
+ } finally {
+ TimeZone.setDefault(defaultTimeZone);
+ Locale.setDefault(defaultLocale);
+ }
+ }
+
+ public void testDateSerialization() throws Exception {
+ int dateStyle = DateFormat.LONG;
+ DefaultDateTypeAdapter dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle);
+ DateFormat formatter = DateFormat.getDateInstance(dateStyle, Locale.US);
+ Date currentDate = new Date();
+
+ String dateString = dateTypeAdapter.serialize(currentDate, Date.class, null).getAsString();
+ assertEquals(formatter.format(currentDate), dateString);
+ }
+
+ public void testDatePattern() throws Exception {
+ String pattern = "yyyy-MM-dd";
+ DefaultDateTypeAdapter dateTypeAdapter = new DefaultDateTypeAdapter(pattern);
+ DateFormat formatter = new SimpleDateFormat(pattern);
+ Date currentDate = new Date();
+
+ String dateString = dateTypeAdapter.serialize(currentDate, Date.class, null).getAsString();
+ assertEquals(formatter.format(currentDate), dateString);
+ }
+
+ public void testInvalidDatePattern() throws Exception {
+ try {
+ new DefaultDateTypeAdapter("I am a bad Date pattern....");
+ fail("Invalid date pattern should fail.");
+ } catch (IllegalArgumentException expected) { }
+ }
+
+ private void assertFormatted(String formatted, DefaultDateTypeAdapter adapter) {
+ assertEquals(formatted, adapter.serialize(new Date(0), Date.class, null).getAsString());
+ }
+
+ private void assertParsed(String date, DefaultDateTypeAdapter adapter) {
+ assertEquals(date, new Date(0), adapter.deserialize(new JsonPrimitive(date), Date.class, null));
+ assertEquals("ISO 8601", new Date(0), adapter.deserialize(
+ new JsonPrimitive("1970-01-01T00:00:00Z"), Date.class, null));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/DefaultInetAddressTypeAdapterTest.java b/gson/src/test/java/com/google/gson/DefaultInetAddressTypeAdapterTest.java
new file mode 100644
index 00000000..6b853f5d
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/DefaultInetAddressTypeAdapterTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 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;
+
+import java.net.InetAddress;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for the default serializer/deserializer for the {@code InetAddress} type.
+ *
+ * @author Joel Leitch
+ */
+public class DefaultInetAddressTypeAdapterTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testInetAddressSerializationAndDeserialization() throws Exception {
+ InetAddress address = InetAddress.getByName("8.8.8.8");
+ String jsonAddress = gson.toJson(address);
+ assertEquals("\"8.8.8.8\"", jsonAddress);
+
+ InetAddress value = gson.fromJson(jsonAddress, InetAddress.class);
+ assertEquals(value, address);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/DefaultMapJsonSerializerTest.java b/gson/src/test/java/com/google/gson/DefaultMapJsonSerializerTest.java
new file mode 100644
index 00000000..5c061953
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/DefaultMapJsonSerializerTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import junit.framework.TestCase;
+
+/**
+ * Unit test for the default JSON map serialization object located in the
+ * {@link DefaultTypeAdapters} class.
+ *
+ * @author Joel Leitch
+ */
+public class DefaultMapJsonSerializerTest extends TestCase {
+ private Gson gson = new Gson();
+
+ public void testEmptyMapNoTypeSerialization() {
+ Map<String, String> emptyMap = new HashMap<String, String>();
+ JsonElement element = gson.toJsonTree(emptyMap, emptyMap.getClass());
+ assertTrue(element instanceof JsonObject);
+ JsonObject emptyMapJsonObject = (JsonObject) element;
+ assertTrue(emptyMapJsonObject.entrySet().isEmpty());
+ }
+
+ public void testEmptyMapSerialization() {
+ Type mapType = new TypeToken<Map<String, String>>() { }.getType();
+ Map<String, String> emptyMap = new HashMap<String, String>();
+ JsonElement element = gson.toJsonTree(emptyMap, mapType);
+
+ assertTrue(element instanceof JsonObject);
+ JsonObject emptyMapJsonObject = (JsonObject) element;
+ assertTrue(emptyMapJsonObject.entrySet().isEmpty());
+ }
+
+ public void testNonEmptyMapSerialization() {
+ Type mapType = new TypeToken<Map<String, String>>() { }.getType();
+ Map<String, String> myMap = new HashMap<String, String>();
+ String key = "key1";
+ myMap.put(key, "value1");
+ Gson gson = new Gson();
+ JsonElement element = gson.toJsonTree(myMap, mapType);
+
+ assertTrue(element.isJsonObject());
+ JsonObject mapJsonObject = element.getAsJsonObject();
+ assertTrue(mapJsonObject.has(key));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/ExposeAnnotationExclusionStrategyTest.java b/gson/src/test/java/com/google/gson/ExposeAnnotationExclusionStrategyTest.java
new file mode 100644
index 00000000..dd8a7a92
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/ExposeAnnotationExclusionStrategyTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011 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;
+
+import com.google.gson.annotations.Expose;
+
+import com.google.gson.internal.Excluder;
+import junit.framework.TestCase;
+
+import java.lang.reflect.Field;
+
+/**
+ * Unit tests for GsonBuilder.REQUIRE_EXPOSE_DESERIALIZE.
+ *
+ * @author Joel Leitch
+ */
+public class ExposeAnnotationExclusionStrategyTest extends TestCase {
+ private Excluder excluder = Excluder.DEFAULT.excludeFieldsWithoutExposeAnnotation();
+
+ public void testNeverSkipClasses() throws Exception {
+ assertFalse(excluder.excludeClass(MockObject.class, true));
+ assertFalse(excluder.excludeClass(MockObject.class, false));
+ }
+
+ public void testSkipNonAnnotatedFields() throws Exception {
+ Field f = createFieldAttributes("hiddenField");
+ assertTrue(excluder.excludeField(f, true));
+ assertTrue(excluder.excludeField(f, false));
+ }
+
+ public void testSkipExplicitlySkippedFields() throws Exception {
+ Field f = createFieldAttributes("explicitlyHiddenField");
+ assertTrue(excluder.excludeField(f, true));
+ assertTrue(excluder.excludeField(f, false));
+ }
+
+ public void testNeverSkipExposedAnnotatedFields() throws Exception {
+ Field f = createFieldAttributes("exposedField");
+ assertFalse(excluder.excludeField(f, true));
+ assertFalse(excluder.excludeField(f, false));
+ }
+
+ public void testNeverSkipExplicitlyExposedAnnotatedFields() throws Exception {
+ Field f = createFieldAttributes("explicitlyExposedField");
+ assertFalse(excluder.excludeField(f, true));
+ assertFalse(excluder.excludeField(f, false));
+ }
+
+ public void testDifferentSerializeAndDeserializeField() throws Exception {
+ Field f = createFieldAttributes("explicitlyDifferentModeField");
+ assertFalse(excluder.excludeField(f, true));
+ assertTrue(excluder.excludeField(f, false));
+ }
+
+ private static Field createFieldAttributes(String fieldName) throws Exception {
+ return MockObject.class.getField(fieldName);
+ }
+
+ @SuppressWarnings("unused")
+ private static class MockObject {
+ @Expose
+ public final int exposedField = 0;
+
+ @Expose(serialize=true, deserialize=true)
+ public final int explicitlyExposedField = 0;
+
+ @Expose(serialize=false, deserialize=false)
+ public final int explicitlyHiddenField = 0;
+
+ @Expose(serialize=true, deserialize=false)
+ public final int explicitlyDifferentModeField = 0;
+
+ public final int hiddenField = 0;
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/FieldAttributesTest.java b/gson/src/test/java/com/google/gson/FieldAttributesTest.java
new file mode 100644
index 00000000..8a9d9533
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/FieldAttributesTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 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;
+
+import com.google.gson.reflect.TypeToken;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Unit tests for the {@link FieldAttributes} class.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class FieldAttributesTest extends TestCase {
+ private FieldAttributes fieldAttributes;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ fieldAttributes = new FieldAttributes(Foo.class.getField("bar"));
+ }
+
+ public void testNullField() throws Exception {
+ try {
+ new FieldAttributes(null);
+ fail("Field parameter can not be null");
+ } catch (NullPointerException expected) { }
+ }
+
+ public void testDeclaringClass() throws Exception {
+ assertEquals(Foo.class, fieldAttributes.getDeclaringClass());
+ }
+
+ public void testModifiers() throws Exception {
+ assertFalse(fieldAttributes.hasModifier(Modifier.STATIC));
+ assertFalse(fieldAttributes.hasModifier(Modifier.FINAL));
+ assertFalse(fieldAttributes.hasModifier(Modifier.ABSTRACT));
+ assertFalse(fieldAttributes.hasModifier(Modifier.VOLATILE));
+ assertFalse(fieldAttributes.hasModifier(Modifier.PROTECTED));
+
+ assertTrue(fieldAttributes.hasModifier(Modifier.PUBLIC));
+ assertTrue(fieldAttributes.hasModifier(Modifier.TRANSIENT));
+ }
+
+ public void testIsSynthetic() throws Exception {
+ assertFalse(fieldAttributes.isSynthetic());
+ }
+
+ public void testName() throws Exception {
+ assertEquals("bar", fieldAttributes.getName());
+ }
+
+ public void testDeclaredTypeAndClass() throws Exception {
+ Type expectedType = new TypeToken<List<String>>() {}.getType();
+ assertEquals(expectedType, fieldAttributes.getDeclaredType());
+ assertEquals(List.class, fieldAttributes.getDeclaredClass());
+ }
+
+ private static class Foo {
+ @SuppressWarnings("unused")
+ public transient List<String> bar;
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/GenericArrayTypeTest.java b/gson/src/test/java/com/google/gson/GenericArrayTypeTest.java
new file mode 100644
index 00000000..42acb8a2
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/GenericArrayTypeTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import com.google.gson.internal.$Gson$Types;
+import com.google.gson.reflect.TypeToken;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Unit tests for the {@code GenericArrayType}s created by the {@link $Gson$Types} class.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class GenericArrayTypeTest extends TestCase {
+ private GenericArrayType ourType;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ ourType = $Gson$Types.arrayOf($Gson$Types.newParameterizedTypeWithOwner(null, List.class, String.class));
+ }
+
+ public void testOurTypeFunctionality() throws Exception {
+ Type parameterizedType = new TypeToken<List<String>>() {}.getType();
+ Type genericArrayType = new TypeToken<List<String>[]>() {}.getType();
+
+ assertEquals(parameterizedType, ourType.getGenericComponentType());
+ assertEquals(genericArrayType, ourType);
+ assertEquals(genericArrayType.hashCode(), ourType.hashCode());
+ }
+
+ public void testNotEquals() throws Exception {
+ Type differentGenericArrayType = new TypeToken<List<String>[][]>() {}.getType();
+ assertFalse(differentGenericArrayType.equals(ourType));
+ assertFalse(ourType.equals(differentGenericArrayType));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/GsonBuilderTest.java b/gson/src/test/java/com/google/gson/GsonBuilderTest.java
new file mode 100755
index 00000000..73601c0e
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/GsonBuilderTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+
+import junit.framework.TestCase;
+
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Unit tests for {@link GsonBuilder}.
+ *
+ * @author Inderjeet Singh
+ */
+public class GsonBuilderTest extends TestCase {
+ private static final TypeAdapter<Object> NULL_TYPE_ADAPTER = new TypeAdapter<Object>() {
+ @Override public void write(JsonWriter out, Object value) {
+ throw new AssertionError();
+ }
+ @Override public Object read(JsonReader in) {
+ throw new AssertionError();
+ }
+ };
+
+ public void testCreatingMoreThanOnce() {
+ GsonBuilder builder = new GsonBuilder();
+ builder.create();
+ builder.create();
+ }
+
+ public void testExcludeFieldsWithModifiers() {
+ Gson gson = new GsonBuilder()
+ .excludeFieldsWithModifiers(Modifier.VOLATILE, Modifier.PRIVATE)
+ .create();
+ assertEquals("{\"d\":\"d\"}", gson.toJson(new HasModifiers()));
+ }
+
+ public void testRegisterTypeAdapterForCoreType() {
+ Type[] types = {
+ byte.class,
+ int.class,
+ double.class,
+ Short.class,
+ Long.class,
+ String.class,
+ };
+ for (Type type : types) {
+ new GsonBuilder().registerTypeAdapter(type, NULL_TYPE_ADAPTER);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ static class HasModifiers {
+ private String a = "a";
+ volatile String b = "b";
+ private volatile String c = "c";
+ String d = "d";
+ }
+
+ public void testTransientFieldExclusion() {
+ Gson gson = new GsonBuilder()
+ .excludeFieldsWithModifiers()
+ .create();
+ assertEquals("{\"a\":\"a\"}", gson.toJson(new HasTransients()));
+ }
+
+ static class HasTransients {
+ transient String a = "a";
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java b/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java
new file mode 100644
index 00000000..922cecc4
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+import java.lang.reflect.Type;
+import java.math.BigInteger;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import junit.framework.TestCase;
+
+/**
+ * Contains numerous tests involving registered type converters with a Gson instance.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class GsonTypeAdapterTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new GsonBuilder()
+ .registerTypeAdapter(AtomicLong.class, new ExceptionTypeAdapter())
+ .registerTypeAdapter(AtomicInteger.class, new AtomicIntegerTypeAdapter())
+ .create();
+ }
+
+ public void testDefaultTypeAdapterThrowsParseException() throws Exception {
+ try {
+ gson.fromJson("{\"abc\":123}", BigInteger.class);
+ fail("Should have thrown a JsonParseException");
+ } catch (JsonParseException expected) { }
+ }
+
+ public void testTypeAdapterThrowsException() throws Exception {
+ try {
+ gson.toJson(new AtomicLong(0));
+ fail("Type Adapter should have thrown an exception");
+ } catch (IllegalStateException expected) { }
+
+ try {
+ gson.fromJson("123", AtomicLong.class);
+ fail("Type Adapter should have thrown an exception");
+ } catch (JsonParseException expected) { }
+ }
+
+ public void testTypeAdapterProperlyConvertsTypes() throws Exception {
+ int intialValue = 1;
+ AtomicInteger atomicInt = new AtomicInteger(intialValue);
+ String json = gson.toJson(atomicInt);
+ assertEquals(intialValue + 1, Integer.parseInt(json));
+
+ atomicInt = gson.fromJson(json, AtomicInteger.class);
+ assertEquals(intialValue, atomicInt.get());
+ }
+
+ public void testTypeAdapterDoesNotAffectNonAdaptedTypes() throws Exception {
+ String expected = "blah";
+ String actual = gson.toJson(expected);
+ assertEquals("\"" + expected + "\"", actual);
+
+ actual = gson.fromJson(actual, String.class);
+ assertEquals(expected, actual);
+ }
+
+ private static class ExceptionTypeAdapter
+ implements JsonSerializer<AtomicLong>, JsonDeserializer<AtomicLong> {
+ public JsonElement serialize(
+ AtomicLong src, Type typeOfSrc, JsonSerializationContext context) {
+ throw new IllegalStateException();
+ }
+
+ public AtomicLong deserialize(
+ JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ throw new IllegalStateException();
+ }
+ }
+
+ private static class AtomicIntegerTypeAdapter
+ implements JsonSerializer<AtomicInteger>, JsonDeserializer<AtomicInteger> {
+ public JsonElement serialize(AtomicInteger src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive(src.incrementAndGet());
+ }
+
+ public AtomicInteger deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ int intValue = json.getAsInt();
+ return new AtomicInteger(--intValue);
+ }
+ }
+
+ static abstract class Abstract {
+ String a;
+ }
+
+ static class Concrete extends Abstract {
+ String b;
+ }
+
+ // https://groups.google.com/d/topic/google-gson/EBmOCa8kJPE/discussion
+ public void testDeserializerForAbstractClass() {
+ Concrete instance = new Concrete();
+ instance.a = "android";
+ instance.b = "beep";
+ assertSerialized("{\"a\":\"android\"}", Abstract.class, true, true, instance);
+ assertSerialized("{\"a\":\"android\"}", Abstract.class, true, false, instance);
+ assertSerialized("{\"a\":\"android\"}", Abstract.class, false, true, instance);
+ assertSerialized("{\"a\":\"android\"}", Abstract.class, false, false, instance);
+ assertSerialized("{\"b\":\"beep\",\"a\":\"android\"}", Concrete.class, true, true, instance);
+ assertSerialized("{\"b\":\"beep\",\"a\":\"android\"}", Concrete.class, true, false, instance);
+ assertSerialized("{\"b\":\"beep\",\"a\":\"android\"}", Concrete.class, false, true, instance);
+ assertSerialized("{\"b\":\"beep\",\"a\":\"android\"}", Concrete.class, false, false, instance);
+ }
+
+ private void assertSerialized(String expected, Class<?> instanceType, boolean registerAbstractDeserializer,
+ boolean registerAbstractHierarchyDeserializer, Object instance) {
+ JsonDeserializer<Abstract> deserializer = new JsonDeserializer<Abstract>() {
+ public Abstract deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context) throws JsonParseException {
+ throw new AssertionError();
+ }
+ };
+ GsonBuilder builder = new GsonBuilder();
+ if (registerAbstractDeserializer) {
+ builder.registerTypeAdapter(Abstract.class, deserializer);
+ }
+ if (registerAbstractHierarchyDeserializer) {
+ builder.registerTypeHierarchyAdapter(Abstract.class, deserializer);
+ }
+ Gson gson = builder.create();
+ assertEquals(expected, gson.toJson(instance, instanceType));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/InnerClassExclusionStrategyTest.java b/gson/src/test/java/com/google/gson/InnerClassExclusionStrategyTest.java
new file mode 100644
index 00000000..86f7a622
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/InnerClassExclusionStrategyTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+import com.google.gson.internal.Excluder;
+import java.lang.reflect.Field;
+import junit.framework.TestCase;
+
+/**
+ * Unit test for GsonBuilder.EXCLUDE_INNER_CLASSES.
+ *
+ * @author Joel Leitch
+ */
+public class InnerClassExclusionStrategyTest extends TestCase {
+ public InnerClass innerClass = new InnerClass();
+ public StaticNestedClass staticNestedClass = new StaticNestedClass();
+ private Excluder excluder = Excluder.DEFAULT.disableInnerClassSerialization();
+
+ public void testExcludeInnerClassObject() throws Exception {
+ Class<?> clazz = innerClass.getClass();
+ assertTrue(excluder.excludeClass(clazz, true));
+ }
+
+ public void testExcludeInnerClassField() throws Exception {
+ Field f = getClass().getField("innerClass");
+ assertTrue(excluder.excludeField(f, true));
+ }
+
+ public void testIncludeStaticNestedClassObject() throws Exception {
+ Class<?> clazz = staticNestedClass.getClass();
+ assertFalse(excluder.excludeClass(clazz, true));
+ }
+
+ public void testIncludeStaticNestedClassField() throws Exception {
+ Field f = getClass().getField("staticNestedClass");
+ assertFalse(excluder.excludeField(f, true));
+ }
+
+ class InnerClass {
+ }
+
+ static class StaticNestedClass {
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/JavaSerializationTest.java b/gson/src/test/java/com/google/gson/JavaSerializationTest.java
new file mode 100644
index 00000000..fbaea19d
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/JavaSerializationTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 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;
+
+import com.google.gson.reflect.TypeToken;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import junit.framework.TestCase;
+
+/**
+ * Check that Gson doesn't return non-serializable data types.
+ *
+ * @author Jesse Wilson
+ */
+public final class JavaSerializationTest extends TestCase {
+ private final Gson gson = new Gson();
+
+ public void testMapIsSerializable() throws Exception {
+ Type type = new TypeToken<Map<String, Integer>>() {}.getType();
+ Map<String, Integer> map = gson.fromJson("{\"b\":1,\"c\":2,\"a\":3}", type);
+ Map<String, Integer> serialized = serializedCopy(map);
+ assertEquals(map, serialized);
+ // Also check that the iteration order is retained.
+ assertEquals(Arrays.asList("b", "c", "a"), new ArrayList<String>(serialized.keySet()));
+ }
+
+ public void testListIsSerializable() throws Exception {
+ Type type = new TypeToken<List<String>>() {}.getType();
+ List<String> list = gson.fromJson("[\"a\",\"b\",\"c\"]", type);
+ List<String> serialized = serializedCopy(list);
+ assertEquals(list, serialized);
+ }
+
+ public void testNumberIsSerializable() throws Exception {
+ Type type = new TypeToken<List<Number>>() {}.getType();
+ List<Number> list = gson.fromJson("[1,3.14,6.673e-11]", type);
+ List<Number> serialized = serializedCopy(list);
+ assertEquals(1.0, serialized.get(0).doubleValue());
+ assertEquals(3.14, serialized.get(1).doubleValue());
+ assertEquals(6.673e-11, serialized.get(2).doubleValue());
+ }
+
+ @SuppressWarnings("unchecked") // Serialization promises to return the same type.
+ private <T> T serializedCopy(T object) throws IOException, ClassNotFoundException {
+ ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(bytesOut);
+ out.writeObject(object);
+ out.close();
+ ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesOut.toByteArray());
+ ObjectInputStream in = new ObjectInputStream(bytesIn);
+ return (T) in.readObject();
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/JsonArrayTest.java b/gson/src/test/java/com/google/gson/JsonArrayTest.java
new file mode 100644
index 00000000..b77d6f1b
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/JsonArrayTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2011 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;
+
+import junit.framework.TestCase;
+
+import com.google.gson.common.MoreAsserts;
+
+/**
+ * @author Jesse Wilson
+ */
+public final class JsonArrayTest extends TestCase {
+
+ public void testEqualsOnEmptyArray() {
+ MoreAsserts.assertEqualsAndHashCode(new JsonArray(), new JsonArray());
+ }
+
+ public void testEqualsNonEmptyArray() {
+ JsonArray a = new JsonArray();
+ JsonArray b = new JsonArray();
+
+ assertEquals(a, a);
+
+ a.add(new JsonObject());
+ assertFalse(a.equals(b));
+ assertFalse(b.equals(a));
+
+ b.add(new JsonObject());
+ MoreAsserts.assertEqualsAndHashCode(a, b);
+
+ a.add(new JsonObject());
+ assertFalse(a.equals(b));
+ assertFalse(b.equals(a));
+
+ b.add(JsonNull.INSTANCE);
+ assertFalse(a.equals(b));
+ assertFalse(b.equals(a));
+ }
+
+ public void testRemove() {
+ JsonArray array = new JsonArray();
+ try {
+ array.remove(0);
+ fail();
+ } catch (IndexOutOfBoundsException expected) {}
+ JsonPrimitive a = new JsonPrimitive("a");
+ array.add(a);
+ assertTrue(array.remove(a));
+ assertFalse(array.contains(a));
+ array.add(a);
+ array.add(new JsonPrimitive("b"));
+ assertEquals("b", array.remove(1).getAsString());
+ assertEquals(1, array.size());
+ assertTrue(array.contains(a));
+ }
+
+ public void testSet() {
+ JsonArray array = new JsonArray();
+ try {
+ array.set(0, new JsonPrimitive(1));
+ fail();
+ } catch (IndexOutOfBoundsException expected) {}
+ JsonPrimitive a = new JsonPrimitive("a");
+ array.add(a);
+ array.set(0, new JsonPrimitive("b"));
+ assertEquals("b", array.get(0).getAsString());
+ array.set(0, null);
+ assertNull(array.get(0));
+ array.set(0, new JsonPrimitive("c"));
+ assertEquals("c", array.get(0).getAsString());
+ assertEquals(1, array.size());
+ }
+
+ public void testDeepCopy() {
+ JsonArray original = new JsonArray();
+ JsonArray firstEntry = new JsonArray();
+ original.add(firstEntry);
+
+ JsonArray copy = original.deepCopy();
+ original.add(new JsonPrimitive("y"));
+
+ assertEquals(1, copy.size());
+ firstEntry.add(new JsonPrimitive("z"));
+
+ assertEquals(1, original.get(0).getAsJsonArray().size());
+ assertEquals(0, copy.get(0).getAsJsonArray().size());
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/JsonNullTest.java b/gson/src/test/java/com/google/gson/JsonNullTest.java
new file mode 100644
index 00000000..6157e387
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/JsonNullTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 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;
+
+import com.google.gson.common.MoreAsserts;
+import junit.framework.TestCase;
+
+/**
+ * @author Jesse Wilson
+ */
+public final class JsonNullTest extends TestCase {
+
+ @SuppressWarnings("deprecation")
+ public void testEqualsAndHashcode() {
+ MoreAsserts.assertEqualsAndHashCode(new JsonNull(), new JsonNull());
+ MoreAsserts.assertEqualsAndHashCode(new JsonNull(), JsonNull.INSTANCE);
+ MoreAsserts.assertEqualsAndHashCode(JsonNull.INSTANCE, JsonNull.INSTANCE);
+ }
+
+ public void testDeepCopy() {
+ @SuppressWarnings("deprecation")
+ JsonNull a = new JsonNull();
+ assertSame(JsonNull.INSTANCE, a.deepCopy());
+ assertSame(JsonNull.INSTANCE, JsonNull.INSTANCE.deepCopy());
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/JsonObjectTest.java b/gson/src/test/java/com/google/gson/JsonObjectTest.java
new file mode 100644
index 00000000..9423a24d
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/JsonObjectTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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;
+
+import com.google.gson.common.MoreAsserts;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for the {@link JsonObject} class.
+ *
+ * @author Joel Leitch
+ */
+public class JsonObjectTest extends TestCase {
+
+ public void testAddingAndRemovingObjectProperties() throws Exception {
+ JsonObject jsonObj = new JsonObject();
+ String propertyName = "property";
+ assertFalse(jsonObj.has(propertyName));
+ assertNull(jsonObj.get(propertyName));
+
+ JsonPrimitive value = new JsonPrimitive("blah");
+ jsonObj.add(propertyName, value);
+ assertEquals(value, jsonObj.get(propertyName));
+
+ JsonElement removedElement = jsonObj.remove(propertyName);
+ assertEquals(value, removedElement);
+ assertFalse(jsonObj.has(propertyName));
+ assertNull(jsonObj.get(propertyName));
+ }
+
+ public void testAddingNullPropertyValue() throws Exception {
+ String propertyName = "property";
+ JsonObject jsonObj = new JsonObject();
+ jsonObj.add(propertyName, null);
+
+ assertTrue(jsonObj.has(propertyName));
+
+ JsonElement jsonElement = jsonObj.get(propertyName);
+ assertNotNull(jsonElement);
+ assertTrue(jsonElement.isJsonNull());
+ }
+
+ public void testAddingNullOrEmptyPropertyName() throws Exception {
+ JsonObject jsonObj = new JsonObject();
+ try {
+ jsonObj.add(null, JsonNull.INSTANCE);
+ fail("Should not allow null property names.");
+ } catch (NullPointerException expected) { }
+
+ jsonObj.add("", JsonNull.INSTANCE);
+ jsonObj.add(" \t", JsonNull.INSTANCE);
+ }
+
+ public void testAddingBooleanProperties() throws Exception {
+ String propertyName = "property";
+ JsonObject jsonObj = new JsonObject();
+ jsonObj.addProperty(propertyName, true);
+
+ assertTrue(jsonObj.has(propertyName));
+
+ JsonElement jsonElement = jsonObj.get(propertyName);
+ assertNotNull(jsonElement);
+ assertTrue(jsonElement.getAsBoolean());
+ }
+
+ public void testAddingStringProperties() throws Exception {
+ String propertyName = "property";
+ String value = "blah";
+
+ JsonObject jsonObj = new JsonObject();
+ jsonObj.addProperty(propertyName, value);
+
+ assertTrue(jsonObj.has(propertyName));
+
+ JsonElement jsonElement = jsonObj.get(propertyName);
+ assertNotNull(jsonElement);
+ assertEquals(value, jsonElement.getAsString());
+ }
+
+ public void testAddingCharacterProperties() throws Exception {
+ String propertyName = "property";
+ char value = 'a';
+
+ JsonObject jsonObj = new JsonObject();
+ jsonObj.addProperty(propertyName, value);
+
+ assertTrue(jsonObj.has(propertyName));
+
+ JsonElement jsonElement = jsonObj.get(propertyName);
+ assertNotNull(jsonElement);
+ assertEquals(String.valueOf(value), jsonElement.getAsString());
+ assertEquals(value, jsonElement.getAsCharacter());
+ }
+
+ /**
+ * From bug report http://code.google.com/p/google-gson/issues/detail?id=182
+ */
+ public void testPropertyWithQuotes() {
+ JsonObject jsonObj = new JsonObject();
+ jsonObj.add("a\"b", new JsonPrimitive("c\"d"));
+ String json = new Gson().toJson(jsonObj);
+ assertEquals("{\"a\\\"b\":\"c\\\"d\"}", json);
+ }
+
+ /**
+ * From issue 227.
+ */
+ public void testWritePropertyWithEmptyStringName() {
+ JsonObject jsonObj = new JsonObject();
+ jsonObj.add("", new JsonPrimitive(true));
+ assertEquals("{\"\":true}", new Gson().toJson(jsonObj));
+
+ }
+
+ public void testReadPropertyWithEmptyStringName() {
+ JsonObject jsonObj = new JsonParser().parse("{\"\":true}").getAsJsonObject();
+ assertEquals(true, jsonObj.get("").getAsBoolean());
+ }
+
+ public void testEqualsOnEmptyObject() {
+ MoreAsserts.assertEqualsAndHashCode(new JsonObject(), new JsonObject());
+ }
+
+ public void testEqualsNonEmptyObject() {
+ JsonObject a = new JsonObject();
+ JsonObject b = new JsonObject();
+
+ assertEquals(a, a);
+
+ a.add("foo", new JsonObject());
+ assertFalse(a.equals(b));
+ assertFalse(b.equals(a));
+
+ b.add("foo", new JsonObject());
+ MoreAsserts.assertEqualsAndHashCode(a, b);
+
+ a.add("bar", new JsonObject());
+ assertFalse(a.equals(b));
+ assertFalse(b.equals(a));
+
+ b.add("bar", JsonNull.INSTANCE);
+ assertFalse(a.equals(b));
+ assertFalse(b.equals(a));
+ }
+
+ public void testDeepCopy() {
+ JsonObject original = new JsonObject();
+ JsonArray firstEntry = new JsonArray();
+ original.add("key", firstEntry);
+
+ JsonObject copy = original.deepCopy();
+ firstEntry.add(new JsonPrimitive("z"));
+
+ assertEquals(1, original.get("key").getAsJsonArray().size());
+ assertEquals(0, copy.get("key").getAsJsonArray().size());
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/JsonParserTest.java b/gson/src/test/java/com/google/gson/JsonParserTest.java
new file mode 100644
index 00000000..7efa7fd2
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/JsonParserTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2009 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;
+
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+import java.io.StringReader;
+
+import junit.framework.TestCase;
+
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.internal.Streams;
+import com.google.gson.stream.JsonReader;
+
+/**
+ * Unit test for {@link JsonParser}
+ *
+ * @author Inderjeet Singh
+ */
+public class JsonParserTest extends TestCase {
+ private JsonParser parser;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ parser = new JsonParser();
+ }
+
+ public void testParseInvalidJson() {
+ try {
+ parser.parse("[[]");
+ fail();
+ } catch (JsonSyntaxException expected) { }
+ }
+
+ public void testParseUnquotedStringArrayFails() {
+ JsonElement element = parser.parse("[a,b,c]");
+ assertEquals("a", element.getAsJsonArray().get(0).getAsString());
+ assertEquals("b", element.getAsJsonArray().get(1).getAsString());
+ assertEquals("c", element.getAsJsonArray().get(2).getAsString());
+ assertEquals(3, element.getAsJsonArray().size());
+ }
+
+ public void testParseString() {
+ String json = "{a:10,b:'c'}";
+ JsonElement e = parser.parse(json);
+ assertTrue(e.isJsonObject());
+ assertEquals(10, e.getAsJsonObject().get("a").getAsInt());
+ assertEquals("c", e.getAsJsonObject().get("b").getAsString());
+ }
+
+ public void testParseEmptyString() {
+ JsonElement e = parser.parse("\" \"");
+ assertTrue(e.isJsonPrimitive());
+ assertEquals(" ", e.getAsString());
+ }
+
+ public void testParseEmptyWhitespaceInput() {
+ JsonElement e = parser.parse(" ");
+ assertTrue(e.isJsonNull());
+ }
+
+ public void testParseUnquotedSingleWordStringFails() {
+ assertEquals("Test", parser.parse("Test").getAsString());
+ }
+
+ public void testParseUnquotedMultiWordStringFails() {
+ String unquotedSentence = "Test is a test..blah blah";
+ try {
+ parser.parse(unquotedSentence);
+ fail();
+ } catch (JsonSyntaxException expected) { }
+ }
+
+ public void testParseMixedArray() {
+ String json = "[{},13,\"stringValue\"]";
+ JsonElement e = parser.parse(json);
+ assertTrue(e.isJsonArray());
+
+ JsonArray array = e.getAsJsonArray();
+ assertEquals("{}", array.get(0).toString());
+ assertEquals(13, array.get(1).getAsInt());
+ assertEquals("stringValue", array.get(2).getAsString());
+ }
+
+ public void testParseReader() {
+ StringReader reader = new StringReader("{a:10,b:'c'}");
+ JsonElement e = parser.parse(reader);
+ assertTrue(e.isJsonObject());
+ assertEquals(10, e.getAsJsonObject().get("a").getAsInt());
+ assertEquals("c", e.getAsJsonObject().get("b").getAsString());
+ }
+
+ public void testReadWriteTwoObjects() throws Exception {
+ Gson gson = new Gson();
+ CharArrayWriter writer = new CharArrayWriter();
+ BagOfPrimitives expectedOne = new BagOfPrimitives(1, 1, true, "one");
+ writer.write(gson.toJson(expectedOne).toCharArray());
+ BagOfPrimitives expectedTwo = new BagOfPrimitives(2, 2, false, "two");
+ writer.write(gson.toJson(expectedTwo).toCharArray());
+ CharArrayReader reader = new CharArrayReader(writer.toCharArray());
+
+ JsonReader parser = new JsonReader(reader);
+ parser.setLenient(true);
+ JsonElement element1 = Streams.parse(parser);
+ JsonElement element2 = Streams.parse(parser);
+ BagOfPrimitives actualOne = gson.fromJson(element1, BagOfPrimitives.class);
+ assertEquals("one", actualOne.stringValue);
+ BagOfPrimitives actualTwo = gson.fromJson(element2, BagOfPrimitives.class);
+ assertEquals("two", actualTwo.stringValue);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java b/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java
new file mode 100644
index 00000000..fa3611c9
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java
@@ -0,0 +1,247 @@
+/*
+ * 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;
+
+import com.google.gson.common.MoreAsserts;
+
+import junit.framework.TestCase;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Unit test for the {@link JsonPrimitive} class.
+ *
+ * @author Joel Leitch
+ */
+public class JsonPrimitiveTest extends TestCase {
+
+ public void testBoolean() throws Exception {
+ JsonPrimitive json = new JsonPrimitive(Boolean.TRUE);
+
+ assertTrue(json.isBoolean());
+ assertTrue(json.getAsBoolean());
+
+ // Extra support for booleans
+ json = new JsonPrimitive(1);
+ assertFalse(json.getAsBoolean());
+
+ json = new JsonPrimitive("1");
+ assertFalse(json.getAsBoolean());
+
+ json = new JsonPrimitive("true");
+ assertTrue(json.getAsBoolean());
+
+ json = new JsonPrimitive("TrUe");
+ assertTrue(json.getAsBoolean());
+
+ json = new JsonPrimitive("1.3");
+ assertFalse(json.getAsBoolean());
+ }
+
+ public void testParsingStringAsBoolean() throws Exception {
+ JsonPrimitive json = new JsonPrimitive("true");
+
+ assertFalse(json.isBoolean());
+ assertTrue(json.getAsBoolean());
+ }
+
+ public void testParsingStringAsNumber() throws Exception {
+ JsonPrimitive json = new JsonPrimitive("1");
+
+ assertFalse(json.isNumber());
+ assertEquals(1D, json.getAsDouble(), 0.00001);
+ assertEquals(1F, json.getAsFloat(), 0.00001);
+ assertEquals(1, json.getAsInt());
+ assertEquals(1L, json.getAsLong());
+ assertEquals((short) 1, json.getAsShort());
+ assertEquals((byte) 1, json.getAsByte());
+ assertEquals(new BigInteger("1"), json.getAsBigInteger());
+ assertEquals(new BigDecimal("1"), json.getAsBigDecimal());
+ }
+
+ public void testStringsAndChar() throws Exception {
+ JsonPrimitive json = new JsonPrimitive("abc");
+ assertTrue(json.isString());
+ assertEquals('a', json.getAsCharacter());
+ assertEquals("abc", json.getAsString());
+
+ json = new JsonPrimitive('z');
+ assertTrue(json.isString());
+ assertEquals('z', json.getAsCharacter());
+ assertEquals("z", json.getAsString());
+ }
+
+ public void testExponential() throws Exception {
+ JsonPrimitive json = new JsonPrimitive("1E+7");
+
+ assertEquals(new BigDecimal("1E+7"), json.getAsBigDecimal());
+ assertEquals(new Double("1E+7"), json.getAsDouble(), 0.00001);
+ assertEquals(new Float("1E+7"), json.getAsDouble(), 0.00001);
+
+ try {
+ json.getAsInt();
+ fail("Integers can not handle exponents like this.");
+ } catch (NumberFormatException expected) { }
+ }
+
+ public void testByteEqualsShort() {
+ JsonPrimitive p1 = new JsonPrimitive(new Byte((byte)10));
+ JsonPrimitive p2 = new JsonPrimitive(new Short((short)10));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testByteEqualsInteger() {
+ JsonPrimitive p1 = new JsonPrimitive(new Byte((byte)10));
+ JsonPrimitive p2 = new JsonPrimitive(new Integer(10));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testByteEqualsLong() {
+ JsonPrimitive p1 = new JsonPrimitive(new Byte((byte)10));
+ JsonPrimitive p2 = new JsonPrimitive(new Long(10L));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testByteEqualsBigInteger() {
+ JsonPrimitive p1 = new JsonPrimitive(new Byte((byte)10));
+ JsonPrimitive p2 = new JsonPrimitive(new BigInteger("10"));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testShortEqualsInteger() {
+ JsonPrimitive p1 = new JsonPrimitive(new Short((short)10));
+ JsonPrimitive p2 = new JsonPrimitive(new Integer(10));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testShortEqualsLong() {
+ JsonPrimitive p1 = new JsonPrimitive(new Short((short)10));
+ JsonPrimitive p2 = new JsonPrimitive(new Long(10));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testShortEqualsBigInteger() {
+ JsonPrimitive p1 = new JsonPrimitive(new Short((short)10));
+ JsonPrimitive p2 = new JsonPrimitive(new BigInteger("10"));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testIntegerEqualsLong() {
+ JsonPrimitive p1 = new JsonPrimitive(new Integer(10));
+ JsonPrimitive p2 = new JsonPrimitive(new Long(10L));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testIntegerEqualsBigInteger() {
+ JsonPrimitive p1 = new JsonPrimitive(new Integer(10));
+ JsonPrimitive p2 = new JsonPrimitive(new BigInteger("10"));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testLongEqualsBigInteger() {
+ JsonPrimitive p1 = new JsonPrimitive(new Long(10L));
+ JsonPrimitive p2 = new JsonPrimitive(new BigInteger("10"));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testFloatEqualsDouble() {
+ JsonPrimitive p1 = new JsonPrimitive(new Float(10.25F));
+ JsonPrimitive p2 = new JsonPrimitive(new Double(10.25D));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testFloatEqualsBigDecimal() {
+ JsonPrimitive p1 = new JsonPrimitive(new Float(10.25F));
+ JsonPrimitive p2 = new JsonPrimitive(new BigDecimal("10.25"));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testDoubleEqualsBigDecimal() {
+ JsonPrimitive p1 = new JsonPrimitive(new Double(10.25D));
+ JsonPrimitive p2 = new JsonPrimitive(new BigDecimal("10.25"));
+ assertEquals(p1, p2);
+ assertEquals(p1.hashCode(), p2.hashCode());
+ }
+
+ public void testValidJsonOnToString() throws Exception {
+ JsonPrimitive json = new JsonPrimitive("Some\nEscaped\nValue");
+ assertEquals("\"Some\\nEscaped\\nValue\"", json.toString());
+
+ json = new JsonPrimitive(new BigDecimal("1.333"));
+ assertEquals("1.333", json.toString());
+ }
+
+ public void testEquals() {
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive("A"), new JsonPrimitive("A"));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(true), new JsonPrimitive(true));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(5L), new JsonPrimitive(5L));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive('a'), new JsonPrimitive('a'));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(Float.NaN), new JsonPrimitive(Float.NaN));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(Float.NEGATIVE_INFINITY),
+ new JsonPrimitive(Float.NEGATIVE_INFINITY));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(Float.POSITIVE_INFINITY),
+ new JsonPrimitive(Float.POSITIVE_INFINITY));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(Double.NaN), new JsonPrimitive(Double.NaN));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(Double.NEGATIVE_INFINITY),
+ new JsonPrimitive(Double.NEGATIVE_INFINITY));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(Double.POSITIVE_INFINITY),
+ new JsonPrimitive(Double.POSITIVE_INFINITY));
+ assertFalse(new JsonPrimitive("a").equals(new JsonPrimitive("b")));
+ assertFalse(new JsonPrimitive(true).equals(new JsonPrimitive(false)));
+ assertFalse(new JsonPrimitive(0).equals(new JsonPrimitive(1)));
+ }
+
+ public void testEqualsAcrossTypes() {
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive("a"), new JsonPrimitive('a'));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(new BigInteger("0")), new JsonPrimitive(0));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(0), new JsonPrimitive(0L));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(new BigInteger("0")), new JsonPrimitive(0));
+ MoreAsserts.assertEqualsAndHashCode(new JsonPrimitive(Float.NaN), new JsonPrimitive(Double.NaN));
+ }
+
+ public void testEqualsIntegerAndBigInteger() {
+ JsonPrimitive a = new JsonPrimitive(5L);
+ JsonPrimitive b = new JsonPrimitive(new BigInteger("18446744073709551621")); // 2^64 + 5
+ // Ideally, the following assertion should have failed but the price is too much to pay
+ // assertFalse(a + " equals " + b, a.equals(b));
+ assertTrue(a + " equals " + b, a.equals(b));
+ }
+
+ public void testEqualsDoesNotEquateStringAndNonStringTypes() {
+ assertFalse(new JsonPrimitive("true").equals(new JsonPrimitive(true)));
+ assertFalse(new JsonPrimitive("0").equals(new JsonPrimitive(0)));
+ assertFalse(new JsonPrimitive("NaN").equals(new JsonPrimitive(Float.NaN)));
+ }
+
+ public void testDeepCopy() {
+ JsonPrimitive a = new JsonPrimitive("a");
+ assertSame(a, a.deepCopy()); // Primitives are immutable!
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/JsonStreamParserTest.java b/gson/src/test/java/com/google/gson/JsonStreamParserTest.java
new file mode 100644
index 00000000..1b40b58b
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/JsonStreamParserTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009 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;
+
+import junit.framework.TestCase;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Unit tests for {@link JsonStreamParser}
+ *
+ * @author Inderjeet Singh
+ */
+public class JsonStreamParserTest extends TestCase {
+ private JsonStreamParser parser;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ parser = new JsonStreamParser("'one' 'two'");
+ }
+
+ public void testParseTwoStrings() {
+ String actualOne = parser.next().getAsString();
+ assertEquals("one", actualOne);
+ String actualTwo = parser.next().getAsString();
+ assertEquals("two", actualTwo);
+ }
+
+ public void testIterator() {
+ assertTrue(parser.hasNext());
+ assertEquals("one", parser.next().getAsString());
+ assertTrue(parser.hasNext());
+ assertEquals("two", parser.next().getAsString());
+ assertFalse(parser.hasNext());
+ }
+
+ public void testNoSideEffectForHasNext() throws Exception {
+ assertTrue(parser.hasNext());
+ assertTrue(parser.hasNext());
+ assertTrue(parser.hasNext());
+ assertEquals("one", parser.next().getAsString());
+
+ assertTrue(parser.hasNext());
+ assertTrue(parser.hasNext());
+ assertEquals("two", parser.next().getAsString());
+
+ assertFalse(parser.hasNext());
+ assertFalse(parser.hasNext());
+ }
+
+ public void testCallingNextBeyondAvailableInput() {
+ parser.next();
+ parser.next();
+ try {
+ parser.next();
+ fail("Parser should not go beyond available input");
+ } catch (NoSuchElementException expected) {
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/LongSerializationPolicyTest.java b/gson/src/test/java/com/google/gson/LongSerializationPolicyTest.java
new file mode 100644
index 00000000..d0a06320
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/LongSerializationPolicyTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 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;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for the {@link LongSerializationPolicy} class.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class LongSerializationPolicyTest extends TestCase {
+
+ public void testDefaultLongSerialization() throws Exception {
+ JsonElement element = LongSerializationPolicy.DEFAULT.serialize(1556L);
+ assertTrue(element.isJsonPrimitive());
+
+ JsonPrimitive jsonPrimitive = element.getAsJsonPrimitive();
+ assertFalse(jsonPrimitive.isString());
+ assertTrue(jsonPrimitive.isNumber());
+ assertEquals(1556L, element.getAsLong());
+ }
+
+ public void testDefaultLongSerializationIntegration() {
+ Gson gson = new GsonBuilder()
+ .setLongSerializationPolicy(LongSerializationPolicy.DEFAULT)
+ .create();
+ assertEquals("[1]", gson.toJson(new long[] { 1L }, long[].class));
+ assertEquals("[1]", gson.toJson(new Long[] { 1L }, Long[].class));
+ }
+
+ public void testStringLongSerialization() throws Exception {
+ JsonElement element = LongSerializationPolicy.STRING.serialize(1556L);
+ assertTrue(element.isJsonPrimitive());
+
+ JsonPrimitive jsonPrimitive = element.getAsJsonPrimitive();
+ assertFalse(jsonPrimitive.isNumber());
+ assertTrue(jsonPrimitive.isString());
+ assertEquals("1556", element.getAsString());
+ }
+
+ public void testStringLongSerializationIntegration() {
+ Gson gson = new GsonBuilder()
+ .setLongSerializationPolicy(LongSerializationPolicy.STRING)
+ .create();
+ assertEquals("[\"1\"]", gson.toJson(new long[] { 1L }, long[].class));
+ assertEquals("[\"1\"]", gson.toJson(new Long[] { 1L }, Long[].class));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/MixedStreamTest.java b/gson/src/test/java/com/google/gson/MixedStreamTest.java
new file mode 100644
index 00000000..00eb4bc8
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/MixedStreamTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2010 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;
+
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+import junit.framework.TestCase;
+
+public final class MixedStreamTest extends TestCase {
+
+ private static final Car BLUE_MUSTANG = new Car("mustang", 0x0000FF);
+ private static final Car BLACK_BMW = new Car("bmw", 0x000000);
+ private static final Car RED_MIATA = new Car("miata", 0xFF0000);
+ private static final String CARS_JSON = "[\n"
+ + " {\n"
+ + " \"name\": \"mustang\",\n"
+ + " \"color\": 255\n"
+ + " },\n"
+ + " {\n"
+ + " \"name\": \"bmw\",\n"
+ + " \"color\": 0\n"
+ + " },\n"
+ + " {\n"
+ + " \"name\": \"miata\",\n"
+ + " \"color\": 16711680\n"
+ + " }\n"
+ + "]";
+
+ public void testWriteMixedStreamed() throws IOException {
+ Gson gson = new Gson();
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+
+ jsonWriter.beginArray();
+ jsonWriter.setIndent(" ");
+ gson.toJson(BLUE_MUSTANG, Car.class, jsonWriter);
+ gson.toJson(BLACK_BMW, Car.class, jsonWriter);
+ gson.toJson(RED_MIATA, Car.class, jsonWriter);
+ jsonWriter.endArray();
+
+ assertEquals(CARS_JSON, stringWriter.toString());
+ }
+
+ public void testReadMixedStreamed() throws IOException {
+ Gson gson = new Gson();
+ StringReader stringReader = new StringReader(CARS_JSON);
+ JsonReader jsonReader = new JsonReader(stringReader);
+
+ jsonReader.beginArray();
+ assertEquals(BLUE_MUSTANG, gson.fromJson(jsonReader, Car.class));
+ assertEquals(BLACK_BMW, gson.fromJson(jsonReader, Car.class));
+ assertEquals(RED_MIATA, gson.fromJson(jsonReader, Car.class));
+ jsonReader.endArray();
+ }
+
+ public void testReaderDoesNotMutateState() throws IOException {
+ Gson gson = new Gson();
+ JsonReader jsonReader = new JsonReader(new StringReader(CARS_JSON));
+ jsonReader.beginArray();
+
+ jsonReader.setLenient(false);
+ gson.fromJson(jsonReader, Car.class);
+ assertFalse(jsonReader.isLenient());
+
+ jsonReader.setLenient(true);
+ gson.fromJson(jsonReader, Car.class);
+ assertTrue(jsonReader.isLenient());
+ }
+
+ public void testWriteDoesNotMutateState() throws IOException {
+ Gson gson = new Gson();
+ JsonWriter jsonWriter = new JsonWriter(new StringWriter());
+ jsonWriter.beginArray();
+
+ jsonWriter.setHtmlSafe(true);
+ jsonWriter.setLenient(true);
+ gson.toJson(BLUE_MUSTANG, Car.class, jsonWriter);
+ assertTrue(jsonWriter.isHtmlSafe());
+ assertTrue(jsonWriter.isLenient());
+
+ jsonWriter.setHtmlSafe(false);
+ jsonWriter.setLenient(false);
+ gson.toJson(BLUE_MUSTANG, Car.class, jsonWriter);
+ assertFalse(jsonWriter.isHtmlSafe());
+ assertFalse(jsonWriter.isLenient());
+ }
+
+ public void testReadInvalidState() throws IOException {
+ Gson gson = new Gson();
+ JsonReader jsonReader = new JsonReader(new StringReader(CARS_JSON));
+ jsonReader.beginArray();
+ jsonReader.beginObject();
+ try {
+ gson.fromJson(jsonReader, String.class);
+ fail();
+ } catch (JsonParseException expected) {
+ }
+ }
+
+ public void testReadClosed() throws IOException {
+ Gson gson = new Gson();
+ JsonReader jsonReader = new JsonReader(new StringReader(CARS_JSON));
+ jsonReader.close();
+ try {
+ gson.fromJson(jsonReader, new TypeToken<List<Car>>() {}.getType());
+ fail();
+ } catch (JsonParseException expected) {
+ }
+ }
+
+ public void testWriteInvalidState() throws IOException {
+ Gson gson = new Gson();
+ JsonWriter jsonWriter = new JsonWriter(new StringWriter());
+ jsonWriter.beginObject();
+ try {
+ gson.toJson(BLUE_MUSTANG, Car.class, jsonWriter);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testWriteClosed() throws IOException {
+ Gson gson = new Gson();
+ JsonWriter jsonWriter = new JsonWriter(new StringWriter());
+ jsonWriter.beginArray();
+ jsonWriter.endArray();
+ jsonWriter.close();
+ try {
+ gson.toJson(BLUE_MUSTANG, Car.class, jsonWriter);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testWriteNulls() {
+ Gson gson = new Gson();
+ try {
+ gson.toJson(new JsonPrimitive("hello"), (JsonWriter) null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ StringWriter stringWriter = new StringWriter();
+ gson.toJson(null, new JsonWriter(stringWriter));
+ assertEquals("null", stringWriter.toString());
+ }
+
+ public void testReadNulls() {
+ Gson gson = new Gson();
+ try {
+ gson.fromJson((JsonReader) null, Integer.class);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ gson.fromJson(new JsonReader(new StringReader("true")), null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testWriteHtmlSafe() {
+ List<String> contents = Arrays.asList("<", ">", "&", "=", "'");
+ Type type = new TypeToken<List<String>>() {}.getType();
+
+ StringWriter writer = new StringWriter();
+ new Gson().toJson(contents, type, new JsonWriter(writer));
+ assertEquals("[\"\\u003c\",\"\\u003e\",\"\\u0026\",\"\\u003d\",\"\\u0027\"]",
+ writer.toString());
+
+ writer = new StringWriter();
+ new GsonBuilder().disableHtmlEscaping().create()
+ .toJson(contents, type, new JsonWriter(writer));
+ assertEquals("[\"<\",\">\",\"&\",\"=\",\"'\"]",
+ writer.toString());
+ }
+
+ public void testWriteLenient() {
+ List<Double> doubles = Arrays.asList(Double.NaN, Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY, -0.0d, 0.5d, 0.0d);
+ Type type = new TypeToken<List<Double>>() {}.getType();
+
+ StringWriter writer = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(writer);
+ new GsonBuilder().serializeSpecialFloatingPointValues().create()
+ .toJson(doubles, type, jsonWriter);
+ assertEquals("[NaN,-Infinity,Infinity,-0.0,0.5,0.0]", writer.toString());
+
+ try {
+ new Gson().toJson(doubles, type, new JsonWriter(new StringWriter()));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ static final class Car {
+ String name;
+ int color;
+
+ Car(String name, int color) {
+ this.name = name;
+ this.color = color;
+ }
+
+ // used by Gson
+ Car() {}
+
+ @Override public int hashCode() {
+ return name.hashCode() ^ color;
+ }
+
+ @Override public boolean equals(Object o) {
+ return o instanceof Car
+ && ((Car) o).name.equals(name)
+ && ((Car) o).color == color;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/MockExclusionStrategy.java b/gson/src/test/java/com/google/gson/MockExclusionStrategy.java
new file mode 100644
index 00000000..2e5db94d
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/MockExclusionStrategy.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+/**
+ * This is a configurable {@link ExclusionStrategy} that can be used for
+ * unit testing.
+ *
+ * @author Joel Leitch
+ */
+final class MockExclusionStrategy implements ExclusionStrategy {
+ private final boolean skipClass;
+ private final boolean skipField;
+
+ public MockExclusionStrategy(boolean skipClass, boolean skipField) {
+ this.skipClass = skipClass;
+ this.skipField = skipField;
+ }
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ return skipField;
+ }
+
+ public boolean shouldSkipClass(Class<?> clazz) {
+ return skipClass;
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java b/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java
new file mode 100644
index 00000000..2891bffc
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 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;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import junit.framework.TestCase;
+
+public final class ObjectTypeAdapterTest extends TestCase {
+ private final Gson gson = new GsonBuilder().create();
+ private final TypeAdapter<Object> adapter = gson.getAdapter(Object.class);
+
+ public void testDeserialize() throws Exception {
+ Map<?, ?> map = (Map<?, ?>) adapter.fromJson("{\"a\":5,\"b\":[1,2,null],\"c\":{\"x\":\"y\"}}");
+ assertEquals(5.0, map.get("a"));
+ assertEquals(Arrays.asList(1.0, 2.0, null), map.get("b"));
+ assertEquals(Collections.singletonMap("x", "y"), map.get("c"));
+ assertEquals(3, map.size());
+ }
+
+ public void testSerialize() throws Exception {
+ Object object = new RuntimeType();
+ assertEquals("{'a':5,'b':[1,2,null]}", adapter.toJson(object).replace("\"", "'"));
+ }
+
+ public void testSerializeNullValue() throws Exception {
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+ map.put("a", null);
+ assertEquals("{'a':null}", adapter.toJson(map).replace('"', '\''));
+ }
+
+ public void testDeserializeNullValue() throws Exception {
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+ map.put("a", null);
+ assertEquals(map, adapter.fromJson("{\"a\":null}"));
+ }
+
+ public void testSerializeObject() throws Exception {
+ assertEquals("{}", adapter.toJson(new Object()));
+ }
+
+ @SuppressWarnings("unused")
+ private class RuntimeType {
+ Object a = 5;
+ Object b = Arrays.asList(1, 2, null);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/OverrideCoreTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/OverrideCoreTypeAdaptersTest.java
new file mode 100644
index 00000000..79ae1698
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/OverrideCoreTypeAdaptersTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 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;
+
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.util.Locale;
+import junit.framework.TestCase;
+
+/**
+ * @author Jesse Wilson
+ */
+public class OverrideCoreTypeAdaptersTest extends TestCase {
+ private static final TypeAdapter<Boolean> booleanAsIntAdapter = new TypeAdapter<Boolean>() {
+ @Override public void write(JsonWriter out, Boolean value) throws IOException {
+ out.value(value ? 1 : 0);
+ }
+ @Override public Boolean read(JsonReader in) throws IOException {
+ int value = in.nextInt();
+ return value != 0;
+ }
+ };
+
+ private static final TypeAdapter<String> swapCaseStringAdapter = new TypeAdapter<String>() {
+ @Override public void write(JsonWriter out, String value) throws IOException {
+ out.value(value.toUpperCase(Locale.US));
+ }
+ @Override public String read(JsonReader in) throws IOException {
+ return in.nextString().toLowerCase(Locale.US);
+ }
+ };
+
+ public void testOverrideWrapperBooleanAdapter() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Boolean.class, booleanAsIntAdapter)
+ .create();
+ assertEquals("true", gson.toJson(true, boolean.class));
+ assertEquals("1", gson.toJson(true, Boolean.class));
+ assertEquals(Boolean.TRUE, gson.fromJson("true", boolean.class));
+ assertEquals(Boolean.TRUE, gson.fromJson("1", Boolean.class));
+ assertEquals(Boolean.FALSE, gson.fromJson("0", Boolean.class));
+ }
+
+ public void testOverridePrimitiveBooleanAdapter() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(boolean.class, booleanAsIntAdapter)
+ .create();
+ assertEquals("1", gson.toJson(true, boolean.class));
+ assertEquals("true", gson.toJson(true, Boolean.class));
+ assertEquals(Boolean.TRUE, gson.fromJson("1", boolean.class));
+ assertEquals(Boolean.TRUE, gson.fromJson("true", Boolean.class));
+ assertEquals("0", gson.toJson(false, boolean.class));
+ }
+
+ public void testOverrideStringAdapter() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(String.class, swapCaseStringAdapter)
+ .create();
+ assertEquals("\"HELLO\"", gson.toJson("Hello", String.class));
+ assertEquals("hello", gson.fromJson("\"Hello\"", String.class));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java b/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java
new file mode 100644
index 00000000..6bae432f
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java
@@ -0,0 +1,177 @@
+/*
+ * 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;
+
+import com.google.gson.internal.$Gson$Types;
+
+import com.google.gson.internal.Primitives;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+
+/**
+ * This class contains some test fixtures for Parameterized types. These classes should ideally
+ * belong either in the common or functional package, but they are placed here because they need
+ * access to package protected elements of com.google.gson.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class ParameterizedTypeFixtures {
+
+ public static class MyParameterizedType<T> {
+ public final T value;
+ public MyParameterizedType(T value) {
+ this.value = value;
+ }
+ public T getValue() {
+ return value;
+ }
+
+ public String getExpectedJson() {
+ String valueAsJson = getExpectedJson(value);
+ return String.format("{\"value\":%s}", valueAsJson);
+ }
+
+ private String getExpectedJson(Object obj) {
+ Class<?> clazz = obj.getClass();
+ if (Primitives.isWrapperType(Primitives.wrap(clazz))) {
+ return obj.toString();
+ } else if (obj.getClass().equals(String.class)) {
+ return "\"" + obj.toString() + "\"";
+ } else {
+ // Try invoking a getExpectedJson() method if it exists
+ try {
+ Method method = clazz.getMethod("getExpectedJson");
+ Object results = method.invoke(obj);
+ return (String) results;
+ } catch (SecurityException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return value == null ? 0 : value.hashCode();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ MyParameterizedType<T> other = (MyParameterizedType<T>) obj;
+ if (value == null) {
+ if (other.value != null) {
+ return false;
+ }
+ } else if (!value.equals(other.value)) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public static class MyParameterizedTypeInstanceCreator<T>
+ implements InstanceCreator<MyParameterizedType<T>>{
+ private final T instanceOfT;
+ /**
+ * Caution the specified instance is reused by the instance creator for each call.
+ * This means that the fields of the same objects will be overwritten by Gson.
+ * This is usually fine in tests since there we deserialize just once, but quite
+ * dangerous in practice.
+ *
+ * @param instanceOfT
+ */
+ public MyParameterizedTypeInstanceCreator(T instanceOfT) {
+ this.instanceOfT = instanceOfT;
+ }
+ public MyParameterizedType<T> createInstance(Type type) {
+ return new MyParameterizedType<T>(instanceOfT);
+ }
+ }
+
+ public static class MyParameterizedTypeAdapter<T>
+ implements JsonSerializer<MyParameterizedType<T>>, JsonDeserializer<MyParameterizedType<T>> {
+ @SuppressWarnings("unchecked")
+ public static<T> String getExpectedJson(MyParameterizedType<T> obj) {
+ Class<T> clazz = (Class<T>) obj.value.getClass();
+ boolean addQuotes = !clazz.isArray() && !Primitives.unwrap(clazz).isPrimitive();
+ StringBuilder sb = new StringBuilder("{\"");
+ sb.append(obj.value.getClass().getSimpleName()).append("\":");
+ if (addQuotes) {
+ sb.append("\"");
+ }
+ sb.append(obj.value.toString());
+ if (addQuotes) {
+ sb.append("\"");
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ public JsonElement serialize(MyParameterizedType<T> src, Type classOfSrc,
+ JsonSerializationContext context) {
+ JsonObject json = new JsonObject();
+ T value = src.getValue();
+ json.add(value.getClass().getSimpleName(), context.serialize(value));
+ return json;
+ }
+
+ @SuppressWarnings("unchecked")
+ public MyParameterizedType<T> deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context) throws JsonParseException {
+ Type genericClass = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
+ Class<?> rawType = $Gson$Types.getRawType(genericClass);
+ String className = rawType.getSimpleName();
+ JsonElement jsonElement = json.getAsJsonObject().get(className);
+
+ T value;
+ if (genericClass == Integer.class) {
+ value = (T) Integer.valueOf(jsonElement.getAsInt());
+ } else if (genericClass == String.class) {
+ value = (T) jsonElement.getAsString();
+ } else {
+ value = (T) jsonElement;
+ }
+
+ if (Primitives.isPrimitive(genericClass)) {
+ PrimitiveTypeAdapter typeAdapter = new PrimitiveTypeAdapter();
+ value = (T) typeAdapter.adaptType(value, rawType);
+ }
+ return new MyParameterizedType<T>(value);
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/ParameterizedTypeTest.java b/gson/src/test/java/com/google/gson/ParameterizedTypeTest.java
new file mode 100644
index 00000000..8b56579e
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/ParameterizedTypeTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import com.google.gson.internal.$Gson$Types;
+import com.google.gson.reflect.TypeToken;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Unit tests for {@code ParamterizedType}s created by the {@link $Gson$Types} class.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class ParameterizedTypeTest extends TestCase {
+ private ParameterizedType ourType;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ ourType = $Gson$Types.newParameterizedTypeWithOwner(null, List.class, String.class);
+ }
+
+ public void testOurTypeFunctionality() throws Exception {
+ Type parameterizedType = new TypeToken<List<String>>() {}.getType();
+ assertNull(ourType.getOwnerType());
+ assertEquals(String.class, ourType.getActualTypeArguments()[0]);
+ assertEquals(List.class, ourType.getRawType());
+ assertEquals(parameterizedType, ourType);
+ assertEquals(parameterizedType.hashCode(), ourType.hashCode());
+ }
+
+ public void testNotEquals() throws Exception {
+ Type differentParameterizedType = new TypeToken<List<Integer>>() {}.getType();
+ assertFalse(differentParameterizedType.equals(ourType));
+ assertFalse(ourType.equals(differentParameterizedType));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java b/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java
new file mode 100644
index 00000000..fb38687b
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+import com.google.gson.internal.Primitives;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Handles type conversion from some object to some primitive (or primitive
+ * wrapper instance).
+ *
+ * @author Joel Leitch
+ */
+final class PrimitiveTypeAdapter {
+
+ @SuppressWarnings("unchecked")
+ public <T> T adaptType(Object from, Class<T> to) {
+ Class<?> aClass = Primitives.wrap(to);
+ if (Primitives.isWrapperType(aClass)) {
+ if (aClass == Character.class) {
+ String value = from.toString();
+ if (value.length() == 1) {
+ return (T) (Character) from.toString().charAt(0);
+ }
+ throw new JsonParseException("The value: " + value + " contains more than a character.");
+ }
+
+ try {
+ Constructor<?> constructor = aClass.getConstructor(String.class);
+ return (T) constructor.newInstance(from.toString());
+ } catch (NoSuchMethodException e) {
+ throw new JsonParseException(e);
+ } catch (IllegalAccessException e) {
+ throw new JsonParseException(e);
+ } catch (InvocationTargetException e) {
+ throw new JsonParseException(e);
+ } catch (InstantiationException e) {
+ throw new JsonParseException(e);
+ }
+ } else if (Enum.class.isAssignableFrom(to)) {
+ // Case where the type being adapted to is an Enum
+ // We will try to convert from.toString() to the enum
+ try {
+ Method valuesMethod = to.getMethod("valueOf", String.class);
+ return (T) valuesMethod.invoke(null, from.toString());
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ throw new JsonParseException("Can not adapt type " + from.getClass() + " to " + to);
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/VersionExclusionStrategyTest.java b/gson/src/test/java/com/google/gson/VersionExclusionStrategyTest.java
new file mode 100644
index 00000000..d878850e
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/VersionExclusionStrategyTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+import com.google.gson.annotations.Since;
+import com.google.gson.internal.Excluder;
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for the {@link Excluder} class.
+ *
+ * @author Joel Leitch
+ */
+public class VersionExclusionStrategyTest extends TestCase {
+ private static final double VERSION = 5.0D;
+
+ public void testClassAndFieldAreAtSameVersion() throws Exception {
+ Excluder excluder = Excluder.DEFAULT.withVersion(VERSION);
+ assertFalse(excluder.excludeClass(MockObject.class, true));
+ assertFalse(excluder.excludeField(MockObject.class.getField("someField"), true));
+ }
+
+ public void testClassAndFieldAreBehindInVersion() throws Exception {
+ Excluder excluder = Excluder.DEFAULT.withVersion(VERSION + 1);
+ assertFalse(excluder.excludeClass(MockObject.class, true));
+ assertFalse(excluder.excludeField(MockObject.class.getField("someField"), true));
+ }
+
+ public void testClassAndFieldAreAheadInVersion() throws Exception {
+ Excluder excluder = Excluder.DEFAULT.withVersion(VERSION - 1);
+ assertTrue(excluder.excludeClass(MockObject.class, true));
+ assertTrue(excluder.excludeField(MockObject.class.getField("someField"), true));
+ }
+
+ @Since(VERSION)
+ private static class MockObject {
+
+ @Since(VERSION)
+ public final int someField = 0;
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/common/MoreAsserts.java b/gson/src/test/java/com/google/gson/common/MoreAsserts.java
new file mode 100644
index 00000000..5e05832a
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/common/MoreAsserts.java
@@ -0,0 +1,73 @@
+/*
+ * 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.common;
+
+import junit.framework.Assert;
+
+import java.util.Collection;
+
+/**
+ * Handy asserts that we wish were present in {@link Assert}
+ * so that we didn't have to write them.
+ *
+ * @author Inderjeet Singh
+ */
+public class MoreAsserts {
+
+ public static void assertEquals(int[] expected, int[] target) {
+ if (expected == null) {
+ Assert.assertNull(target);
+ }
+ Assert.assertEquals(expected.length, target.length);
+ for (int i = 0; i < expected.length; ++i) {
+ Assert.assertEquals(expected[i], target[i]);
+ }
+ }
+
+ public static void assertEquals(Integer[] expected, Integer[] target) {
+ if (expected == null) {
+ Assert.assertNull(target);
+ }
+ Assert.assertEquals(expected.length, target.length);
+ for (int i = 0; i < expected.length; ++i) {
+ Assert.assertEquals(expected[i], target[i]);
+ }
+ }
+
+ /**
+ * Asserts that the specified {@code value} is not present in {@code collection}
+ * @param collection the collection to look into
+ * @param value the value that needs to be checked for presence
+ */
+ public static <T> void assertContains(Collection<T> collection, T value) {
+ for (T entry : collection) {
+ if (entry.equals(value)) {
+ return;
+ }
+ }
+ Assert.fail(value + " not present in " + collection);
+ }
+
+ public static void assertEqualsAndHashCode(Object a, Object b) {
+ Assert.assertTrue(a.equals(b));
+ Assert.assertTrue(b.equals(a));
+ Assert.assertEquals(a.hashCode(), b.hashCode());
+ Assert.assertFalse(a.equals(null));
+ Assert.assertFalse(a.equals(new Object()));
+ }
+
+}
diff --git a/gson/src/test/java/com/google/gson/common/TestTypes.java b/gson/src/test/java/com/google/gson/common/TestTypes.java
new file mode 100644
index 00000000..4754d97b
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/common/TestTypes.java
@@ -0,0 +1,418 @@
+/*
+ * 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.common;
+
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Types used for testing JSON serialization and deserialization
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class TestTypes {
+
+ public static class Base {
+ public static final String BASE_NAME = Base.class.getSimpleName();
+ public static final String BASE_FIELD_KEY = "baseName";
+ public static final String SERIALIZER_KEY = "serializerName";
+ public String baseName = BASE_NAME;
+ public String serializerName;
+ }
+
+ public static class Sub extends Base {
+ public static final String SUB_NAME = Sub.class.getSimpleName();
+ public static final String SUB_FIELD_KEY = "subName";
+ public final String subName = SUB_NAME;
+ }
+
+ public static class ClassWithBaseField {
+ public static final String FIELD_KEY = "base";
+ public final Base base;
+ public ClassWithBaseField(Base base) {
+ this.base = base;
+ }
+ }
+
+ public static class ClassWithBaseArrayField {
+ public static final String FIELD_KEY = "base";
+ public final Base[] base;
+ public ClassWithBaseArrayField(Base[] base) {
+ this.base = base;
+ }
+ }
+
+ public static class ClassWithBaseCollectionField {
+ public static final String FIELD_KEY = "base";
+ public final Collection<Base> base;
+ public ClassWithBaseCollectionField(Collection<Base> base) {
+ this.base = base;
+ }
+ }
+
+ public static class BaseSerializer implements JsonSerializer<Base> {
+ public static final String NAME = BaseSerializer.class.getSimpleName();
+ public JsonElement serialize(Base src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject obj = new JsonObject();
+ obj.addProperty(Base.SERIALIZER_KEY, NAME);
+ return obj;
+ }
+ }
+ public static class SubSerializer implements JsonSerializer<Sub> {
+ public static final String NAME = SubSerializer.class.getSimpleName();
+ public JsonElement serialize(Sub src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject obj = new JsonObject();
+ obj.addProperty(Base.SERIALIZER_KEY, NAME);
+ return obj;
+ }
+ }
+
+ public static class StringWrapper {
+ public final String someConstantStringInstanceField;
+
+ public StringWrapper(String value) {
+ someConstantStringInstanceField = value;
+ }
+ }
+
+ public static class BagOfPrimitives {
+ public static final long DEFAULT_VALUE = 0;
+ public long longValue;
+ public int intValue;
+ public boolean booleanValue;
+ public String stringValue;
+
+ public BagOfPrimitives() {
+ this(DEFAULT_VALUE, 0, false, "");
+ }
+
+ public BagOfPrimitives(long longValue, int intValue, boolean booleanValue, String stringValue) {
+ this.longValue = longValue;
+ this.intValue = intValue;
+ this.booleanValue = booleanValue;
+ this.stringValue = stringValue;
+ }
+
+ public int getIntValue() {
+ return intValue;
+ }
+
+ public String getExpectedJson() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ sb.append("\"longValue\":").append(longValue).append(",");
+ sb.append("\"intValue\":").append(intValue).append(",");
+ sb.append("\"booleanValue\":").append(booleanValue).append(",");
+ sb.append("\"stringValue\":\"").append(stringValue).append("\"");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (booleanValue ? 1231 : 1237);
+ result = prime * result + intValue;
+ result = prime * result + (int) (longValue ^ (longValue >>> 32));
+ result = prime * result + ((stringValue == null) ? 0 : stringValue.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ BagOfPrimitives other = (BagOfPrimitives) obj;
+ if (booleanValue != other.booleanValue)
+ return false;
+ if (intValue != other.intValue)
+ return false;
+ if (longValue != other.longValue)
+ return false;
+ if (stringValue == null) {
+ if (other.stringValue != null)
+ return false;
+ } else if (!stringValue.equals(other.stringValue))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("(longValue=%d,intValue=%d,booleanValue=%b,stringValue=%s)",
+ longValue, intValue, booleanValue, stringValue);
+ }
+ }
+
+ public static class BagOfPrimitiveWrappers {
+ private final Long longValue;
+ private final Integer intValue;
+ private final Boolean booleanValue;
+
+ public BagOfPrimitiveWrappers(Long longValue, Integer intValue, Boolean booleanValue) {
+ this.longValue = longValue;
+ this.intValue = intValue;
+ this.booleanValue = booleanValue;
+ }
+
+ public String getExpectedJson() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ sb.append("\"longValue\":").append(longValue).append(",");
+ sb.append("\"intValue\":").append(intValue).append(",");
+ sb.append("\"booleanValue\":").append(booleanValue);
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ public static class PrimitiveArray {
+ private final long[] longArray;
+
+ public PrimitiveArray() {
+ this(new long[0]);
+ }
+
+ public PrimitiveArray(long[] longArray) {
+ this.longArray = longArray;
+ }
+
+ public String getExpectedJson() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{\"longArray\":[");
+
+ boolean first = true;
+ for (long l : longArray) {
+ if (!first) {
+ sb.append(",");
+ } else {
+ first = false;
+ }
+ sb.append(l);
+ }
+
+ sb.append("]}");
+ return sb.toString();
+ }
+ }
+
+ public static class ClassWithNoFields {
+ // Nothing here.. .
+ @Override
+ public boolean equals(Object other) {
+ return other.getClass() == ClassWithNoFields.class;
+ }
+ }
+
+ public static class Nested {
+ private final BagOfPrimitives primitive1;
+ private final BagOfPrimitives primitive2;
+
+ public Nested() {
+ this(null, null);
+ }
+
+ public Nested(BagOfPrimitives primitive1, BagOfPrimitives primitive2) {
+ this.primitive1 = primitive1;
+ this.primitive2 = primitive2;
+ }
+
+ public String getExpectedJson() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ appendFields(sb);
+ sb.append("}");
+ return sb.toString();
+ }
+
+ public void appendFields(StringBuilder sb) {
+ if (primitive1 != null) {
+ sb.append("\"primitive1\":").append(primitive1.getExpectedJson());
+ }
+ if (primitive1 != null && primitive2 != null) {
+ sb.append(",");
+ }
+ if (primitive2 != null) {
+ sb.append("\"primitive2\":").append(primitive2.getExpectedJson());
+ }
+ }
+ }
+
+ public static class ClassWithTransientFields<T> {
+ public transient T transientT;
+ public final transient long transientLongValue;
+ private final long[] longValue;
+
+ public ClassWithTransientFields() {
+ this(0L);
+ }
+
+ public ClassWithTransientFields(long value) {
+ longValue = new long[] { value };
+ transientLongValue = value + 1;
+ }
+
+ public String getExpectedJson() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ sb.append("\"longValue\":[").append(longValue[0]).append("]");
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ public static class ClassWithCustomTypeConverter {
+ private final BagOfPrimitives bag;
+ private final int value;
+
+ public ClassWithCustomTypeConverter() {
+ this(new BagOfPrimitives(), 10);
+ }
+
+ public ClassWithCustomTypeConverter(int value) {
+ this(new BagOfPrimitives(value, value, false, ""), value);
+ }
+
+ public ClassWithCustomTypeConverter(BagOfPrimitives bag, int value) {
+ this.bag = bag;
+ this.value = value;
+ }
+
+ public BagOfPrimitives getBag() {
+ return bag;
+ }
+
+ public String getExpectedJson() {
+ return "{\"url\":\"" + bag.getExpectedJson() + "\",\"value\":" + value + "}";
+ }
+
+ public int getValue() {
+ return value;
+ }
+ }
+
+ public static class ArrayOfObjects {
+ private final BagOfPrimitives[] elements;
+ public ArrayOfObjects() {
+ elements = new BagOfPrimitives[3];
+ for (int i = 0; i < elements.length; ++i) {
+ elements[i] = new BagOfPrimitives(i, i+2, false, "i"+i);
+ }
+ }
+ public String getExpectedJson() {
+ StringBuilder sb = new StringBuilder("{\"elements\":[");
+ boolean first = true;
+ for (BagOfPrimitives element : elements) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(element.getExpectedJson());
+ }
+ sb.append("]}");
+ return sb.toString();
+ }
+ }
+
+ public static class ClassOverridingEquals {
+ public ClassOverridingEquals ref;
+
+ public String getExpectedJson() {
+ if (ref == null) {
+ return "{}";
+ }
+ return "{\"ref\":" + ref.getExpectedJson() + "}";
+ }
+ @Override
+ public boolean equals(Object obj) {
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return 1;
+ }
+ }
+
+ public static class ClassWithArray {
+ public final Object[] array;
+ public ClassWithArray() {
+ array = null;
+ }
+
+ public ClassWithArray(Object[] array) {
+ this.array = array;
+ }
+ }
+
+ public static class ClassWithObjects {
+ public final BagOfPrimitives bag;
+ public ClassWithObjects() {
+ this(new BagOfPrimitives());
+ }
+ public ClassWithObjects(BagOfPrimitives bag) {
+ this.bag = bag;
+ }
+ }
+
+ public static class ClassWithSerializedNameFields {
+ @SerializedName("fooBar") public final int f;
+ @SerializedName("Another Foo") public final int g;
+
+ public ClassWithSerializedNameFields() {
+ this(1, 4);
+ }
+ public ClassWithSerializedNameFields(int f, int g) {
+ this.f = f;
+ this.g = g;
+ }
+
+ public String getExpectedJson() {
+ return '{' + "\"fooBar\":" + f + ",\"Another Foo\":" + g + '}';
+ }
+ }
+
+ public static class CrazyLongTypeAdapter
+ implements JsonSerializer<Long>, JsonDeserializer<Long> {
+ public static final long DIFFERENCE = 5L;
+ public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive(src + DIFFERENCE);
+ }
+
+ public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ return json.getAsLong() - DIFFERENCE;
+ }
+}
+} \ No newline at end of file
diff --git a/gson/src/test/java/com/google/gson/functional/ArrayTest.java b/gson/src/test/java/com/google/gson/functional/ArrayTest.java
new file mode 100644
index 00000000..11388e90
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/ArrayTest.java
@@ -0,0 +1,268 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.JsonParseException;
+import com.google.gson.common.MoreAsserts;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.common.TestTypes.ClassWithObjects;
+import com.google.gson.reflect.TypeToken;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+/**
+ * Functional tests for Json serialization and deserialization of arrays.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class ArrayTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testTopLevelArrayOfIntsSerialization() {
+ int[] target = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ assertEquals("[1,2,3,4,5,6,7,8,9]", gson.toJson(target));
+ }
+
+ public void testTopLevelArrayOfIntsDeserialization() {
+ int[] expected = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ int[] actual = gson.fromJson("[1,2,3,4,5,6,7,8,9]", int[].class);
+ MoreAsserts.assertEquals(expected, actual);
+ }
+
+ public void testInvalidArrayDeserialization() {
+ String json = "[1, 2 3, 4, 5]";
+ try {
+ gson.fromJson(json, int[].class);
+ fail("Gson should not deserialize array elements with missing ,");
+ } catch (JsonParseException expected) {
+ }
+ }
+
+ public void testEmptyArraySerialization() {
+ int[] target = {};
+ assertEquals("[]", gson.toJson(target));
+ }
+
+ public void testEmptyArrayDeserialization() {
+ int[] actualObject = gson.fromJson("[]", int[].class);
+ assertTrue(actualObject.length == 0);
+
+ Integer[] actualObject2 = gson.fromJson("[]", Integer[].class);
+ assertTrue(actualObject2.length == 0);
+
+ actualObject = gson.fromJson("[ ]", int[].class);
+ assertTrue(actualObject.length == 0);
+ }
+
+ public void testNullsInArraySerialization() {
+ String[] array = {"foo", null, "bar"};
+ String expected = "[\"foo\",null,\"bar\"]";
+ String json = gson.toJson(array);
+ assertEquals(expected, json);
+ }
+
+ public void testNullsInArrayDeserialization() {
+ String json = "[\"foo\",null,\"bar\"]";
+ String[] expected = {"foo", null, "bar"};
+ String[] target = gson.fromJson(json, expected.getClass());
+ for (int i = 0; i < expected.length; ++i) {
+ assertEquals(expected[i], target[i]);
+ }
+ }
+
+ public void testSingleNullInArraySerialization() {
+ BagOfPrimitives[] array = new BagOfPrimitives[1];
+ array[0] = null;
+ String json = gson.toJson(array);
+ assertEquals("[null]", json);
+ }
+
+ public void testSingleNullInArrayDeserialization() {
+ BagOfPrimitives[] array = gson.fromJson("[null]", BagOfPrimitives[].class);
+ assertNull(array[0]);
+ }
+
+ public void testNullsInArrayWithSerializeNullPropertySetSerialization() {
+ gson = new GsonBuilder().serializeNulls().create();
+ String[] array = {"foo", null, "bar"};
+ String expected = "[\"foo\",null,\"bar\"]";
+ String json = gson.toJson(array);
+ assertEquals(expected, json);
+ }
+
+ public void testArrayOfStringsSerialization() {
+ String[] target = {"Hello", "World"};
+ assertEquals("[\"Hello\",\"World\"]", gson.toJson(target));
+ }
+
+ public void testArrayOfStringsDeserialization() {
+ String json = "[\"Hello\",\"World\"]";
+ String[] target = gson.fromJson(json, String[].class);
+ assertEquals("Hello", target[0]);
+ assertEquals("World", target[1]);
+ }
+
+ public void testSingleStringArraySerialization() throws Exception {
+ String[] s = { "hello" };
+ String output = gson.toJson(s);
+ assertEquals("[\"hello\"]", output);
+ }
+
+ public void testSingleStringArrayDeserialization() throws Exception {
+ String json = "[\"hello\"]";
+ String[] arrayType = gson.fromJson(json, String[].class);
+ assertEquals(1, arrayType.length);
+ assertEquals("hello", arrayType[0]);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testArrayOfCollectionSerialization() throws Exception {
+ StringBuilder sb = new StringBuilder("[");
+ int arraySize = 3;
+
+ Type typeToSerialize = new TypeToken<Collection<Integer>[]>() {}.getType();
+ Collection<Integer>[] arrayOfCollection = new ArrayList[arraySize];
+ for (int i = 0; i < arraySize; ++i) {
+ int startValue = (3 * i) + 1;
+ sb.append('[').append(startValue).append(',').append(startValue + 1).append(']');
+ ArrayList<Integer> tmpList = new ArrayList<Integer>();
+ tmpList.add(startValue);
+ tmpList.add(startValue + 1);
+ arrayOfCollection[i] = tmpList;
+
+ if (i < arraySize - 1) {
+ sb.append(',');
+ }
+ }
+ sb.append(']');
+
+ String json = gson.toJson(arrayOfCollection, typeToSerialize);
+ assertEquals(sb.toString(), json);
+ }
+
+ public void testArrayOfCollectionDeserialization() throws Exception {
+ String json = "[[1,2],[3,4]]";
+ Type type = new TypeToken<Collection<Integer>[]>() {}.getType();
+ Collection<Integer>[] target = gson.fromJson(json, type);
+
+ assertEquals(2, target.length);
+ MoreAsserts.assertEquals(new Integer[] { 1, 2 }, target[0].toArray(new Integer[0]));
+ MoreAsserts.assertEquals(new Integer[] { 3, 4 }, target[1].toArray(new Integer[0]));
+ }
+
+ public void testArrayOfPrimitivesAsObjectsSerialization() throws Exception {
+ Object[] objs = new Object[] {1, "abc", 0.3f, 5L};
+ String json = gson.toJson(objs);
+ assertTrue(json.contains("abc"));
+ assertTrue(json.contains("0.3"));
+ assertTrue(json.contains("5"));
+ }
+
+ public void testArrayOfPrimitivesAsObjectsDeserialization() throws Exception {
+ 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]);
+ assertEquals(0.3, ((Number)objs[2]).doubleValue());
+ assertEquals(new BigDecimal("1.1"), new BigDecimal(objs[3].toString()));
+ assertEquals(5, ((Number)objs[4]).shortValue());
+ }
+
+ public void testObjectArrayWithNonPrimitivesSerialization() throws Exception {
+ ClassWithObjects classWithObjects = new ClassWithObjects();
+ BagOfPrimitives bagOfPrimitives = new BagOfPrimitives();
+ String classWithObjectsJson = gson.toJson(classWithObjects);
+ String bagOfPrimitivesJson = gson.toJson(bagOfPrimitives);
+
+ Object[] objects = new Object[] { classWithObjects, bagOfPrimitives };
+ String json = gson.toJson(objects);
+
+ assertTrue(json.contains(classWithObjectsJson));
+ assertTrue(json.contains(bagOfPrimitivesJson));
+ }
+
+ public void testArrayOfNullSerialization() {
+ Object[] array = new Object[] {null};
+ String json = gson.toJson(array);
+ assertEquals("[null]", json);
+ }
+
+ public void testArrayOfNullDeserialization() {
+ String[] values = gson.fromJson("[null]", String[].class);
+ assertNull(values[0]);
+ }
+
+ /**
+ * Regression tests for Issue 272
+ */
+ public void testMultidimenstionalArraysSerialization() {
+ String[][] items = new String[][]{
+ {"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 = gson.toJson(items);
+ assertTrue(json.contains("[[\"3m Co"));
+ assertTrue(json.contains("Manufacturing\"]]"));
+ }
+
+ public void testMultiDimenstionalObjectArraysSerialization() {
+ Object[][] array = new Object[][] { new Object[] { 1, 2 } };
+ assertEquals("[[1,2]]", gson.toJson(array));
+ }
+
+ /**
+ * Regression test for Issue 205
+ */
+ public void testMixingTypesInObjectArraySerialization() {
+ Object[] array = new Object[] { 1, 2, new Object[] { "one", "two", 3 } };
+ assertEquals("[1,2,[\"one\",\"two\",3]]", gson.toJson(array));
+ }
+
+ /**
+ * 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[][] items = gson.fromJson(json, String[][].class);
+ assertEquals("3m Co", items[0][0]);
+ assertEquals("Manufacturing", items[1][5]);
+ }
+
+ /** http://code.google.com/p/google-gson/issues/detail?id=342 */
+ public void testArrayElementsAreArrays() {
+ Object[] stringArrays = {
+ new String[] {"test1", "test2"},
+ new String[] {"test3", "test4"}
+ };
+ assertEquals("[[\"test1\",\"test2\"],[\"test3\",\"test4\"]]",
+ new Gson().toJson(stringArrays));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/CircularReferenceTest.java b/gson/src/test/java/com/google/gson/functional/CircularReferenceTest.java
new file mode 100644
index 00000000..d352e241
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/CircularReferenceTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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 java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import junit.framework.TestCase;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.common.TestTypes.ClassOverridingEquals;
+
+/**
+ * Functional tests related to circular reference detection and error reporting.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class CircularReferenceTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testCircularSerialization() throws Exception {
+ ContainsReferenceToSelfType a = new ContainsReferenceToSelfType();
+ ContainsReferenceToSelfType b = new ContainsReferenceToSelfType();
+ a.children.add(b);
+ b.children.add(a);
+ try {
+ gson.toJson(a);
+ fail("Circular types should not get printed!");
+ } catch (StackOverflowError expected) {
+ }
+ }
+
+ public void testSelfReferenceIgnoredInSerialization() throws Exception {
+ ClassOverridingEquals objA = new ClassOverridingEquals();
+ objA.ref = objA;
+
+ String json = gson.toJson(objA);
+ assertFalse(json.contains("ref")); // self-reference is ignored
+ }
+
+ public void testSelfReferenceArrayFieldSerialization() throws Exception {
+ ClassWithSelfReferenceArray objA = new ClassWithSelfReferenceArray();
+ objA.children = new ClassWithSelfReferenceArray[]{objA};
+
+ try {
+ gson.toJson(objA);
+ fail("Circular reference to self can not be serialized!");
+ } catch (StackOverflowError expected) {
+ }
+ }
+
+ public void testSelfReferenceCustomHandlerSerialization() throws Exception {
+ ClassWithSelfReference obj = new ClassWithSelfReference();
+ obj.child = obj;
+ Gson gson = new GsonBuilder().registerTypeAdapter(ClassWithSelfReference.class, new JsonSerializer<ClassWithSelfReference>() {
+ public JsonElement serialize(ClassWithSelfReference src, Type typeOfSrc,
+ JsonSerializationContext context) {
+ JsonObject obj = new JsonObject();
+ obj.addProperty("property", "value");
+ obj.add("child", context.serialize(src.child));
+ return obj;
+ }
+ }).create();
+ try {
+ gson.toJson(obj);
+ fail("Circular reference to self can not be serialized!");
+ } catch (StackOverflowError expected) {
+ }
+ }
+
+ public void testDirectedAcyclicGraphSerialization() throws Exception {
+ ContainsReferenceToSelfType a = new ContainsReferenceToSelfType();
+ ContainsReferenceToSelfType b = new ContainsReferenceToSelfType();
+ ContainsReferenceToSelfType c = new ContainsReferenceToSelfType();
+ a.children.add(b);
+ a.children.add(c);
+ b.children.add(c);
+ assertNotNull(gson.toJson(a));
+ }
+
+ public void testDirectedAcyclicGraphDeserialization() throws Exception {
+ String json = "{\"children\":[{\"children\":[{\"children\":[]}]},{\"children\":[]}]}";
+ ContainsReferenceToSelfType target = gson.fromJson(json, ContainsReferenceToSelfType.class);
+ assertNotNull(target);
+ assertEquals(2, target.children.size());
+ }
+
+ private static class ContainsReferenceToSelfType {
+ Collection<ContainsReferenceToSelfType> children = new ArrayList<ContainsReferenceToSelfType>();
+ }
+
+ private static class ClassWithSelfReference {
+ ClassWithSelfReference child;
+ }
+
+ private static class ClassWithSelfReferenceArray {
+ @SuppressWarnings("unused")
+ ClassWithSelfReferenceArray[] children;
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/CollectionTest.java b/gson/src/test/java/com/google/gson/functional/CollectionTest.java
new file mode 100644
index 00000000..ac6fec93
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/CollectionTest.java
@@ -0,0 +1,360 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.common.MoreAsserts;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.reflect.TypeToken;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+/**
+ * Functional tests for Json serialization and deserialization of collections.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class CollectionTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testTopLevelCollectionOfIntegersSerialization() {
+ Collection<Integer> target = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ Type targetType = new TypeToken<Collection<Integer>>() {}.getType();
+ String json = gson.toJson(target, targetType);
+ assertEquals("[1,2,3,4,5,6,7,8,9]", json);
+ }
+
+ public void testTopLevelCollectionOfIntegersDeserialization() {
+ String json = "[0,1,2,3,4,5,6,7,8,9]";
+ Type collectionType = new TypeToken<Collection<Integer>>() { }.getType();
+ Collection<Integer> target = gson.fromJson(json, collectionType);
+ int[] expected = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ MoreAsserts.assertEquals(expected, toIntArray(target));
+ }
+
+ public void testTopLevelListOfIntegerCollectionsDeserialization() throws Exception {
+ String json = "[[1,2,3],[4,5,6],[7,8,9]]";
+ Type collectionType = new TypeToken<Collection<Collection<Integer>>>() {}.getType();
+ List<Collection<Integer>> target = gson.fromJson(json, collectionType);
+ int[][] expected = new int[3][3];
+ for (int i = 0; i < 3; ++i) {
+ int start = (3 * i) + 1;
+ for (int j = 0; j < 3; ++j) {
+ expected[i][j] = start + j;
+ }
+ }
+
+ for (int i = 0; i < 3; i++) {
+ MoreAsserts.assertEquals(expected[i], toIntArray(target.get(i)));
+ }
+ }
+
+ public void testLinkedListSerialization() {
+ List<String> list = new LinkedList<String>();
+ list.add("a1");
+ list.add("a2");
+ Type linkedListType = new TypeToken<LinkedList<String>>() {}.getType();
+ String json = gson.toJson(list, linkedListType);
+ assertTrue(json.contains("a1"));
+ assertTrue(json.contains("a2"));
+ }
+
+ public void testLinkedListDeserialization() {
+ String json = "['a1','a2']";
+ Type linkedListType = new TypeToken<LinkedList<String>>() {}.getType();
+ List<String> list = gson.fromJson(json, linkedListType);
+ assertEquals("a1", list.get(0));
+ assertEquals("a2", list.get(1));
+ }
+
+ public void testQueueSerialization() {
+ Queue<String> queue = new LinkedList<String>();
+ queue.add("a1");
+ queue.add("a2");
+ Type queueType = new TypeToken<Queue<String>>() {}.getType();
+ String json = gson.toJson(queue, queueType);
+ assertTrue(json.contains("a1"));
+ assertTrue(json.contains("a2"));
+ }
+
+ public void testQueueDeserialization() {
+ String json = "['a1','a2']";
+ Type queueType = new TypeToken<Queue<String>>() {}.getType();
+ Queue<String> queue = gson.fromJson(json, queueType);
+ assertEquals("a1", queue.element());
+ queue.remove();
+ assertEquals("a2", queue.element());
+ }
+
+ public void testNullsInListSerialization() {
+ List<String> list = new ArrayList<String>();
+ list.add("foo");
+ list.add(null);
+ list.add("bar");
+ String expected = "[\"foo\",null,\"bar\"]";
+ Type typeOfList = new TypeToken<List<String>>() {}.getType();
+ String json = gson.toJson(list, typeOfList);
+ assertEquals(expected, json);
+ }
+
+ public void testNullsInListDeserialization() {
+ List<String> expected = new ArrayList<String>();
+ expected.add("foo");
+ expected.add(null);
+ expected.add("bar");
+ String json = "[\"foo\",null,\"bar\"]";
+ Type expectedType = new TypeToken<List<String>>() {}.getType();
+ List<String> target = gson.fromJson(json, expectedType);
+ for (int i = 0; i < expected.size(); ++i) {
+ assertEquals(expected.get(i), target.get(i));
+ }
+ }
+
+ public void testCollectionOfObjectSerialization() {
+ List<Object> target = new ArrayList<Object>();
+ target.add("Hello");
+ target.add("World");
+ assertEquals("[\"Hello\",\"World\"]", gson.toJson(target));
+
+ Type type = new TypeToken<List<Object>>() {}.getType();
+ assertEquals("[\"Hello\",\"World\"]", gson.toJson(target, type));
+ }
+
+ public void testCollectionOfObjectWithNullSerialization() {
+ List<Object> target = new ArrayList<Object>();
+ target.add("Hello");
+ target.add(null);
+ target.add("World");
+ assertEquals("[\"Hello\",null,\"World\"]", gson.toJson(target));
+
+ Type type = new TypeToken<List<Object>>() {}.getType();
+ assertEquals("[\"Hello\",null,\"World\"]", gson.toJson(target, type));
+ }
+
+ public void testCollectionOfStringsSerialization() {
+ List<String> target = new ArrayList<String>();
+ target.add("Hello");
+ target.add("World");
+ assertEquals("[\"Hello\",\"World\"]", gson.toJson(target));
+ }
+
+ public void testCollectionOfBagOfPrimitivesSerialization() {
+ List<BagOfPrimitives> target = new ArrayList<BagOfPrimitives>();
+ BagOfPrimitives objA = new BagOfPrimitives(3L, 1, true, "blah");
+ BagOfPrimitives objB = new BagOfPrimitives(2L, 6, false, "blahB");
+ target.add(objA);
+ target.add(objB);
+
+ String result = gson.toJson(target);
+ assertTrue(result.startsWith("["));
+ assertTrue(result.endsWith("]"));
+ for (BagOfPrimitives obj : target) {
+ assertTrue(result.contains(obj.getExpectedJson()));
+ }
+ }
+
+ public void testCollectionOfStringsDeserialization() {
+ String json = "[\"Hello\",\"World\"]";
+ Type collectionType = new TypeToken<Collection<String>>() { }.getType();
+ Collection<String> target = gson.fromJson(json, collectionType);
+
+ assertTrue(target.contains("Hello"));
+ assertTrue(target.contains("World"));
+ }
+
+ public void testRawCollectionOfIntegersSerialization() {
+ Collection<Integer> target = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ assertEquals("[1,2,3,4,5,6,7,8,9]", gson.toJson(target));
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void testRawCollectionSerialization() {
+ BagOfPrimitives bag1 = new BagOfPrimitives();
+ Collection target = Arrays.asList(bag1, bag1);
+ String json = gson.toJson(target);
+ assertTrue(json.contains(bag1.getExpectedJson()));
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void testRawCollectionDeserializationNotAlllowed() {
+ String json = "[0,1,2,3,4,5,6,7,8,9]";
+ Collection integers = gson.fromJson(json, Collection.class);
+ // JsonReader converts numbers to double by default so we need a floating point comparison
+ assertEquals(Arrays.asList(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0), integers);
+
+ json = "[\"Hello\", \"World\"]";
+ Collection strings = gson.fromJson(json, Collection.class);
+ assertTrue(strings.contains("Hello"));
+ assertTrue(strings.contains("World"));
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public void testRawCollectionOfBagOfPrimitivesNotAllowed() {
+ BagOfPrimitives bag = new BagOfPrimitives(10, 20, false, "stringValue");
+ String json = '[' + bag.getExpectedJson() + ',' + bag.getExpectedJson() + ']';
+ Collection target = gson.fromJson(json, Collection.class);
+ assertEquals(2, target.size());
+ for (Object bag1 : target) {
+ // Gson 2.0 converts raw objects into maps
+ Map<String, Object> values = (Map<String, Object>) bag1;
+ assertTrue(values.containsValue(10.0));
+ assertTrue(values.containsValue(20.0));
+ assertTrue(values.containsValue("stringValue"));
+ }
+ }
+
+ public void testWildcardPrimitiveCollectionSerilaization() throws Exception {
+ Collection<? extends Integer> target = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ Type collectionType = new TypeToken<Collection<? extends Integer>>() { }.getType();
+ String json = gson.toJson(target, collectionType);
+ assertEquals("[1,2,3,4,5,6,7,8,9]", json);
+
+ json = gson.toJson(target);
+ assertEquals("[1,2,3,4,5,6,7,8,9]", json);
+ }
+
+ public void testWildcardPrimitiveCollectionDeserilaization() throws Exception {
+ String json = "[1,2,3,4,5,6,7,8,9]";
+ Type collectionType = new TypeToken<Collection<? extends Integer>>() { }.getType();
+ Collection<? extends Integer> target = gson.fromJson(json, collectionType);
+ assertEquals(9, target.size());
+ assertTrue(target.contains(1));
+ assertTrue(target.contains(9));
+ }
+
+ public void testWildcardCollectionField() throws Exception {
+ Collection<BagOfPrimitives> collection = new ArrayList<BagOfPrimitives>();
+ BagOfPrimitives objA = new BagOfPrimitives(3L, 1, true, "blah");
+ BagOfPrimitives objB = new BagOfPrimitives(2L, 6, false, "blahB");
+ collection.add(objA);
+ collection.add(objB);
+
+ ObjectWithWildcardCollection target = new ObjectWithWildcardCollection(collection);
+ String json = gson.toJson(target);
+ assertTrue(json.contains(objA.getExpectedJson()));
+ assertTrue(json.contains(objB.getExpectedJson()));
+
+ target = gson.fromJson(json, ObjectWithWildcardCollection.class);
+ Collection<? extends BagOfPrimitives> deserializedCollection = target.getCollection();
+ assertEquals(2, deserializedCollection.size());
+ assertTrue(deserializedCollection.contains(objA));
+ assertTrue(deserializedCollection.contains(objB));
+ }
+
+ public void testFieldIsArrayList() {
+ HasArrayListField object = new HasArrayListField();
+ object.longs.add(1L);
+ object.longs.add(3L);
+ String json = gson.toJson(object, HasArrayListField.class);
+ assertEquals("{\"longs\":[1,3]}", json);
+ HasArrayListField copy = gson.fromJson("{\"longs\":[1,3]}", HasArrayListField.class);
+ assertEquals(Arrays.asList(1L, 3L), copy.longs);
+ }
+
+ public void testUserCollectionTypeAdapter() {
+ Type listOfString = new TypeToken<List<String>>() {}.getType();
+ Object stringListSerializer = new JsonSerializer<List<String>>() {
+ public JsonElement serialize(List<String> src, Type typeOfSrc,
+ JsonSerializationContext context) {
+ return new JsonPrimitive(src.get(0) + ";" + src.get(1));
+ }
+ };
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(listOfString, stringListSerializer)
+ .create();
+ assertEquals("\"ab;cd\"", gson.toJson(Arrays.asList("ab", "cd"), listOfString));
+ }
+
+ static class HasArrayListField {
+ ArrayList<Long> longs = new ArrayList<Long>();
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static int[] toIntArray(Collection collection) {
+ int[] ints = new int[collection.size()];
+ int i = 0;
+ for (Iterator iterator = collection.iterator(); iterator.hasNext(); ++i) {
+ Object obj = iterator.next();
+ if (obj instanceof Integer) {
+ ints[i] = ((Integer)obj).intValue();
+ } else if (obj instanceof Long) {
+ ints[i] = ((Long)obj).intValue();
+ }
+ }
+ return ints;
+ }
+
+ private static class ObjectWithWildcardCollection {
+ private final Collection<? extends BagOfPrimitives> collection;
+
+ public ObjectWithWildcardCollection(Collection<? extends BagOfPrimitives> collection) {
+ this.collection = collection;
+ }
+
+ public Collection<? extends BagOfPrimitives> getCollection() {
+ return collection;
+ }
+ }
+
+ private static class Entry {
+ int value;
+ Entry(int value) {
+ this.value = value;
+ }
+ }
+ public void testSetSerialization() {
+ Set<Entry> set = new HashSet<Entry>();
+ set.add(new Entry(1));
+ set.add(new Entry(2));
+ String json = gson.toJson(set);
+ assertTrue(json.contains("1"));
+ assertTrue(json.contains("2"));
+ }
+ public void testSetDeserialization() {
+ String json = "[{value:1},{value:2}]";
+ Type type = new TypeToken<Set<Entry>>() {}.getType();
+ Set<Entry> set = gson.fromJson(json, type);
+ assertEquals(2, set.size());
+ for (Entry entry : set) {
+ assertTrue(entry.value == 1 || entry.value == 2);
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java b/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java
new file mode 100755
index 00000000..2dccf4b6
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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 java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import junit.framework.TestCase;
+
+import com.google.gson.Gson;
+
+/**
+ * Tests for ensuring Gson thread-safety.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class ConcurrencyTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ /**
+ * Source-code based on
+ * http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081
+ */
+ public void testSingleThreadSerialization() {
+ MyObject myObj = new MyObject();
+ for (int i = 0; i < 10; i++) {
+ gson.toJson(myObj);
+ }
+ }
+
+ /**
+ * Source-code based on
+ * http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081
+ */
+ public void testSingleThreadDeserialization() {
+ for (int i = 0; i < 10; i++) {
+ gson.fromJson("{'a':'hello','b':'world','i':1}", MyObject.class);
+ }
+ }
+
+ /**
+ * Source-code based on
+ * http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081
+ */
+ public void testMultiThreadSerialization() throws InterruptedException {
+ final CountDownLatch startLatch = new CountDownLatch(1);
+ final CountDownLatch finishedLatch = new CountDownLatch(10);
+ final AtomicBoolean failed = new AtomicBoolean(false);
+ ExecutorService executor = Executors.newFixedThreadPool(10);
+ for (int taskCount = 0; taskCount < 10; taskCount++) {
+ executor.execute(new Runnable() {
+ public void run() {
+ MyObject myObj = new MyObject();
+ try {
+ startLatch.await();
+ for (int i = 0; i < 10; i++) {
+ gson.toJson(myObj);
+ }
+ } catch (Throwable t) {
+ failed.set(true);
+ } finally {
+ finishedLatch.countDown();
+ }
+ }
+ });
+ }
+ startLatch.countDown();
+ finishedLatch.await();
+ assertFalse(failed.get());
+ }
+
+ /**
+ * Source-code based on
+ * http://groups.google.com/group/google-gson/browse_thread/thread/563bb51ee2495081
+ */
+ public void testMultiThreadDeserialization() throws InterruptedException {
+ final CountDownLatch startLatch = new CountDownLatch(1);
+ final CountDownLatch finishedLatch = new CountDownLatch(10);
+ final AtomicBoolean failed = new AtomicBoolean(false);
+ ExecutorService executor = Executors.newFixedThreadPool(10);
+ for (int taskCount = 0; taskCount < 10; taskCount++) {
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ startLatch.await();
+ for (int i = 0; i < 10; i++) {
+ gson.fromJson("{'a':'hello','b':'world','i':1}", MyObject.class);
+ }
+ } catch (Throwable t) {
+ failed.set(true);
+ } finally {
+ finishedLatch.countDown();
+ }
+ }
+ });
+ }
+ startLatch.countDown();
+ finishedLatch.await();
+ assertFalse(failed.get());
+ }
+
+ @SuppressWarnings("unused")
+ private static class MyObject {
+ String a;
+ String b;
+ int i;
+
+ MyObject() {
+ this("hello", "world", 42);
+ }
+
+ public MyObject(String a, String b, int i) {
+ this.a = a;
+ this.b = b;
+ this.i = i;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java b/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java
new file mode 100644
index 00000000..54ecade2
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java
@@ -0,0 +1,207 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.common.TestTypes.Base;
+import com.google.gson.common.TestTypes.ClassWithBaseField;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Type;
+
+/**
+ * Functional Test exercising custom deserialization only. When test applies to both
+ * serialization and deserialization then add it to CustomTypeAdapterTest.
+ *
+ * @author Joel Leitch
+ */
+public class CustomDeserializerTest extends TestCase {
+ private static final String DEFAULT_VALUE = "test123";
+ private static final String SUFFIX = "blah";
+
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new GsonBuilder().registerTypeAdapter(DataHolder.class, new DataHolderDeserializer()).create();
+ }
+
+ public void testDefaultConstructorNotCalledOnObject() throws Exception {
+ DataHolder data = new DataHolder(DEFAULT_VALUE);
+ String json = gson.toJson(data);
+
+ DataHolder actual = gson.fromJson(json, DataHolder.class);
+ assertEquals(DEFAULT_VALUE + SUFFIX, actual.getData());
+ }
+
+ public void testDefaultConstructorNotCalledOnField() throws Exception {
+ DataHolderWrapper dataWrapper = new DataHolderWrapper(new DataHolder(DEFAULT_VALUE));
+ String json = gson.toJson(dataWrapper);
+
+ DataHolderWrapper actual = gson.fromJson(json, DataHolderWrapper.class);
+ assertEquals(DEFAULT_VALUE + SUFFIX, actual.getWrappedData().getData());
+ }
+
+ private static class DataHolder {
+ private final String data;
+
+ // For use by Gson
+ @SuppressWarnings("unused")
+ private DataHolder() {
+ throw new IllegalStateException();
+ }
+
+ public DataHolder(String data) {
+ this.data = data;
+ }
+
+ public String getData() {
+ return data;
+ }
+ }
+
+ private static class DataHolderWrapper {
+ private final DataHolder wrappedData;
+
+ // For use by Gson
+ @SuppressWarnings("unused")
+ private DataHolderWrapper() {
+ this(new DataHolder(DEFAULT_VALUE));
+ }
+
+ public DataHolderWrapper(DataHolder data) {
+ this.wrappedData = data;
+ }
+
+ public DataHolder getWrappedData() {
+ return wrappedData;
+ }
+ }
+
+ private static class DataHolderDeserializer implements JsonDeserializer<DataHolder> {
+ public DataHolder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ JsonObject jsonObj = json.getAsJsonObject();
+ String dataString = jsonObj.get("data").getAsString();
+ return new DataHolder(dataString + SUFFIX);
+ }
+ }
+
+ public void testJsonTypeFieldBasedDeserialization() {
+ String json = "{field1:'abc',field2:'def',__type__:'SUB_TYPE1'}";
+ Gson gson = new GsonBuilder().registerTypeAdapter(MyBase.class, new JsonDeserializer<MyBase>() {
+ 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());
+ }
+ }).create();
+ SubType1 target = (SubType1) gson.fromJson(json, MyBase.class);
+ assertEquals("abc", target.field1);
+ }
+
+ private static class MyBase {
+ static final String TYPE_ACCESS = "__type__";
+ }
+
+ private enum SubTypes {
+ SUB_TYPE1(SubType1.class),
+ SUB_TYPE2(SubType2.class);
+ private final Type subClass;
+ private SubTypes(Type subClass) {
+ this.subClass = subClass;
+ }
+ public Type getSubclass() {
+ return subClass;
+ }
+ }
+
+ private static class SubType1 extends MyBase {
+ String field1;
+ }
+
+ private static class SubType2 extends MyBase {
+ @SuppressWarnings("unused")
+ String field2;
+ }
+
+ public void testCustomDeserializerReturnsNullForTopLevelObject() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
+ public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ return null;
+ }
+ }).create();
+ String json = "{baseName:'Base',subName:'SubRevised'}";
+ Base target = gson.fromJson(json, Base.class);
+ assertNull(target);
+ }
+
+ public void testCustomDeserializerReturnsNull() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
+ public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ return null;
+ }
+ }).create();
+ String json = "{base:{baseName:'Base',subName:'SubRevised'}}";
+ ClassWithBaseField target = gson.fromJson(json, ClassWithBaseField.class);
+ assertNull(target.base);
+ }
+
+ public void testCustomDeserializerReturnsNullForArrayElements() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
+ public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ return null;
+ }
+ }).create();
+ String json = "[{baseName:'Base'},{baseName:'Base'}]";
+ Base[] target = gson.fromJson(json, Base[].class);
+ assertNull(target[0]);
+ assertNull(target[1]);
+ }
+
+ public void testCustomDeserializerReturnsNullForArrayElementsForArrayField() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
+ public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ return null;
+ }
+ }).create();
+ String json = "{bases:[{baseName:'Base'},{baseName:'Base'}]}";
+ ClassWithBaseArray target = gson.fromJson(json, ClassWithBaseArray.class);
+ assertNull(target.bases[0]);
+ assertNull(target.bases[1]);
+ }
+
+ private static class ClassWithBaseArray {
+ Base[] bases;
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/CustomSerializerTest.java b/gson/src/test/java/com/google/gson/functional/CustomSerializerTest.java
new file mode 100644
index 00000000..c8095463
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/CustomSerializerTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2009 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.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.common.TestTypes.Base;
+import com.google.gson.common.TestTypes.BaseSerializer;
+import com.google.gson.common.TestTypes.ClassWithBaseArrayField;
+import com.google.gson.common.TestTypes.ClassWithBaseField;
+import com.google.gson.common.TestTypes.Sub;
+import com.google.gson.common.TestTypes.SubSerializer;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Type;
+
+/**
+ * Functional Test exercising custom serialization only. When test applies to both
+ * serialization and deserialization then add it to CustomTypeAdapterTest.
+ *
+ * @author Inderjeet Singh
+ */
+public class CustomSerializerTest extends TestCase {
+
+ public void testBaseClassSerializerInvokedForBaseClassFields() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new BaseSerializer())
+ .registerTypeAdapter(Sub.class, new SubSerializer())
+ .create();
+ ClassWithBaseField target = new ClassWithBaseField(new Base());
+ JsonObject json = (JsonObject) gson.toJsonTree(target);
+ JsonObject base = json.get("base").getAsJsonObject();
+ assertEquals(BaseSerializer.NAME, base.get(Base.SERIALIZER_KEY).getAsString());
+ }
+
+ public void testSubClassSerializerInvokedForBaseClassFieldsHoldingSubClassInstances() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new BaseSerializer())
+ .registerTypeAdapter(Sub.class, new SubSerializer())
+ .create();
+ ClassWithBaseField target = new ClassWithBaseField(new Sub());
+ JsonObject json = (JsonObject) gson.toJsonTree(target);
+ JsonObject base = json.get("base").getAsJsonObject();
+ assertEquals(SubSerializer.NAME, base.get(Base.SERIALIZER_KEY).getAsString());
+ }
+
+ public void testSubClassSerializerInvokedForBaseClassFieldsHoldingArrayOfSubClassInstances() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new BaseSerializer())
+ .registerTypeAdapter(Sub.class, new SubSerializer())
+ .create();
+ ClassWithBaseArrayField target = new ClassWithBaseArrayField(new Base[] {new Sub(), new Sub()});
+ JsonObject json = (JsonObject) gson.toJsonTree(target);
+ JsonArray array = json.get("base").getAsJsonArray();
+ for (JsonElement element : array) {
+ JsonElement serializerKey = element.getAsJsonObject().get(Base.SERIALIZER_KEY);
+ assertEquals(SubSerializer.NAME, serializerKey.getAsString());
+ }
+ }
+
+ public void testBaseClassSerializerInvokedForBaseClassFieldsHoldingSubClassInstances() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new BaseSerializer())
+ .create();
+ ClassWithBaseField target = new ClassWithBaseField(new Sub());
+ JsonObject json = (JsonObject) gson.toJsonTree(target);
+ JsonObject base = json.get("base").getAsJsonObject();
+ assertEquals(BaseSerializer.NAME, base.get(Base.SERIALIZER_KEY).getAsString());
+ }
+
+ public void testSerializerReturnsNull() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new JsonSerializer<Base>() {
+ public JsonElement serialize(Base src, Type typeOfSrc, JsonSerializationContext context) {
+ return null;
+ }
+ })
+ .create();
+ JsonElement json = gson.toJsonTree(new Base());
+ assertTrue(json.isJsonNull());
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java
new file mode 100644
index 00000000..93ec7858
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java
@@ -0,0 +1,456 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.InstanceCreator;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.common.TestTypes.ClassWithCustomTypeConverter;
+import com.google.gson.reflect.TypeToken;
+
+import java.util.Date;
+import junit.framework.TestCase;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Functional tests for the support of custom serializer and deserializers.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class CustomTypeAdaptersTest extends TestCase {
+ private GsonBuilder builder;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ builder = new GsonBuilder();
+ }
+
+ public void testCustomSerializers() {
+ Gson gson = builder.registerTypeAdapter(
+ ClassWithCustomTypeConverter.class, new JsonSerializer<ClassWithCustomTypeConverter>() {
+ public JsonElement serialize(ClassWithCustomTypeConverter src, Type typeOfSrc,
+ JsonSerializationContext context) {
+ JsonObject json = new JsonObject();
+ json.addProperty("bag", 5);
+ json.addProperty("value", 25);
+ return json;
+ }
+ }).create();
+ ClassWithCustomTypeConverter target = new ClassWithCustomTypeConverter();
+ assertEquals("{\"bag\":5,\"value\":25}", gson.toJson(target));
+ }
+
+ public void testCustomDeserializers() {
+ Gson gson = new GsonBuilder().registerTypeAdapter(
+ ClassWithCustomTypeConverter.class, new JsonDeserializer<ClassWithCustomTypeConverter>() {
+ public ClassWithCustomTypeConverter deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context) {
+ JsonObject jsonObject = json.getAsJsonObject();
+ int value = jsonObject.get("bag").getAsInt();
+ return new ClassWithCustomTypeConverter(new BagOfPrimitives(value,
+ value, false, ""), value);
+ }
+ }).create();
+ String json = "{\"bag\":5,\"value\":25}";
+ ClassWithCustomTypeConverter target = gson.fromJson(json, ClassWithCustomTypeConverter.class);
+ assertEquals(5, target.getBag().getIntValue());
+ }
+
+ public void disable_testCustomSerializersOfSelf() {
+ Gson gson = createGsonObjectWithFooTypeAdapter();
+ Gson basicGson = new Gson();
+ Foo newFooObject = new Foo(1, 2L);
+ String jsonFromCustomSerializer = gson.toJson(newFooObject);
+ String jsonFromGson = basicGson.toJson(newFooObject);
+
+ assertEquals(jsonFromGson, jsonFromCustomSerializer);
+ }
+
+ public void disable_testCustomDeserializersOfSelf() {
+ Gson gson = createGsonObjectWithFooTypeAdapter();
+ Gson basicGson = new Gson();
+ Foo expectedFoo = new Foo(1, 2L);
+ String json = basicGson.toJson(expectedFoo);
+ Foo newFooObject = gson.fromJson(json, Foo.class);
+
+ assertEquals(expectedFoo.key, newFooObject.key);
+ assertEquals(expectedFoo.value, newFooObject.value);
+ }
+
+ public void testCustomNestedSerializers() {
+ Gson gson = new GsonBuilder().registerTypeAdapter(
+ BagOfPrimitives.class, new JsonSerializer<BagOfPrimitives>() {
+ public JsonElement serialize(BagOfPrimitives src, Type typeOfSrc,
+ JsonSerializationContext context) {
+ return new JsonPrimitive(6);
+ }
+ }).create();
+ ClassWithCustomTypeConverter target = new ClassWithCustomTypeConverter();
+ assertEquals("{\"bag\":6,\"value\":10}", gson.toJson(target));
+ }
+
+ public void testCustomNestedDeserializers() {
+ Gson gson = new GsonBuilder().registerTypeAdapter(
+ BagOfPrimitives.class, new JsonDeserializer<BagOfPrimitives>() {
+ public BagOfPrimitives deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context) throws JsonParseException {
+ int value = json.getAsInt();
+ return new BagOfPrimitives(value, value, false, "");
+ }
+ }).create();
+ String json = "{\"bag\":7,\"value\":25}";
+ ClassWithCustomTypeConverter target = gson.fromJson(json, ClassWithCustomTypeConverter.class);
+ assertEquals(7, target.getBag().getIntValue());
+ }
+
+ public void testCustomTypeAdapterDoesNotAppliesToSubClasses() {
+ Gson gson = new GsonBuilder().registerTypeAdapter(Base.class, new JsonSerializer<Base> () {
+ public JsonElement serialize(Base src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject json = new JsonObject();
+ json.addProperty("value", src.baseValue);
+ return json;
+ }
+ }).create();
+ Base b = new Base();
+ String json = gson.toJson(b);
+ assertTrue(json.contains("value"));
+ b = new Derived();
+ json = gson.toJson(b);
+ assertTrue(json.contains("derivedValue"));
+ }
+
+ public void testCustomTypeAdapterAppliesToSubClassesSerializedAsBaseClass() {
+ Gson gson = new GsonBuilder().registerTypeAdapter(Base.class, new JsonSerializer<Base> () {
+ public JsonElement serialize(Base src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject json = new JsonObject();
+ json.addProperty("value", src.baseValue);
+ return json;
+ }
+ }).create();
+ Base b = new Base();
+ String json = gson.toJson(b);
+ assertTrue(json.contains("value"));
+ b = new Derived();
+ json = gson.toJson(b, Base.class);
+ assertTrue(json.contains("value"));
+ assertFalse(json.contains("derivedValue"));
+ }
+
+ private static class Base {
+ int baseValue = 2;
+ }
+
+ private static class Derived extends Base {
+ @SuppressWarnings("unused")
+ int derivedValue = 3;
+ }
+
+
+ private Gson createGsonObjectWithFooTypeAdapter() {
+ return new GsonBuilder().registerTypeAdapter(Foo.class, new FooTypeAdapter()).create();
+ }
+
+ public static class Foo {
+ private final int key;
+ private final long value;
+
+ public Foo() {
+ this(0, 0L);
+ }
+
+ public Foo(int key, long value) {
+ this.key = key;
+ this.value = value;
+ }
+ }
+
+ public static class FooTypeAdapter implements JsonSerializer<Foo>, JsonDeserializer<Foo> {
+ public Foo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ return context.deserialize(json, typeOfT);
+ }
+
+ public JsonElement serialize(Foo src, Type typeOfSrc, JsonSerializationContext context) {
+ return context.serialize(src, typeOfSrc);
+ }
+ }
+
+ public void testCustomSerializerInvokedForPrimitives() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(boolean.class, new JsonSerializer<Boolean>() {
+ public JsonElement serialize(Boolean s, Type t, JsonSerializationContext c) {
+ return new JsonPrimitive(s ? 1 : 0);
+ }
+ })
+ .create();
+ assertEquals("1", gson.toJson(true, boolean.class));
+ assertEquals("true", gson.toJson(true, Boolean.class));
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void testCustomDeserializerInvokedForPrimitives() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(boolean.class, new JsonDeserializer() {
+ public Object deserialize(JsonElement json, Type t, JsonDeserializationContext context) {
+ return json.getAsInt() != 0;
+ }
+ })
+ .create();
+ assertEquals(Boolean.TRUE, gson.fromJson("1", boolean.class));
+ assertEquals(Boolean.TRUE, gson.fromJson("true", Boolean.class));
+ }
+
+ public void testCustomByteArraySerializer() {
+ Gson gson = new GsonBuilder().registerTypeAdapter(byte[].class, new JsonSerializer<byte[]>() {
+ public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
+ StringBuilder sb = new StringBuilder(src.length);
+ for (byte b : src) {
+ sb.append(b);
+ }
+ return new JsonPrimitive(sb.toString());
+ }
+ }).create();
+ byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ String json = gson.toJson(data);
+ assertEquals("\"0123456789\"", json);
+ }
+
+ public void testCustomByteArrayDeserializerAndInstanceCreator() {
+ GsonBuilder gsonBuilder = new GsonBuilder().registerTypeAdapter(byte[].class,
+ new JsonDeserializer<byte[]>() {
+ public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ String str = json.getAsString();
+ byte[] data = new byte[str.length()];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = Byte.parseByte(""+str.charAt(i));
+ }
+ return data;
+ }
+ });
+ Gson gson = gsonBuilder.create();
+ 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) {
+ assertEquals(expected[i], actual[i]);
+ }
+ }
+
+ private static class StringHolder {
+ String part1;
+ String part2;
+
+ public StringHolder(String string) {
+ String[] parts = string.split(":");
+ part1 = parts[0];
+ part2 = parts[1];
+ }
+ public StringHolder(String part1, String part2) {
+ this.part1 = part1;
+ this.part2 = part2;
+ }
+ }
+
+ private static class StringHolderTypeAdapter implements JsonSerializer<StringHolder>,
+ JsonDeserializer<StringHolder>, InstanceCreator<StringHolder> {
+
+ public StringHolder createInstance(Type type) {
+ //Fill up with objects that will be thrown away
+ return new StringHolder("unknown:thing");
+ }
+
+ public StringHolder deserialize(JsonElement src, Type type,
+ JsonDeserializationContext context) {
+ return new StringHolder(src.getAsString());
+ }
+
+ public JsonElement serialize(StringHolder src, Type typeOfSrc,
+ JsonSerializationContext context) {
+ String contents = src.part1 + ':' + src.part2;
+ return new JsonPrimitive(contents);
+ }
+ }
+
+ // Test created from Issue 70
+ public void testCustomAdapterInvokedForCollectionElementSerializationWithType() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter())
+ .create();
+ Type setType = new TypeToken<Set<StringHolder>>() {}.getType();
+ StringHolder holder = new StringHolder("Jacob", "Tomaw");
+ Set<StringHolder> setOfHolders = new HashSet<StringHolder>();
+ setOfHolders.add(holder);
+ String json = gson.toJson(setOfHolders, setType);
+ assertTrue(json.contains("Jacob:Tomaw"));
+ }
+
+ // Test created from Issue 70
+ public void testCustomAdapterInvokedForCollectionElementSerialization() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter())
+ .create();
+ StringHolder holder = new StringHolder("Jacob", "Tomaw");
+ Set<StringHolder> setOfHolders = new HashSet<StringHolder>();
+ setOfHolders.add(holder);
+ String json = gson.toJson(setOfHolders);
+ assertTrue(json.contains("Jacob:Tomaw"));
+ }
+
+ // Test created from Issue 70
+ public void testCustomAdapterInvokedForCollectionElementDeserialization() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter())
+ .create();
+ Type setType = new TypeToken<Set<StringHolder>>() {}.getType();
+ Set<StringHolder> setOfHolders = gson.fromJson("['Jacob:Tomaw']", setType);
+ assertEquals(1, setOfHolders.size());
+ StringHolder foo = setOfHolders.iterator().next();
+ assertEquals("Jacob", foo.part1);
+ assertEquals("Tomaw", foo.part2);
+ }
+
+ // Test created from Issue 70
+ public void testCustomAdapterInvokedForMapElementSerializationWithType() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter())
+ .create();
+ Type mapType = new TypeToken<Map<String,StringHolder>>() {}.getType();
+ StringHolder holder = new StringHolder("Jacob", "Tomaw");
+ Map<String, StringHolder> mapOfHolders = new HashMap<String, StringHolder>();
+ mapOfHolders.put("foo", holder);
+ String json = gson.toJson(mapOfHolders, mapType);
+ assertTrue(json.contains("\"foo\":\"Jacob:Tomaw\""));
+ }
+
+ // Test created from Issue 70
+ public void testCustomAdapterInvokedForMapElementSerialization() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter())
+ .create();
+ StringHolder holder = new StringHolder("Jacob", "Tomaw");
+ Map<String, StringHolder> mapOfHolders = new HashMap<String, StringHolder>();
+ mapOfHolders.put("foo", holder);
+ String json = gson.toJson(mapOfHolders);
+ assertTrue(json.contains("\"foo\":\"Jacob:Tomaw\""));
+ }
+
+ // Test created from Issue 70
+ public void testCustomAdapterInvokedForMapElementDeserialization() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(StringHolder.class, new StringHolderTypeAdapter())
+ .create();
+ Type mapType = new TypeToken<Map<String, StringHolder>>() {}.getType();
+ Map<String, StringHolder> mapOfFoo = gson.fromJson("{'foo':'Jacob:Tomaw'}", mapType);
+ assertEquals(1, mapOfFoo.size());
+ StringHolder foo = mapOfFoo.get("foo");
+ assertEquals("Jacob", foo.part1);
+ assertEquals("Tomaw", foo.part2);
+ }
+
+ public void testEnsureCustomSerializerNotInvokedForNullValues() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(DataHolder.class, new DataHolderSerializer())
+ .create();
+ DataHolderWrapper target = new DataHolderWrapper(new DataHolder("abc"));
+ String json = gson.toJson(target);
+ assertEquals("{\"wrappedData\":{\"myData\":\"abc\"}}", json);
+ }
+
+ public void testEnsureCustomDeserializerNotInvokedForNullValues() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(DataHolder.class, new DataHolderDeserializer())
+ .create();
+ String json = "{wrappedData:null}";
+ DataHolderWrapper actual = gson.fromJson(json, DataHolderWrapper.class);
+ assertNull(actual.wrappedData);
+ }
+
+ // Test created from Issue 352
+ public void testRegisterHierarchyAdapterForDate() {
+ Gson gson = new GsonBuilder()
+ .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));
+ assertEquals(new java.sql.Date(0), gson.fromJson("0", java.sql.Date.class));
+ }
+
+ private static class DataHolder {
+ final String data;
+
+ public DataHolder(String data) {
+ this.data = data;
+ }
+ }
+
+ private static class DataHolderWrapper {
+ final DataHolder wrappedData;
+
+ public DataHolderWrapper(DataHolder data) {
+ this.wrappedData = data;
+ }
+ }
+
+ private static class DataHolderSerializer implements JsonSerializer<DataHolder> {
+ public JsonElement serialize(DataHolder src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject obj = new JsonObject();
+ obj.addProperty("myData", src.data);
+ return obj;
+ }
+ }
+
+ private static class DataHolderDeserializer implements JsonDeserializer<DataHolder> {
+ public DataHolder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ JsonObject jsonObj = json.getAsJsonObject();
+ JsonElement jsonElement = jsonObj.get("data");
+ if (jsonElement == null || jsonElement.isJsonNull()) {
+ return new DataHolder(null);
+ }
+ return new DataHolder(jsonElement.getAsString());
+ }
+ }
+
+ private static class DateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
+ public Date deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context) throws JsonParseException {
+ return typeOfT == Date.class
+ ? new Date(json.getAsLong())
+ : new java.sql.Date(json.getAsLong());
+ }
+ public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive(src.getTime());
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java
new file mode 100644
index 00000000..2b4db893
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java
@@ -0,0 +1,724 @@
+/*
+ * 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 java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URL;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import junit.framework.TestCase;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.TypeAdapter;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Functional test for Json serialization and deserialization for common classes for which default
+ * support is provided in Gson. The tests for Map types are available in {@link MapTest}.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class DefaultTypeAdaptersTest extends TestCase {
+ private Gson gson;
+ private TimeZone oldTimeZone;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ this.oldTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
+ Locale.setDefault(Locale.US);
+ gson = new Gson();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ TimeZone.setDefault(oldTimeZone);
+ super.tearDown();
+ }
+
+ public void testClassSerialization() {
+ try {
+ gson.toJson(String.class);
+ } catch (UnsupportedOperationException expected) {}
+ // Override with a custom type adapter for class.
+ gson = new GsonBuilder().registerTypeAdapter(Class.class, new MyClassTypeAdapter()).create();
+ assertEquals("\"java.lang.String\"", gson.toJson(String.class));
+ }
+
+ public void testClassDeserialization() {
+ try {
+ gson.fromJson("String.class", String.class.getClass());
+ } catch (UnsupportedOperationException expected) {}
+ // Override with a custom type adapter for class.
+ gson = new GsonBuilder().registerTypeAdapter(Class.class, new MyClassTypeAdapter()).create();
+ assertEquals(String.class, gson.fromJson("java.lang.String", Class.class));
+ }
+
+ public void testUrlSerialization() throws Exception {
+ String urlValue = "http://google.com/";
+ URL url = new URL(urlValue);
+ assertEquals("\"http://google.com/\"", gson.toJson(url));
+ }
+
+ public void testUrlDeserialization() {
+ String urlValue = "http://google.com/";
+ String json = "'http:\\/\\/google.com\\/'";
+ URL target = gson.fromJson(json, URL.class);
+ assertEquals(urlValue, target.toExternalForm());
+
+ gson.fromJson('"' + urlValue + '"', URL.class);
+ assertEquals(urlValue, target.toExternalForm());
+ }
+
+ public void testUrlNullSerialization() throws Exception {
+ ClassWithUrlField target = new ClassWithUrlField();
+ assertEquals("{}", gson.toJson(target));
+ }
+
+ public void testUrlNullDeserialization() {
+ String json = "{}";
+ ClassWithUrlField target = gson.fromJson(json, ClassWithUrlField.class);
+ assertNull(target.url);
+ }
+
+ private static class ClassWithUrlField {
+ URL url;
+ }
+
+ public void testUriSerialization() throws Exception {
+ String uriValue = "http://google.com/";
+ URI uri = new URI(uriValue);
+ assertEquals("\"http://google.com/\"", gson.toJson(uri));
+ }
+
+ public void testUriDeserialization() {
+ String uriValue = "http://google.com/";
+ String json = '"' + uriValue + '"';
+ URI target = gson.fromJson(json, URI.class);
+ assertEquals(uriValue, target.toASCIIString());
+ }
+
+ public void testNullSerialization() throws Exception {
+ testNullSerializationAndDeserialization(Boolean.class);
+ testNullSerializationAndDeserialization(Byte.class);
+ testNullSerializationAndDeserialization(Short.class);
+ testNullSerializationAndDeserialization(Integer.class);
+ testNullSerializationAndDeserialization(Long.class);
+ testNullSerializationAndDeserialization(Double.class);
+ testNullSerializationAndDeserialization(Float.class);
+ testNullSerializationAndDeserialization(Number.class);
+ testNullSerializationAndDeserialization(Character.class);
+ testNullSerializationAndDeserialization(String.class);
+ testNullSerializationAndDeserialization(StringBuilder.class);
+ testNullSerializationAndDeserialization(StringBuffer.class);
+ testNullSerializationAndDeserialization(BigDecimal.class);
+ testNullSerializationAndDeserialization(BigInteger.class);
+ testNullSerializationAndDeserialization(TreeSet.class);
+ testNullSerializationAndDeserialization(ArrayList.class);
+ testNullSerializationAndDeserialization(HashSet.class);
+ testNullSerializationAndDeserialization(Properties.class);
+ testNullSerializationAndDeserialization(URL.class);
+ testNullSerializationAndDeserialization(URI.class);
+ testNullSerializationAndDeserialization(UUID.class);
+ testNullSerializationAndDeserialization(Locale.class);
+ testNullSerializationAndDeserialization(InetAddress.class);
+ testNullSerializationAndDeserialization(BitSet.class);
+ testNullSerializationAndDeserialization(Date.class);
+ testNullSerializationAndDeserialization(GregorianCalendar.class);
+ testNullSerializationAndDeserialization(Calendar.class);
+ testNullSerializationAndDeserialization(Time.class);
+ testNullSerializationAndDeserialization(Timestamp.class);
+ testNullSerializationAndDeserialization(java.sql.Date.class);
+ testNullSerializationAndDeserialization(Enum.class);
+ testNullSerializationAndDeserialization(Class.class);
+ }
+
+ private void testNullSerializationAndDeserialization(Class<?> c) {
+ assertEquals("null", gson.toJson(null, c));
+ assertEquals(null, gson.fromJson("null", c));
+ }
+
+ public void testUuidSerialization() throws Exception {
+ String uuidValue = "c237bec1-19ef-4858-a98e-521cf0aad4c0";
+ UUID uuid = UUID.fromString(uuidValue);
+ assertEquals('"' + uuidValue + '"', gson.toJson(uuid));
+ }
+
+ public void testUuidDeserialization() {
+ String uuidValue = "c237bec1-19ef-4858-a98e-521cf0aad4c0";
+ String json = '"' + uuidValue + '"';
+ UUID target = gson.fromJson(json, UUID.class);
+ assertEquals(uuidValue, target.toString());
+ }
+
+ public void testLocaleSerializationWithLanguage() {
+ Locale target = new Locale("en");
+ assertEquals("\"en\"", gson.toJson(target));
+ }
+
+ public void testLocaleDeserializationWithLanguage() {
+ String json = "\"en\"";
+ Locale locale = gson.fromJson(json, Locale.class);
+ assertEquals("en", locale.getLanguage());
+ }
+
+ public void testLocaleSerializationWithLanguageCountry() {
+ Locale target = Locale.CANADA_FRENCH;
+ assertEquals("\"fr_CA\"", gson.toJson(target));
+ }
+
+ public void testLocaleDeserializationWithLanguageCountry() {
+ String json = "\"fr_CA\"";
+ Locale locale = gson.fromJson(json, Locale.class);
+ assertEquals(Locale.CANADA_FRENCH, locale);
+ }
+
+ public void testLocaleSerializationWithLanguageCountryVariant() {
+ Locale target = new Locale("de", "DE", "EURO");
+ String json = gson.toJson(target);
+ assertEquals("\"de_DE_EURO\"", json);
+ }
+
+ public void testLocaleDeserializationWithLanguageCountryVariant() {
+ String json = "\"de_DE_EURO\"";
+ Locale locale = gson.fromJson(json, Locale.class);
+ assertEquals("de", locale.getLanguage());
+ assertEquals("DE", locale.getCountry());
+ assertEquals("EURO", locale.getVariant());
+ }
+
+ public void testBigDecimalFieldSerialization() {
+ ClassWithBigDecimal target = new ClassWithBigDecimal("-122.01e-21");
+ String json = gson.toJson(target);
+ String actual = json.substring(json.indexOf(':') + 1, json.indexOf('}'));
+ assertEquals(target.value, new BigDecimal(actual));
+ }
+
+ public void testBigDecimalFieldDeserialization() {
+ ClassWithBigDecimal expected = new ClassWithBigDecimal("-122.01e-21");
+ String json = expected.getExpectedJson();
+ ClassWithBigDecimal actual = gson.fromJson(json, ClassWithBigDecimal.class);
+ assertEquals(expected.value, actual.value);
+ }
+
+ public void testBadValueForBigDecimalDeserialization() {
+ try {
+ gson.fromJson("{\"value\"=1.5e-1.0031}", ClassWithBigDecimal.class);
+ fail("Exponent of a BigDecimal must be an integer value.");
+ } catch (JsonParseException expected) { }
+ }
+
+ public void testBigIntegerFieldSerialization() {
+ ClassWithBigInteger target = new ClassWithBigInteger("23232323215323234234324324324324324324");
+ String json = gson.toJson(target);
+ assertEquals(target.getExpectedJson(), json);
+ }
+
+ public void testBigIntegerFieldDeserialization() {
+ ClassWithBigInteger expected = new ClassWithBigInteger("879697697697697697697697697697697697");
+ String json = expected.getExpectedJson();
+ ClassWithBigInteger actual = gson.fromJson(json, ClassWithBigInteger.class);
+ assertEquals(expected.value, actual.value);
+ }
+
+ public void testOverrideBigIntegerTypeAdapter() throws Exception {
+ gson = new GsonBuilder()
+ .registerTypeAdapter(BigInteger.class, new NumberAsStringAdapter(BigInteger.class))
+ .create();
+ assertEquals("\"123\"", gson.toJson(new BigInteger("123"), BigInteger.class));
+ assertEquals(new BigInteger("123"), gson.fromJson("\"123\"", BigInteger.class));
+ }
+
+ public void testOverrideBigDecimalTypeAdapter() throws Exception {
+ gson = new GsonBuilder()
+ .registerTypeAdapter(BigDecimal.class, new NumberAsStringAdapter(BigDecimal.class))
+ .create();
+ assertEquals("\"1.1\"", gson.toJson(new BigDecimal("1.1"), BigDecimal.class));
+ assertEquals(new BigDecimal("1.1"), gson.fromJson("\"1.1\"", BigDecimal.class));
+ }
+
+ public void testSetSerialization() throws Exception {
+ Gson gson = new Gson();
+ HashSet<String> s = new HashSet<String>();
+ s.add("blah");
+ String json = gson.toJson(s);
+ assertEquals("[\"blah\"]", json);
+
+ json = gson.toJson(s, Set.class);
+ assertEquals("[\"blah\"]", json);
+ }
+
+ public void testBitSetSerialization() throws Exception {
+ Gson gson = new Gson();
+ BitSet bits = new BitSet();
+ bits.set(1);
+ bits.set(3, 6);
+ bits.set(9);
+ String json = gson.toJson(bits);
+ assertEquals("[0,1,0,1,1,1,0,0,0,1]", json);
+ }
+
+ public void testBitSetDeserialization() throws Exception {
+ BitSet expected = new BitSet();
+ expected.set(0);
+ expected.set(2, 6);
+ expected.set(8);
+
+ Gson gson = new Gson();
+ String json = gson.toJson(expected);
+ assertEquals(expected, gson.fromJson(json, BitSet.class));
+
+ json = "[1,0,1,1,1,1,0,0,1,0,0,0]";
+ assertEquals(expected, gson.fromJson(json, BitSet.class));
+
+ json = "[\"1\",\"0\",\"1\",\"1\",\"1\",\"1\",\"0\",\"0\",\"1\"]";
+ assertEquals(expected, gson.fromJson(json, BitSet.class));
+
+ json = "[true,false,true,true,true,true,false,false,true,false,false]";
+ assertEquals(expected, gson.fromJson(json, BitSet.class));
+ }
+
+ public void testDefaultDateSerialization() {
+ Date now = new Date(1315806903103L);
+ String json = gson.toJson(now);
+ assertEquals("\"Sep 11, 2011 10:55:03 PM\"", json);
+ }
+
+ public void testDefaultDateDeserialization() {
+ String json = "'Dec 13, 2009 07:18:02 AM'";
+ Date extracted = gson.fromJson(json, Date.class);
+ assertEqualsDate(extracted, 2009, 11, 13);
+ assertEqualsTime(extracted, 7, 18, 2);
+ }
+
+ // Date can not directly be compared with another instance since the deserialization loses the
+ // millisecond portion.
+ @SuppressWarnings("deprecation")
+ private void assertEqualsDate(Date date, int year, int month, int day) {
+ assertEquals(year-1900, date.getYear());
+ assertEquals(month, date.getMonth());
+ assertEquals(day, date.getDate());
+ }
+
+ @SuppressWarnings("deprecation")
+ private void assertEqualsTime(Date date, int hours, int minutes, int seconds) {
+ assertEquals(hours, date.getHours());
+ assertEquals(minutes, date.getMinutes());
+ assertEquals(seconds, date.getSeconds());
+ }
+
+ public void testDefaultJavaSqlDateSerialization() {
+ java.sql.Date instant = new java.sql.Date(1259875082000L);
+ String json = gson.toJson(instant);
+ assertEquals("\"Dec 3, 2009\"", json);
+ }
+
+ public void testDefaultJavaSqlDateDeserialization() {
+ String json = "'Dec 3, 2009'";
+ java.sql.Date extracted = gson.fromJson(json, java.sql.Date.class);
+ assertEqualsDate(extracted, 2009, 11, 3);
+ }
+
+ public void testDefaultJavaSqlTimestampSerialization() {
+ Timestamp now = new java.sql.Timestamp(1259875082000L);
+ String json = gson.toJson(now);
+ assertEquals("\"Dec 3, 2009 1:18:02 PM\"", json);
+ }
+
+ public void testDefaultJavaSqlTimestampDeserialization() {
+ String json = "'Dec 3, 2009 1:18:02 PM'";
+ Timestamp extracted = gson.fromJson(json, Timestamp.class);
+ assertEqualsDate(extracted, 2009, 11, 3);
+ assertEqualsTime(extracted, 13, 18, 2);
+ }
+
+ public void testDefaultJavaSqlTimeSerialization() {
+ Time now = new Time(1259875082000L);
+ String json = gson.toJson(now);
+ assertEquals("\"01:18:02 PM\"", json);
+ }
+
+ public void testDefaultJavaSqlTimeDeserialization() {
+ String json = "'1:18:02 PM'";
+ Time extracted = gson.fromJson(json, Time.class);
+ assertEqualsTime(extracted, 13, 18, 2);
+ }
+
+ public void testDefaultDateSerializationUsingBuilder() throws Exception {
+ Gson gson = new GsonBuilder().create();
+ Date now = new Date(1315806903103L);
+ String json = gson.toJson(now);
+ assertEquals("\"Sep 11, 2011 10:55:03 PM\"", json);
+ }
+
+ public void testDefaultDateDeserializationUsingBuilder() throws Exception {
+ Gson gson = new GsonBuilder().create();
+ Date now = new Date(1315806903103L);
+ String json = gson.toJson(now);
+ Date extracted = gson.fromJson(json, Date.class);
+ assertEquals(now.toString(), extracted.toString());
+ }
+
+ public void testDefaultCalendarSerialization() throws Exception {
+ Gson gson = new GsonBuilder().create();
+ String json = gson.toJson(Calendar.getInstance());
+ assertTrue(json.contains("year"));
+ assertTrue(json.contains("month"));
+ assertTrue(json.contains("dayOfMonth"));
+ assertTrue(json.contains("hourOfDay"));
+ assertTrue(json.contains("minute"));
+ assertTrue(json.contains("second"));
+ }
+
+ public void testDefaultCalendarDeserialization() throws Exception {
+ Gson gson = new GsonBuilder().create();
+ 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));
+ assertEquals(2, cal.get(Calendar.MONTH));
+ assertEquals(11, cal.get(Calendar.DAY_OF_MONTH));
+ assertEquals(14, cal.get(Calendar.HOUR_OF_DAY));
+ assertEquals(29, cal.get(Calendar.MINUTE));
+ assertEquals(23, cal.get(Calendar.SECOND));
+ }
+
+ public void testDefaultGregorianCalendarSerialization() throws Exception {
+ Gson gson = new GsonBuilder().create();
+ GregorianCalendar cal = new GregorianCalendar();
+ String json = gson.toJson(cal);
+ assertTrue(json.contains("year"));
+ assertTrue(json.contains("month"));
+ assertTrue(json.contains("dayOfMonth"));
+ assertTrue(json.contains("hourOfDay"));
+ assertTrue(json.contains("minute"));
+ assertTrue(json.contains("second"));
+ }
+
+ public void testDefaultGregorianCalendarDeserialization() throws Exception {
+ Gson gson = new GsonBuilder().create();
+ 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));
+ assertEquals(2, cal.get(Calendar.MONTH));
+ assertEquals(11, cal.get(Calendar.DAY_OF_MONTH));
+ assertEquals(14, cal.get(Calendar.HOUR_OF_DAY));
+ assertEquals(29, cal.get(Calendar.MINUTE));
+ assertEquals(23, cal.get(Calendar.SECOND));
+ }
+
+ public void testDateSerializationWithPattern() throws Exception {
+ String pattern = "yyyy-MM-dd";
+ Gson gson = new GsonBuilder().setDateFormat(DateFormat.FULL).setDateFormat(pattern).create();
+ Date now = new Date(1315806903103L);
+ String json = gson.toJson(now);
+ assertEquals("\"2011-09-11\"", json);
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testDateDeserializationWithPattern() throws Exception {
+ String pattern = "yyyy-MM-dd";
+ Gson gson = new GsonBuilder().setDateFormat(DateFormat.FULL).setDateFormat(pattern).create();
+ Date now = new Date(1315806903103L);
+ String json = gson.toJson(now);
+ Date extracted = gson.fromJson(json, Date.class);
+ assertEquals(now.getYear(), extracted.getYear());
+ assertEquals(now.getMonth(), extracted.getMonth());
+ assertEquals(now.getDay(), extracted.getDay());
+ }
+
+ public void testDateSerializationWithPatternNotOverridenByTypeAdapter() throws Exception {
+ String pattern = "yyyy-MM-dd";
+ Gson gson = new GsonBuilder()
+ .setDateFormat(pattern)
+ .registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
+ public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ return new Date(1315806903103L);
+ }
+ })
+ .create();
+
+ Date now = new Date(1315806903103L);
+ String json = gson.toJson(now);
+ assertEquals("\"2011-09-11\"", json);
+ }
+
+ // http://code.google.com/p/google-gson/issues/detail?id=230
+ public void testDateSerializationInCollection() throws Exception {
+ Type listOfDates = new TypeToken<List<Date>>() {}.getType();
+ TimeZone defaultTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ Locale defaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.US);
+ try {
+ Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
+ List<Date> dates = Arrays.asList(new Date(0));
+ String json = gson.toJson(dates, listOfDates);
+ assertEquals("[\"1970-01-01\"]", json);
+ assertEquals(0L, gson.<List<Date>>fromJson("[\"1970-01-01\"]", listOfDates).get(0).getTime());
+ } finally {
+ TimeZone.setDefault(defaultTimeZone);
+ Locale.setDefault(defaultLocale);
+ }
+ }
+
+ // http://code.google.com/p/google-gson/issues/detail?id=230
+ public void testTimestampSerialization() throws Exception {
+ TimeZone defaultTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ Locale defaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.US);
+ try {
+ Timestamp timestamp = new Timestamp(0L);
+ Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
+ String json = gson.toJson(timestamp, Timestamp.class);
+ assertEquals("\"1970-01-01\"", json);
+ assertEquals(0, gson.fromJson("\"1970-01-01\"", Timestamp.class).getTime());
+ } finally {
+ TimeZone.setDefault(defaultTimeZone);
+ Locale.setDefault(defaultLocale);
+ }
+ }
+
+ // http://code.google.com/p/google-gson/issues/detail?id=230
+ public void testSqlDateSerialization() throws Exception {
+ TimeZone defaultTimeZone = TimeZone.getDefault();
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ Locale defaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.US);
+ try {
+ java.sql.Date sqlDate = new java.sql.Date(0L);
+ Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
+ String json = gson.toJson(sqlDate, Timestamp.class);
+ assertEquals("\"1970-01-01\"", json);
+ assertEquals(0, gson.fromJson("\"1970-01-01\"", java.sql.Date.class).getTime());
+ } finally {
+ TimeZone.setDefault(defaultTimeZone);
+ Locale.setDefault(defaultLocale);
+ }
+ }
+
+ public void testJsonPrimitiveSerialization() {
+ assertEquals("5", gson.toJson(new JsonPrimitive(5), JsonElement.class));
+ assertEquals("true", gson.toJson(new JsonPrimitive(true), JsonElement.class));
+ assertEquals("\"foo\"", gson.toJson(new JsonPrimitive("foo"), JsonElement.class));
+ assertEquals("\"a\"", gson.toJson(new JsonPrimitive('a'), JsonElement.class));
+ }
+
+ public void testJsonPrimitiveDeserialization() {
+ assertEquals(new JsonPrimitive(5), gson.fromJson("5", JsonElement.class));
+ assertEquals(new JsonPrimitive(5), gson.fromJson("5", JsonPrimitive.class));
+ assertEquals(new JsonPrimitive(true), gson.fromJson("true", JsonElement.class));
+ assertEquals(new JsonPrimitive(true), gson.fromJson("true", JsonPrimitive.class));
+ assertEquals(new JsonPrimitive("foo"), gson.fromJson("\"foo\"", JsonElement.class));
+ assertEquals(new JsonPrimitive("foo"), gson.fromJson("\"foo\"", JsonPrimitive.class));
+ assertEquals(new JsonPrimitive('a'), gson.fromJson("\"a\"", JsonElement.class));
+ assertEquals(new JsonPrimitive('a'), gson.fromJson("\"a\"", JsonPrimitive.class));
+ }
+
+ public void testJsonNullSerialization() {
+ assertEquals("null", gson.toJson(JsonNull.INSTANCE, JsonElement.class));
+ assertEquals("null", gson.toJson(JsonNull.INSTANCE, JsonNull.class));
+ }
+
+ public void testNullJsonElementSerialization() {
+ assertEquals("null", gson.toJson(null, JsonElement.class));
+ assertEquals("null", gson.toJson(null, JsonNull.class));
+ }
+
+ public void testJsonArraySerialization() {
+ JsonArray array = new JsonArray();
+ array.add(new JsonPrimitive(1));
+ array.add(new JsonPrimitive(2));
+ array.add(new JsonPrimitive(3));
+ assertEquals("[1,2,3]", gson.toJson(array, JsonElement.class));
+ }
+
+ public void testJsonArrayDeserialization() {
+ JsonArray array = new JsonArray();
+ array.add(new JsonPrimitive(1));
+ array.add(new JsonPrimitive(2));
+ array.add(new JsonPrimitive(3));
+
+ String json = "[1,2,3]";
+ assertEquals(array, gson.fromJson(json, JsonElement.class));
+ assertEquals(array, gson.fromJson(json, JsonArray.class));
+ }
+
+ public void testJsonObjectSerialization() {
+ JsonObject object = new JsonObject();
+ object.add("foo", new JsonPrimitive(1));
+ object.add("bar", new JsonPrimitive(2));
+ assertEquals("{\"foo\":1,\"bar\":2}", gson.toJson(object, JsonElement.class));
+ }
+
+ public void testJsonObjectDeserialization() {
+ JsonObject object = new JsonObject();
+ object.add("foo", new JsonPrimitive(1));
+ object.add("bar", new JsonPrimitive(2));
+
+ String json = "{\"foo\":1,\"bar\":2}";
+ JsonElement actual = gson.fromJson(json, JsonElement.class);
+ assertEquals(object, actual);
+
+ JsonObject actualObj = gson.fromJson(json, JsonObject.class);
+ assertEquals(object, actualObj);
+ }
+
+ public void testJsonNullDeserialization() {
+ assertEquals(JsonNull.INSTANCE, gson.fromJson("null", JsonElement.class));
+ assertEquals(JsonNull.INSTANCE, gson.fromJson("null", JsonNull.class));
+ }
+
+ private static class ClassWithBigDecimal {
+ BigDecimal value;
+ ClassWithBigDecimal(String value) {
+ this.value = new BigDecimal(value);
+ }
+ String getExpectedJson() {
+ return "{\"value\":" + value.toEngineeringString() + "}";
+ }
+ }
+
+ private static class ClassWithBigInteger {
+ BigInteger value;
+ ClassWithBigInteger(String value) {
+ this.value = new BigInteger(value);
+ }
+ String getExpectedJson() {
+ return "{\"value\":" + value + "}";
+ }
+ }
+
+ public void testPropertiesSerialization() {
+ Properties props = new Properties();
+ props.setProperty("foo", "bar");
+ String json = gson.toJson(props);
+ String expected = "{\"foo\":\"bar\"}";
+ assertEquals(expected, json);
+ }
+
+ public void testPropertiesDeserialization() {
+ String json = "{foo:'bar'}";
+ Properties props = gson.fromJson(json, Properties.class);
+ assertEquals("bar", props.getProperty("foo"));
+ }
+
+ public void testTreeSetSerialization() {
+ TreeSet<String> treeSet = new TreeSet<String>();
+ treeSet.add("Value1");
+ String json = gson.toJson(treeSet);
+ assertEquals("[\"Value1\"]", json);
+ }
+
+ public void testTreeSetDeserialization() {
+ String json = "['Value1']";
+ Type type = new TypeToken<TreeSet<String>>() {}.getType();
+ TreeSet<String> treeSet = gson.fromJson(json, type);
+ assertTrue(treeSet.contains("Value1"));
+ }
+
+ public void testStringBuilderSerialization() {
+ StringBuilder sb = new StringBuilder("abc");
+ String json = gson.toJson(sb);
+ assertEquals("\"abc\"", json);
+ }
+
+ public void testStringBuilderDeserialization() {
+ StringBuilder sb = gson.fromJson("'abc'", StringBuilder.class);
+ assertEquals("abc", sb.toString());
+ }
+
+ public void testStringBufferSerialization() {
+ StringBuffer sb = new StringBuffer("abc");
+ String json = gson.toJson(sb);
+ assertEquals("\"abc\"", json);
+ }
+
+ public void testStringBufferDeserialization() {
+ StringBuffer sb = gson.fromJson("'abc'", StringBuffer.class);
+ assertEquals("abc", sb.toString());
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static class MyClassTypeAdapter extends TypeAdapter<Class> {
+ @Override
+ public void write(JsonWriter out, Class value) throws IOException {
+ out.value(value.getName());
+ }
+ @Override
+ public Class read(JsonReader in) throws IOException {
+ String className = in.nextString();
+ try {
+ return Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+
+ static class NumberAsStringAdapter extends TypeAdapter<Number> {
+ private final Constructor<? extends Number> constructor;
+ NumberAsStringAdapter(Class<? extends Number> type) throws Exception {
+ this.constructor = type.getConstructor(String.class);
+ }
+ @Override public void write(JsonWriter out, Number value) throws IOException {
+ out.value(value.toString());
+ }
+ @Override public Number read(JsonReader in) throws IOException {
+ try {
+ return constructor.newInstance(in.nextString());
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/DelegateTypeAdapterTest.java b/gson/src/test/java/com/google/gson/functional/DelegateTypeAdapterTest.java
new file mode 100644
index 00000000..885330d8
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/DelegateTypeAdapterTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 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 java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Functional tests for {@link Gson#getDelegateAdapter(TypeAdapterFactory, TypeToken)} method.
+ *
+ * @author Inderjeet Singh
+ */
+public class DelegateTypeAdapterTest extends TestCase {
+
+ private StatsTypeAdapterFactory stats;
+ private Gson gson;
+ protected void setUp() throws Exception {
+ super.setUp();
+ stats = new StatsTypeAdapterFactory();
+ gson = new GsonBuilder()
+ .registerTypeAdapterFactory(stats)
+ .create();
+ }
+
+ public void testDelegateInvoked() {
+ List<BagOfPrimitives> bags = new ArrayList<BagOfPrimitives>();
+ for (int i = 0; i < 10; ++i) {
+ bags.add(new BagOfPrimitives(i, i, i % 2 == 0, String.valueOf(i)));
+ }
+ String json = gson.toJson(bags);
+ bags = gson.fromJson(json, new TypeToken<List<BagOfPrimitives>>(){}.getType());
+ // 11: 1 list object, and 10 entries. stats invoked on all 5 fields
+ assertEquals(51, stats.numReads);
+ assertEquals(51, stats.numWrites);
+ }
+
+ public void testDelegateInvokedOnStrings() {
+ String[] bags = {"1", "2", "3", "4"};
+ String json = gson.toJson(bags);
+ bags = gson.fromJson(json, String[].class);
+ // 1 array object with 4 elements.
+ assertEquals(5, stats.numReads);
+ assertEquals(5, stats.numWrites);
+ }
+
+ private static class StatsTypeAdapterFactory implements TypeAdapterFactory {
+ public int numReads = 0;
+ public int numWrites = 0;
+
+ public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+ final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
+ return new TypeAdapter<T>() {
+ @Override
+ public void write(JsonWriter out, T value) throws IOException {
+ ++numWrites;
+ delegate.write(out, value);
+ }
+
+ @Override
+ public T read(JsonReader in) throws IOException {
+ ++numReads;
+ return delegate.read(in);
+ }
+ };
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/EnumTest.java b/gson/src/test/java/com/google/gson/functional/EnumTest.java
new file mode 100644
index 00000000..2c21526d
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/EnumTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.annotations.SerializedName;
+import com.google.gson.common.MoreAsserts;
+import com.google.gson.reflect.TypeToken;
+
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Set;
+
+import junit.framework.TestCase;
+/**
+ * Functional tests for Java 5.0 enums.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class EnumTest extends TestCase {
+
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testTopLevelEnumSerialization() throws Exception {
+ String result = gson.toJson(MyEnum.VALUE1);
+ assertEquals('"' + MyEnum.VALUE1.toString() + '"', result);
+ }
+
+ public void testTopLevelEnumDeserialization() throws Exception {
+ MyEnum result = gson.fromJson('"' + MyEnum.VALUE1.toString() + '"', MyEnum.class);
+ assertEquals(MyEnum.VALUE1, result);
+ }
+
+ public void testCollectionOfEnumsSerialization() {
+ Type type = new TypeToken<Collection<MyEnum>>() {}.getType();
+ Collection<MyEnum> target = new ArrayList<MyEnum>();
+ target.add(MyEnum.VALUE1);
+ target.add(MyEnum.VALUE2);
+ String expectedJson = "[\"VALUE1\",\"VALUE2\"]";
+ String actualJson = gson.toJson(target);
+ assertEquals(expectedJson, actualJson);
+ actualJson = gson.toJson(target, type);
+ assertEquals(expectedJson, actualJson);
+ }
+
+ public void testCollectionOfEnumsDeserialization() {
+ Type type = new TypeToken<Collection<MyEnum>>() {}.getType();
+ String json = "[\"VALUE1\",\"VALUE2\"]";
+ Collection<MyEnum> target = gson.fromJson(json, type);
+ MoreAsserts.assertContains(target, MyEnum.VALUE1);
+ MoreAsserts.assertContains(target, MyEnum.VALUE2);
+ }
+
+ public void testClassWithEnumFieldSerialization() throws Exception {
+ ClassWithEnumFields target = new ClassWithEnumFields();
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testClassWithEnumFieldDeserialization() throws Exception {
+ String json = "{value1:'VALUE1',value2:'VALUE2'}";
+ ClassWithEnumFields target = gson.fromJson(json, ClassWithEnumFields.class);
+ assertEquals(MyEnum.VALUE1,target.value1);
+ assertEquals(MyEnum.VALUE2,target.value2);
+ }
+
+ private static enum MyEnum {
+ VALUE1, VALUE2
+ }
+
+ private static class ClassWithEnumFields {
+ private final MyEnum value1 = MyEnum.VALUE1;
+ private final MyEnum value2 = MyEnum.VALUE2;
+ public String getExpectedJson() {
+ return "{\"value1\":\"" + value1 + "\",\"value2\":\"" + value2 + "\"}";
+ }
+ }
+
+ /**
+ * Test for issue 226.
+ */
+ public void testEnumSubclass() {
+ assertFalse(Roshambo.class == Roshambo.ROCK.getClass());
+ assertEquals("\"ROCK\"", gson.toJson(Roshambo.ROCK));
+ assertEquals("[\"ROCK\",\"PAPER\",\"SCISSORS\"]", gson.toJson(EnumSet.allOf(Roshambo.class)));
+ assertEquals(Roshambo.ROCK, gson.fromJson("\"ROCK\"", Roshambo.class));
+ assertEquals(EnumSet.allOf(Roshambo.class),
+ gson.fromJson("[\"ROCK\",\"PAPER\",\"SCISSORS\"]", new TypeToken<Set<Roshambo>>() {}.getType()));
+ }
+
+ public void testEnumSubclassWithRegisteredTypeAdapter() {
+ gson = new GsonBuilder()
+ .registerTypeHierarchyAdapter(Roshambo.class, new MyEnumTypeAdapter())
+ .create();
+ assertFalse(Roshambo.class == Roshambo.ROCK.getClass());
+ assertEquals("\"123ROCK\"", gson.toJson(Roshambo.ROCK));
+ assertEquals("[\"123ROCK\",\"123PAPER\",\"123SCISSORS\"]", gson.toJson(EnumSet.allOf(Roshambo.class)));
+ assertEquals(Roshambo.ROCK, gson.fromJson("\"123ROCK\"", Roshambo.class));
+ assertEquals(EnumSet.allOf(Roshambo.class),
+ gson.fromJson("[\"123ROCK\",\"123PAPER\",\"123SCISSORS\"]", new TypeToken<Set<Roshambo>>() {}.getType()));
+ }
+
+ public void testEnumSubclassAsParameterizedType() {
+ Collection<Roshambo> list = new ArrayList<Roshambo>();
+ list.add(Roshambo.ROCK);
+ list.add(Roshambo.PAPER);
+
+ String json = gson.toJson(list);
+ assertEquals("[\"ROCK\",\"PAPER\"]", json);
+
+ Type collectionType = new TypeToken<Collection<Roshambo>>() {}.getType();
+ Collection<Roshambo> actualJsonList = gson.fromJson(json, collectionType);
+ MoreAsserts.assertContains(actualJsonList, Roshambo.ROCK);
+ MoreAsserts.assertContains(actualJsonList, Roshambo.PAPER);
+ }
+
+ public void testEnumCaseMapping() {
+ assertEquals(Gender.MALE, gson.fromJson("\"boy\"", Gender.class));
+ assertEquals("\"boy\"", gson.toJson(Gender.MALE, Gender.class));
+ }
+
+ public void testEnumSet() {
+ EnumSet<Roshambo> foo = EnumSet.of(Roshambo.ROCK, Roshambo.PAPER);
+ String json = gson.toJson(foo);
+ Type type = new TypeToken<EnumSet<Roshambo>>() {}.getType();
+ EnumSet<Roshambo> bar = gson.fromJson(json, type);
+ assertTrue(bar.contains(Roshambo.ROCK));
+ assertTrue(bar.contains(Roshambo.PAPER));
+ assertFalse(bar.contains(Roshambo.SCISSORS));
+ }
+
+ public enum Roshambo {
+ ROCK {
+ @Override Roshambo defeats() {
+ return SCISSORS;
+ }
+ },
+ PAPER {
+ @Override Roshambo defeats() {
+ return ROCK;
+ }
+ },
+ SCISSORS {
+ @Override Roshambo defeats() {
+ return PAPER;
+ }
+ };
+
+ abstract Roshambo defeats();
+ }
+
+ private static class MyEnumTypeAdapter
+ implements JsonSerializer<Roshambo>, JsonDeserializer<Roshambo> {
+ public JsonElement serialize(Roshambo src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive("123" + src.name());
+ }
+
+ public Roshambo deserialize(JsonElement json, Type classOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ return Roshambo.valueOf(json.getAsString().substring(3));
+ }
+ }
+
+ public enum Gender {
+ @SerializedName("boy")
+ MALE,
+
+ @SerializedName("girl")
+ FEMALE
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/EscapingTest.java b/gson/src/test/java/com/google/gson/functional/EscapingTest.java
new file mode 100644
index 00000000..1581f451
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/EscapingTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import java.util.ArrayList;
+import java.util.List;
+import junit.framework.TestCase;
+
+/**
+ * Performs some functional test involving JSON output escaping.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class EscapingTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testEscapingQuotesInStringArray() throws Exception {
+ String[] valueWithQuotes = { "beforeQuote\"afterQuote" };
+ String jsonRepresentation = gson.toJson(valueWithQuotes);
+ String[] target = gson.fromJson(jsonRepresentation, String[].class);
+ assertEquals(1, target.length);
+ assertEquals(valueWithQuotes[0], target[0]);
+ }
+
+ public void testEscapeAllHtmlCharacters() {
+ List<String> strings = new ArrayList<String>();
+ strings.add("<");
+ strings.add(">");
+ strings.add("=");
+ strings.add("&");
+ strings.add("'");
+ strings.add("\"");
+ assertEquals("[\"\\u003c\",\"\\u003e\",\"\\u003d\",\"\\u0026\",\"\\u0027\",\"\\\"\"]",
+ gson.toJson(strings));
+ }
+
+ public void testEscapingObjectFields() throws Exception {
+ BagOfPrimitives objWithPrimitives = new BagOfPrimitives(1L, 1, true, "test with\" <script>");
+ String jsonRepresentation = gson.toJson(objWithPrimitives);
+ assertFalse(jsonRepresentation.contains("<"));
+ assertFalse(jsonRepresentation.contains(">"));
+ assertTrue(jsonRepresentation.contains("\\\""));
+
+ BagOfPrimitives expectedObject = gson.fromJson(jsonRepresentation, BagOfPrimitives.class);
+ assertEquals(objWithPrimitives.getExpectedJson(), expectedObject.getExpectedJson());
+ }
+
+ public void testGsonAcceptsEscapedAndNonEscapedJsonDeserialization() throws Exception {
+ Gson escapeHtmlGson = new GsonBuilder().create();
+ Gson noEscapeHtmlGson = new GsonBuilder().disableHtmlEscaping().create();
+
+ BagOfPrimitives target = new BagOfPrimitives(1L, 1, true, "test' / w'ith\" / \\ <script>");
+ String escapedJsonForm = escapeHtmlGson.toJson(target);
+ String nonEscapedJsonForm = noEscapeHtmlGson.toJson(target);
+ assertFalse(escapedJsonForm.equals(nonEscapedJsonForm));
+
+ assertEquals(target, noEscapeHtmlGson.fromJson(escapedJsonForm, BagOfPrimitives.class));
+ assertEquals(target, escapeHtmlGson.fromJson(nonEscapedJsonForm, BagOfPrimitives.class));
+ }
+
+ public void testGsonDoubleDeserialization() {
+ BagOfPrimitives expected = new BagOfPrimitives(3L, 4, true, "value1");
+ String json = gson.toJson(gson.toJson(expected));
+ String value = gson.fromJson(json, String.class);
+ BagOfPrimitives actual = gson.fromJson(value, BagOfPrimitives.class);
+ assertEquals(expected, actual);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java b/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java
new file mode 100644
index 00000000..baeab840
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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.ExclusionStrategy;
+import com.google.gson.FieldAttributes;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import junit.framework.TestCase;
+
+/**
+ * Performs some functional tests when Gson is instantiated with some common user defined
+ * {@link ExclusionStrategy} objects.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class ExclusionStrategyFunctionalTest extends TestCase {
+ private static final ExclusionStrategy EXCLUDE_SAMPLE_OBJECT_FOR_TEST = new ExclusionStrategy() {
+ public boolean shouldSkipField(FieldAttributes f) {
+ return false;
+ }
+ public boolean shouldSkipClass(Class<?> clazz) {
+ return clazz == SampleObjectForTest.class;
+ }
+ };
+
+ private SampleObjectForTest src;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ src = new SampleObjectForTest();
+ }
+
+ public void testExclusionStrategySerialization() throws Exception {
+ Gson gson = createGson(new MyExclusionStrategy(String.class), true);
+ String json = gson.toJson(src);
+ assertFalse(json.contains("\"stringField\""));
+ assertFalse(json.contains("\"annotatedField\""));
+ assertTrue(json.contains("\"longField\""));
+ }
+
+ public void testExclusionStrategySerializationDoesNotImpactDeserialization() {
+ String json = "{\"annotatedField\":1,\"stringField\":\"x\",\"longField\":2}";
+ Gson gson = createGson(new MyExclusionStrategy(String.class), true);
+ SampleObjectForTest value = gson.fromJson(json, SampleObjectForTest.class);
+ assertEquals(1, value.annotatedField);
+ assertEquals("x", value.stringField);
+ assertEquals(2, value.longField);
+ }
+
+ public void testExclusionStrategyDeserialization() throws Exception {
+ Gson gson = createGson(new MyExclusionStrategy(String.class), false);
+ JsonObject json = new JsonObject();
+ json.add("annotatedField", new JsonPrimitive(src.annotatedField + 5));
+ json.add("stringField", new JsonPrimitive(src.stringField + "blah,blah"));
+ json.add("longField", new JsonPrimitive(1212311L));
+
+ SampleObjectForTest target = gson.fromJson(json, SampleObjectForTest.class);
+ assertEquals(1212311L, target.longField);
+
+ // assert excluded fields are set to the defaults
+ assertEquals(src.annotatedField, target.annotatedField);
+ assertEquals(src.stringField, target.stringField);
+ }
+
+ public void testExclusionStrategySerializationDoesNotImpactSerialization() throws Exception {
+ Gson gson = createGson(new MyExclusionStrategy(String.class), false);
+ String json = gson.toJson(src);
+ assertTrue(json.contains("\"stringField\""));
+ assertTrue(json.contains("\"annotatedField\""));
+ assertTrue(json.contains("\"longField\""));
+ }
+
+ public void testExclusionStrategyWithMode() throws Exception {
+ SampleObjectForTest testObj = new SampleObjectForTest(
+ src.annotatedField + 5, src.stringField + "blah,blah",
+ src.longField + 655L);
+
+ Gson gson = createGson(new MyExclusionStrategy(String.class), false);
+ JsonObject json = gson.toJsonTree(testObj).getAsJsonObject();
+ assertEquals(testObj.annotatedField, json.get("annotatedField").getAsInt());
+ assertEquals(testObj.stringField, json.get("stringField").getAsString());
+ assertEquals(testObj.longField, json.get("longField").getAsLong());
+
+ SampleObjectForTest target = gson.fromJson(json, SampleObjectForTest.class);
+ assertEquals(testObj.longField, target.longField);
+
+ // assert excluded fields are set to the defaults
+ assertEquals(src.annotatedField, target.annotatedField);
+ assertEquals(src.stringField, target.stringField);
+ }
+
+ public void testExcludeTopLevelClassSerialization() {
+ Gson gson = new GsonBuilder()
+ .addSerializationExclusionStrategy(EXCLUDE_SAMPLE_OBJECT_FOR_TEST)
+ .create();
+ assertEquals("null", gson.toJson(new SampleObjectForTest(), SampleObjectForTest.class));
+ }
+
+ public void testExcludeTopLevelClassSerializationDoesNotImpactDeserialization() {
+ Gson gson = new GsonBuilder()
+ .addSerializationExclusionStrategy(EXCLUDE_SAMPLE_OBJECT_FOR_TEST)
+ .create();
+ String json = "{\"annotatedField\":1,\"stringField\":\"x\",\"longField\":2}";
+ SampleObjectForTest value = gson.fromJson(json, SampleObjectForTest.class);
+ assertEquals(1, value.annotatedField);
+ assertEquals("x", value.stringField);
+ assertEquals(2, value.longField);
+ }
+
+ public void testExcludeTopLevelClassDeserialization() {
+ Gson gson = new GsonBuilder()
+ .addDeserializationExclusionStrategy(EXCLUDE_SAMPLE_OBJECT_FOR_TEST)
+ .create();
+ String json = "{\"annotatedField\":1,\"stringField\":\"x\",\"longField\":2}";
+ SampleObjectForTest value = gson.fromJson(json, SampleObjectForTest.class);
+ assertNull(value);
+ }
+
+ public void testExcludeTopLevelClassDeserializationDoesNotImpactSerialization() {
+ Gson gson = new GsonBuilder()
+ .addDeserializationExclusionStrategy(EXCLUDE_SAMPLE_OBJECT_FOR_TEST)
+ .create();
+ String json = gson.toJson(new SampleObjectForTest(), SampleObjectForTest.class);
+ assertTrue(json.contains("\"stringField\""));
+ assertTrue(json.contains("\"annotatedField\""));
+ assertTrue(json.contains("\"longField\""));
+ }
+
+ private static Gson createGson(ExclusionStrategy exclusionStrategy, boolean serialization) {
+ GsonBuilder gsonBuilder = new GsonBuilder();
+ if (serialization) {
+ gsonBuilder.addSerializationExclusionStrategy(exclusionStrategy);
+ } else {
+ gsonBuilder.addDeserializationExclusionStrategy(exclusionStrategy);
+ }
+ return gsonBuilder
+ .serializeNulls()
+ .create();
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD})
+ private static @interface Foo {
+ // Field tag only annotation
+ }
+
+ private static class SampleObjectForTest {
+ @Foo
+ private final int annotatedField;
+ private final String stringField;
+ private final long longField;
+
+ public SampleObjectForTest() {
+ this(5, "someDefaultValue", 12345L);
+ }
+
+ public SampleObjectForTest(int annotatedField, String stringField, long longField) {
+ this.annotatedField = annotatedField;
+ this.stringField = stringField;
+ this.longField = longField;
+ }
+ }
+
+ private static class MyExclusionStrategy implements ExclusionStrategy {
+ private final Class<?> typeToSkip;
+
+ private MyExclusionStrategy(Class<?> typeToSkip) {
+ this.typeToSkip = typeToSkip;
+ }
+
+ public boolean shouldSkipClass(Class<?> clazz) {
+ return (clazz == typeToSkip);
+ }
+
+ public boolean shouldSkipField(FieldAttributes f) {
+ return f.getAnnotation(Foo.class) != null;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java b/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java
new file mode 100644
index 00000000..0ec5c433
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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 java.lang.reflect.Type;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.InstanceCreator;
+import com.google.gson.annotations.Expose;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for the regarding functional "@Expose" type tests.
+ *
+ * @author Joel Leitch
+ */
+public class ExposeFieldsTest extends TestCase {
+
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new GsonBuilder()
+ .excludeFieldsWithoutExposeAnnotation()
+ .registerTypeAdapter(SomeInterface.class, new SomeInterfaceInstanceCreator())
+ .create();
+ }
+
+ public void testNullExposeFieldSerialization() throws Exception {
+ ClassWithExposedFields object = new ClassWithExposedFields(null, 1);
+ String json = gson.toJson(object);
+
+ assertEquals(object.getExpectedJson(), json);
+ }
+
+ public void testArrayWithOneNullExposeFieldObjectSerialization() throws Exception {
+ ClassWithExposedFields object1 = new ClassWithExposedFields(1, 1);
+ ClassWithExposedFields object2 = new ClassWithExposedFields(null, 1);
+ ClassWithExposedFields object3 = new ClassWithExposedFields(2, 2);
+ ClassWithExposedFields[] objects = { object1, object2, object3 };
+
+ String json = gson.toJson(objects);
+ String expected = new StringBuilder()
+ .append('[').append(object1.getExpectedJson()).append(',')
+ .append(object2.getExpectedJson()).append(',')
+ .append(object3.getExpectedJson()).append(']')
+ .toString();
+
+ assertEquals(expected, json);
+ }
+
+ public void testExposeAnnotationSerialization() throws Exception {
+ ClassWithExposedFields target = new ClassWithExposedFields(1, 2);
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testExposeAnnotationDeserialization() throws Exception {
+ String json = "{a:3,b:4,d:20.0}";
+ ClassWithExposedFields target = gson.fromJson(json, ClassWithExposedFields.class);
+
+ assertEquals(3, (int) target.a);
+ assertNull(target.b);
+ assertFalse(target.d == 20);
+ }
+
+ public void testNoExposedFieldSerialization() throws Exception {
+ ClassWithNoExposedFields obj = new ClassWithNoExposedFields();
+ String json = gson.toJson(obj);
+
+ assertEquals("{}", json);
+ }
+
+ public void testNoExposedFieldDeserialization() throws Exception {
+ String json = "{a:4,b:5}";
+ ClassWithNoExposedFields obj = gson.fromJson(json, ClassWithNoExposedFields.class);
+
+ assertEquals(0, obj.a);
+ assertEquals(1, obj.b);
+ }
+
+ public void testExposedInterfaceFieldSerialization() throws Exception {
+ String expected = "{\"interfaceField\":{}}";
+ ClassWithInterfaceField target = new ClassWithInterfaceField(new SomeObject());
+ String actual = gson.toJson(target);
+
+ assertEquals(expected, actual);
+ }
+
+ public void testExposedInterfaceFieldDeserialization() throws Exception {
+ String json = "{\"interfaceField\":{}}";
+ ClassWithInterfaceField obj = gson.fromJson(json, ClassWithInterfaceField.class);
+
+ assertNotNull(obj.interfaceField);
+ }
+
+ private static class ClassWithExposedFields {
+ @Expose private final Integer a;
+ private final Integer b;
+ @Expose(serialize = false) final long c;
+ @Expose(deserialize = false) final double d;
+ @Expose(serialize = false, deserialize = false) final char e;
+
+ public ClassWithExposedFields(Integer a, Integer b) {
+ this(a, b, 1L, 2.0, 'a');
+ }
+ public ClassWithExposedFields(Integer a, Integer b, long c, double d, char e) {
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = d;
+ this.e = e;
+ }
+
+ public String getExpectedJson() {
+ StringBuilder sb = new StringBuilder("{");
+ if (a != null) {
+ sb.append("\"a\":").append(a).append(",");
+ }
+ sb.append("\"d\":").append(d);
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ private static class ClassWithNoExposedFields {
+ private final int a = 0;
+ private final int b = 1;
+ }
+
+ private static interface SomeInterface {
+ // Empty interface
+ }
+
+ private static class SomeObject implements SomeInterface {
+ // Do nothing
+ }
+
+ private static class SomeInterfaceInstanceCreator implements InstanceCreator<SomeInterface> {
+ public SomeInterface createInstance(Type type) {
+ return new SomeObject();
+ }
+ }
+
+ private static class ClassWithInterfaceField {
+ @Expose
+ private final SomeInterface interfaceField;
+
+ public ClassWithInterfaceField(SomeInterface interfaceField) {
+ this.interfaceField = interfaceField;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/FieldExclusionTest.java b/gson/src/test/java/com/google/gson/functional/FieldExclusionTest.java
new file mode 100644
index 00000000..080a8234
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/FieldExclusionTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.GsonBuilder;
+
+import junit.framework.TestCase;
+
+/**
+ * Performs some functional testing to ensure GSON infrastructure properly serializes/deserializes
+ * fields that either should or should not be included in the output based on the GSON
+ * configuration.
+ *
+ * @author Joel Leitch
+ */
+public class FieldExclusionTest extends TestCase {
+ private static final String VALUE = "blah_1234";
+
+ private Outer outer;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ outer = new Outer();
+ }
+
+ public void testDefaultInnerClassExclusion() throws Exception {
+ Gson gson = new Gson();
+ Outer.Inner target = outer.new Inner(VALUE);
+ String result = gson.toJson(target);
+ assertEquals(target.toJson(), result);
+
+ gson = new GsonBuilder().create();
+ target = outer.new Inner(VALUE);
+ result = gson.toJson(target);
+ assertEquals(target.toJson(), result);
+ }
+
+ public void testInnerClassExclusion() throws Exception {
+ Gson gson = new GsonBuilder().disableInnerClassSerialization().create();
+ Outer.Inner target = outer.new Inner(VALUE);
+ String result = gson.toJson(target);
+ assertEquals("null", result);
+ }
+
+ public void testDefaultNestedStaticClassIncluded() throws Exception {
+ Gson gson = new Gson();
+ Outer.Inner target = outer.new Inner(VALUE);
+ String result = gson.toJson(target);
+ assertEquals(target.toJson(), result);
+
+ gson = new GsonBuilder().create();
+ target = outer.new Inner(VALUE);
+ result = gson.toJson(target);
+ assertEquals(target.toJson(), result);
+ }
+
+ private static class Outer {
+ private class Inner extends NestedClass {
+ public Inner(String value) {
+ super(value);
+ }
+ }
+
+ }
+
+ private static class NestedClass {
+ private final String value;
+ public NestedClass(String value) {
+ this.value = value;
+ }
+
+ public String toJson() {
+ return "{\"value\":\"" + value + "\"}";
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/FieldNamingTest.java b/gson/src/test/java/com/google/gson/functional/FieldNamingTest.java
new file mode 100644
index 00000000..5d326af8
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/FieldNamingTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011 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 static com.google.gson.FieldNamingPolicy.IDENTITY;
+import static com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_DASHES;
+import static com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES;
+import static com.google.gson.FieldNamingPolicy.UPPER_CAMEL_CASE;
+import static com.google.gson.FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.annotations.SerializedName;
+import junit.framework.TestCase;
+
+public final class FieldNamingTest extends TestCase {
+ public void testIdentity() {
+ Gson gson = getGsonWithNamingPolicy(IDENTITY);
+ assertEquals("{'lowerCamel':1,'UpperCamel':2,'_lowerCamelLeadingUnderscore':3," +
+ "'_UpperCamelLeadingUnderscore':4,'lower_words':5,'UPPER_WORDS':6," +
+ "'annotatedName':7,'lowerId':8}",
+ gson.toJson(new TestNames()).replace('\"', '\''));
+ }
+
+ public void testUpperCamelCase() {
+ Gson gson = getGsonWithNamingPolicy(UPPER_CAMEL_CASE);
+ assertEquals("{'LowerCamel':1,'UpperCamel':2,'_LowerCamelLeadingUnderscore':3," +
+ "'_UpperCamelLeadingUnderscore':4,'Lower_words':5,'UPPER_WORDS':6," +
+ "'annotatedName':7,'LowerId':8}",
+ gson.toJson(new TestNames()).replace('\"', '\''));
+ }
+
+ public void testUpperCamelCaseWithSpaces() {
+ Gson gson = getGsonWithNamingPolicy(UPPER_CAMEL_CASE_WITH_SPACES);
+ assertEquals("{'Lower Camel':1,'Upper Camel':2,'_Lower Camel Leading Underscore':3," +
+ "'_ Upper Camel Leading Underscore':4,'Lower_words':5,'U P P E R_ W O R D S':6," +
+ "'annotatedName':7,'Lower Id':8}",
+ gson.toJson(new TestNames()).replace('\"', '\''));
+ }
+
+ public void testLowerCaseWithUnderscores() {
+ Gson gson = getGsonWithNamingPolicy(LOWER_CASE_WITH_UNDERSCORES);
+ assertEquals("{'lower_camel':1,'upper_camel':2,'_lower_camel_leading_underscore':3," +
+ "'__upper_camel_leading_underscore':4,'lower_words':5,'u_p_p_e_r__w_o_r_d_s':6," +
+ "'annotatedName':7,'lower_id':8}",
+ gson.toJson(new TestNames()).replace('\"', '\''));
+ }
+
+ public void testLowerCaseWithDashes() {
+ Gson gson = getGsonWithNamingPolicy(LOWER_CASE_WITH_DASHES);
+ assertEquals("{'lower-camel':1,'upper-camel':2,'_lower-camel-leading-underscore':3," +
+ "'_-upper-camel-leading-underscore':4,'lower_words':5,'u-p-p-e-r_-w-o-r-d-s':6," +
+ "'annotatedName':7,'lower-id':8}",
+ gson.toJson(new TestNames()).replace('\"', '\''));
+ }
+
+ private Gson getGsonWithNamingPolicy(FieldNamingPolicy fieldNamingPolicy){
+ return new GsonBuilder()
+ .setFieldNamingPolicy(fieldNamingPolicy)
+ .create();
+ }
+
+ @SuppressWarnings("unused") // fields are used reflectively
+ private static class TestNames {
+ int lowerCamel = 1;
+ int UpperCamel = 2;
+ int _lowerCamelLeadingUnderscore = 3;
+ int _UpperCamelLeadingUnderscore = 4;
+ int lower_words = 5;
+ int UPPER_WORDS = 6;
+ @SerializedName("annotatedName") int annotated = 7;
+ int lowerId = 8;
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/InheritanceTest.java b/gson/src/test/java/com/google/gson/functional/InheritanceTest.java
new file mode 100644
index 00000000..b93ba0b5
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/InheritanceTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2009 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.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.common.TestTypes.Base;
+import com.google.gson.common.TestTypes.ClassWithBaseArrayField;
+import com.google.gson.common.TestTypes.ClassWithBaseCollectionField;
+import com.google.gson.common.TestTypes.ClassWithBaseField;
+import com.google.gson.common.TestTypes.Nested;
+import com.google.gson.common.TestTypes.Sub;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Functional tests for Json serialization and deserialization of classes with
+ * inheritance hierarchies.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class InheritanceTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testSubClassSerialization() throws Exception {
+ SubTypeOfNested target = new SubTypeOfNested(new BagOfPrimitives(10, 20, false, "stringValue"),
+ new BagOfPrimitives(30, 40, true, "stringValue"));
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testSubClassDeserialization() throws Exception {
+ String json = "{\"value\":5,\"primitive1\":{\"longValue\":10,\"intValue\":20,"
+ + "\"booleanValue\":false,\"stringValue\":\"stringValue\"},\"primitive2\":"
+ + "{\"longValue\":30,\"intValue\":40,\"booleanValue\":true,"
+ + "\"stringValue\":\"stringValue\"}}";
+ SubTypeOfNested target = gson.fromJson(json, SubTypeOfNested.class);
+ assertEquals(json, target.getExpectedJson());
+ }
+
+ public void testClassWithBaseFieldSerialization() {
+ ClassWithBaseField sub = new ClassWithBaseField(new Sub());
+ JsonObject json = (JsonObject) gson.toJsonTree(sub);
+ JsonElement base = json.getAsJsonObject().get(ClassWithBaseField.FIELD_KEY);
+ assertEquals(Sub.SUB_NAME, base.getAsJsonObject().get(Sub.SUB_FIELD_KEY).getAsString());
+ }
+
+ public void testClassWithBaseArrayFieldSerialization() {
+ Base[] baseClasses = new Base[]{ new Sub(), new Sub()};
+ ClassWithBaseArrayField sub = new ClassWithBaseArrayField(baseClasses);
+ JsonObject json = gson.toJsonTree(sub).getAsJsonObject();
+ JsonArray bases = json.get(ClassWithBaseArrayField.FIELD_KEY).getAsJsonArray();
+ for (JsonElement element : bases) {
+ assertEquals(Sub.SUB_NAME, element.getAsJsonObject().get(Sub.SUB_FIELD_KEY).getAsString());
+ }
+ }
+
+ public void testClassWithBaseCollectionFieldSerialization() {
+ Collection<Base> baseClasses = new ArrayList<Base>();
+ baseClasses.add(new Sub());
+ baseClasses.add(new Sub());
+ ClassWithBaseCollectionField sub = new ClassWithBaseCollectionField(baseClasses);
+ JsonObject json = gson.toJsonTree(sub).getAsJsonObject();
+ JsonArray bases = json.get(ClassWithBaseArrayField.FIELD_KEY).getAsJsonArray();
+ for (JsonElement element : bases) {
+ assertEquals(Sub.SUB_NAME, element.getAsJsonObject().get(Sub.SUB_FIELD_KEY).getAsString());
+ }
+ }
+
+ public void testBaseSerializedAsSub() {
+ Base base = new Sub();
+ JsonObject json = gson.toJsonTree(base).getAsJsonObject();
+ assertEquals(Sub.SUB_NAME, json.get(Sub.SUB_FIELD_KEY).getAsString());
+ }
+
+ public void testBaseSerializedAsSubForToJsonMethod() {
+ Base base = new Sub();
+ String json = gson.toJson(base);
+ assertTrue(json.contains(Sub.SUB_NAME));
+ }
+
+ public void testBaseSerializedAsBaseWhenSpecifiedWithExplicitType() {
+ Base base = new Sub();
+ JsonObject json = gson.toJsonTree(base, Base.class).getAsJsonObject();
+ assertEquals(Base.BASE_NAME, json.get(Base.BASE_FIELD_KEY).getAsString());
+ assertNull(json.get(Sub.SUB_FIELD_KEY));
+ }
+
+ public void testBaseSerializedAsBaseWhenSpecifiedWithExplicitTypeForToJsonMethod() {
+ Base base = new Sub();
+ String json = gson.toJson(base, Base.class);
+ assertTrue(json.contains(Base.BASE_NAME));
+ assertFalse(json.contains(Sub.SUB_FIELD_KEY));
+ }
+
+ public void testBaseSerializedAsSubWhenSpecifiedWithExplicitType() {
+ Base base = new Sub();
+ JsonObject json = gson.toJsonTree(base, Sub.class).getAsJsonObject();
+ assertEquals(Sub.SUB_NAME, json.get(Sub.SUB_FIELD_KEY).getAsString());
+ }
+
+ public void testBaseSerializedAsSubWhenSpecifiedWithExplicitTypeForToJsonMethod() {
+ Base base = new Sub();
+ String json = gson.toJson(base, Sub.class);
+ assertTrue(json.contains(Sub.SUB_NAME));
+ }
+
+ private static class SubTypeOfNested extends Nested {
+ private final long value = 5;
+
+ public SubTypeOfNested(BagOfPrimitives primitive1, BagOfPrimitives primitive2) {
+ super(primitive1, primitive2);
+ }
+
+ @Override
+ public void appendFields(StringBuilder sb) {
+ sb.append("\"value\":").append(value).append(",");
+ super.appendFields(sb);
+ }
+ }
+
+ public void testSubInterfacesOfCollectionSerialization() throws Exception {
+ List<Integer> list = new LinkedList<Integer>();
+ list.add(0);
+ list.add(1);
+ list.add(2);
+ list.add(3);
+ Queue<Long> queue = new LinkedList<Long>();
+ queue.add(0L);
+ queue.add(1L);
+ queue.add(2L);
+ queue.add(3L);
+ Set<Float> set = new TreeSet<Float>();
+ set.add(0.1F);
+ set.add(0.2F);
+ set.add(0.3F);
+ set.add(0.4F);
+ SortedSet<Character> sortedSet = new TreeSet<Character>();
+ sortedSet.add('a');
+ sortedSet.add('b');
+ sortedSet.add('c');
+ sortedSet.add('d');
+ ClassWithSubInterfacesOfCollection target =
+ new ClassWithSubInterfacesOfCollection(list, queue, set, sortedSet);
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testSubInterfacesOfCollectionDeserialization() throws Exception {
+ String json = "{\"list\":[0,1,2,3],\"queue\":[0,1,2,3],\"set\":[0.1,0.2,0.3,0.4],"
+ + "\"sortedSet\":[\"a\",\"b\",\"c\",\"d\"]"
+ + "}";
+ ClassWithSubInterfacesOfCollection target =
+ gson.fromJson(json, ClassWithSubInterfacesOfCollection.class);
+ assertTrue(target.listContains(0, 1, 2, 3));
+ assertTrue(target.queueContains(0, 1, 2, 3));
+ assertTrue(target.setContains(0.1F, 0.2F, 0.3F, 0.4F));
+ assertTrue(target.sortedSetContains('a', 'b', 'c', 'd'));
+ }
+
+ private static class ClassWithSubInterfacesOfCollection {
+ private List<Integer> list;
+ private Queue<Long> queue;
+ private Set<Float> set;
+ private SortedSet<Character> sortedSet;
+
+ public ClassWithSubInterfacesOfCollection(List<Integer> list, Queue<Long> queue, Set<Float> set,
+ SortedSet<Character> sortedSet) {
+ this.list = list;
+ this.queue = queue;
+ this.set = set;
+ this.sortedSet = sortedSet;
+ }
+
+ boolean listContains(int... values) {
+ for (int value : values) {
+ if (!list.contains(value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean queueContains(long... values) {
+ for (long value : values) {
+ if (!queue.contains(value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean setContains(float... values) {
+ for (float value : values) {
+ if (!set.contains(value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean sortedSetContains(char... values) {
+ for (char value : values) {
+ if (!sortedSet.contains(value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public String getExpectedJson() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ sb.append("\"list\":");
+ append(sb, list).append(",");
+ sb.append("\"queue\":");
+ append(sb, queue).append(",");
+ sb.append("\"set\":");
+ append(sb, set).append(",");
+ sb.append("\"sortedSet\":");
+ append(sb, sortedSet);
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private StringBuilder append(StringBuilder sb, Collection<?> c) {
+ sb.append("[");
+ boolean first = true;
+ for (Object o : c) {
+ if (!first) {
+ sb.append(",");
+ } else {
+ first = false;
+ }
+ if (o instanceof String || o instanceof Character) {
+ sb.append('\"');
+ }
+ sb.append(o.toString());
+ if (o instanceof String || o instanceof Character) {
+ sb.append('\"');
+ }
+ }
+ sb.append("]");
+ return sb;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java b/gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java
new file mode 100644
index 00000000..0fda10af
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2009 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.GsonBuilder;
+import com.google.gson.InstanceCreator;
+import com.google.gson.common.TestTypes.Base;
+import com.google.gson.common.TestTypes.ClassWithBaseField;
+import com.google.gson.common.TestTypes.Sub;
+
+import com.google.gson.reflect.TypeToken;
+import java.util.ArrayList;
+import java.util.List;
+import junit.framework.TestCase;
+
+import java.lang.reflect.Type;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Functional Test exercising custom serialization only. When test applies to both
+ * serialization and deserialization then add it to CustomTypeAdapterTest.
+ *
+ * @author Inderjeet Singh
+ */
+public class InstanceCreatorTest extends TestCase {
+
+ public void testInstanceCreatorReturnsBaseType() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new InstanceCreator<Base>() {
+ public Base createInstance(Type type) {
+ return new Base();
+ }
+ })
+ .create();
+ String json = "{baseName:'BaseRevised',subName:'Sub'}";
+ Base base = gson.fromJson(json, Base.class);
+ assertEquals("BaseRevised", base.baseName);
+ }
+
+ public void testInstanceCreatorReturnsSubTypeForTopLevelObject() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new InstanceCreator<Base>() {
+ public Base createInstance(Type type) {
+ return new Sub();
+ }
+ })
+ .create();
+
+ String json = "{baseName:'Base',subName:'SubRevised'}";
+ Base base = gson.fromJson(json, Base.class);
+ assertTrue(base instanceof Sub);
+
+ Sub sub = (Sub) base;
+ assertFalse("SubRevised".equals(sub.subName));
+ assertEquals(Sub.SUB_NAME, sub.subName);
+ }
+
+ public void testInstanceCreatorReturnsSubTypeForField() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Base.class, new InstanceCreator<Base>() {
+ public Base createInstance(Type type) {
+ return new Sub();
+ }
+ })
+ .create();
+ String json = "{base:{baseName:'Base',subName:'SubRevised'}}";
+ ClassWithBaseField target = gson.fromJson(json, ClassWithBaseField.class);
+ assertTrue(target.base instanceof Sub);
+ assertEquals(Sub.SUB_NAME, ((Sub)target.base).subName);
+ }
+
+ // This regressed in Gson 2.0 and 2.1
+ public void testInstanceCreatorForCollectionType() {
+ @SuppressWarnings("serial")
+ class SubArrayList<T> extends ArrayList<T> {}
+ InstanceCreator<List<String>> listCreator = new InstanceCreator<List<String>>() {
+ public List<String> createInstance(Type type) {
+ return new SubArrayList<String>();
+ }
+ };
+ Type listOfStringType = new TypeToken<List<String>>() {}.getType();
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(listOfStringType, listCreator)
+ .create();
+ List<String> list = gson.fromJson("[\"a\"]", listOfStringType);
+ assertEquals(SubArrayList.class, list.getClass());
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void testInstanceCreatorForParametrizedType() throws Exception {
+ @SuppressWarnings("serial")
+ class SubTreeSet<T> extends TreeSet<T> {}
+ InstanceCreator<SortedSet> sortedSetCreator = new InstanceCreator<SortedSet>() {
+ public SortedSet createInstance(Type type) {
+ return new SubTreeSet();
+ }
+ };
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(SortedSet.class, sortedSetCreator)
+ .create();
+
+ Type sortedSetType = new TypeToken<SortedSet<String>>() {}.getType();
+ SortedSet<String> set = gson.fromJson("[\"a\"]", sortedSetType);
+ assertEquals(set.first(), "a");
+ assertEquals(SubTreeSet.class, set.getClass());
+
+ set = gson.fromJson("[\"b\"]", SortedSet.class);
+ assertEquals(set.first(), "b");
+ assertEquals(SubTreeSet.class, set.getClass());
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/InterfaceTest.java b/gson/src/test/java/com/google/gson/functional/InterfaceTest.java
new file mode 100644
index 00000000..6851f1e9
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/InterfaceTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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 junit.framework.TestCase;
+
+/**
+ * Functional tests involving interfaces.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class InterfaceTest extends TestCase {
+ private static final String OBJ_JSON = "{\"someStringValue\":\"StringValue\"}";
+
+ private Gson gson;
+ private TestObject obj;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ obj = new TestObject("StringValue");
+ }
+
+ public void testSerializingObjectImplementingInterface() throws Exception {
+ assertEquals(OBJ_JSON, gson.toJson(obj));
+ }
+
+ public void testSerializingInterfaceObjectField() throws Exception {
+ TestObjectWrapper objWrapper = new TestObjectWrapper(obj);
+ assertEquals("{\"obj\":" + OBJ_JSON + "}", gson.toJson(objWrapper));
+ }
+
+ private static interface TestObjectInterface {
+ // Holder
+ }
+
+ private static class TestObject implements TestObjectInterface {
+ @SuppressWarnings("unused")
+ private String someStringValue;
+
+ private TestObject(String value) {
+ this.someStringValue = value;
+ }
+ }
+
+ private static class TestObjectWrapper {
+ @SuppressWarnings("unused")
+ private TestObjectInterface obj;
+
+ private TestObjectWrapper(TestObjectInterface obj) {
+ this.obj = obj;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/InternationalizationTest.java b/gson/src/test/java/com/google/gson/functional/InternationalizationTest.java
new file mode 100644
index 00000000..169c37a5
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/InternationalizationTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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 junit.framework.TestCase;
+
+/**
+ * Functional tests for internationalized strings.
+ *
+ * @author Inderjeet Singh
+ */
+public class InternationalizationTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ /*
+ public void testStringsWithRawChineseCharactersSerialization() throws Exception {
+ String target = "好好好";
+ String json = gson.toJson(target);
+ String expected = "\"\\u597d\\u597d\\u597d\"";
+ assertEquals(expected, json);
+ }
+ */
+
+ public void testStringsWithRawChineseCharactersDeserialization() throws Exception {
+ String expected = "好好好";
+ String json = "\"" + expected + "\"";
+ String actual = gson.fromJson(json, String.class);
+ assertEquals(expected, actual);
+ }
+
+ public void testStringsWithUnicodeChineseCharactersSerialization() throws Exception {
+ String target = "\u597d\u597d\u597d";
+ String json = gson.toJson(target);
+ String expected = "\"\u597d\u597d\u597d\"";
+ assertEquals(expected, json);
+ }
+
+ public void testStringsWithUnicodeChineseCharactersDeserialization() throws Exception {
+ String expected = "\u597d\u597d\u597d";
+ String json = "\"" + expected + "\"";
+ String actual = gson.fromJson(json, String.class);
+ assertEquals(expected, actual);
+ }
+
+ public void testStringsWithUnicodeChineseCharactersEscapedDeserialization() throws Exception {
+ 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/JsonAdapterAnnotationOnClassesTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java
new file mode 100644
index 00000000..1b9800d8
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 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.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.annotations.JsonAdapter;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.Locale;
+import junit.framework.TestCase;
+
+/**
+ * Functional tests for the {@link com.google.gson.annotations.JsonAdapter} annotation on classes.
+ */
+public final class JsonAdapterAnnotationOnClassesTest extends TestCase {
+
+ public void testJsonAdapterInvoked() {
+ Gson gson = new Gson();
+ String json = gson.toJson(new A("bar"));
+ assertEquals("\"jsonAdapter\"", json);
+
+ // 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);
+ assertEquals("Joel", user.firstName);
+ assertEquals("Leitch", user.lastName);
+
+ json = gson.toJson(Foo.BAR);
+ assertEquals("\"bar\"", json);
+ Foo baz = gson.fromJson("\"baz\"", Foo.class);
+ assertEquals(Foo.BAZ, baz);
+ }
+
+ public void testJsonAdapterFactoryInvoked() {
+ Gson gson = new Gson();
+ String json = gson.toJson(new C("bar"));
+ assertEquals("\"jsonAdapterFactory\"", json);
+ C c = gson.fromJson("\"bar\"", C.class);
+ assertEquals("jsonAdapterFactory", c.value);
+ }
+
+ public void testRegisteredAdapterOverridesJsonAdapter() {
+ TypeAdapter<A> typeAdapter = new TypeAdapter<A>() {
+ @Override public void write(JsonWriter out, A value) throws IOException {
+ out.value("registeredAdapter");
+ }
+ @Override public A read(JsonReader in) throws IOException {
+ return new A(in.nextString());
+ }
+ };
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(A.class, typeAdapter)
+ .create();
+ String json = gson.toJson(new A("abcd"));
+ assertEquals("\"registeredAdapter\"", json);
+ }
+
+ /**
+ * The serializer overrides field adapter, but for deserializer the fieldAdapter is used.
+ */
+ public void testRegisteredSerializerOverridesJsonAdapter() {
+ JsonSerializer<A> serializer = new JsonSerializer<A>() {
+ public JsonElement serialize(A src, Type typeOfSrc,
+ JsonSerializationContext context) {
+ return new JsonPrimitive("registeredSerializer");
+ }
+ };
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(A.class, serializer)
+ .create();
+ String json = gson.toJson(new A("abcd"));
+ assertEquals("\"registeredSerializer\"", json);
+ A target = gson.fromJson("abcd", A.class);
+ assertEquals("jsonAdapter", target.value);
+ }
+
+ /**
+ * The deserializer overrides Json adapter, but for serializer the jsonAdapter is used.
+ */
+ public void testRegisteredDeserializerOverridesJsonAdapter() {
+ JsonDeserializer<A> deserializer = new JsonDeserializer<A>() {
+ public A deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context) throws JsonParseException {
+ return new A("registeredDeserializer");
+ }
+ };
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(A.class, deserializer)
+ .create();
+ String json = gson.toJson(new A("abcd"));
+ assertEquals("\"jsonAdapter\"", json);
+ A target = gson.fromJson("abcd", A.class);
+ assertEquals("registeredDeserializer", target.value);
+ }
+
+ public void testIncorrectTypeAdapterFails() {
+ try {
+ String json = new Gson().toJson(new ClassWithIncorrectJsonAdapter("bar"));
+ fail(json);
+ } catch (ClassCastException expected) {}
+ }
+
+ public void testSuperclassTypeAdapterNotInvoked() {
+ String json = new Gson().toJson(new B("bar"));
+ assertFalse(json.contains("jsonAdapter"));
+ }
+
+ @JsonAdapter(A.JsonAdapter.class)
+ private static class A {
+ final String value;
+ A(String value) {
+ this.value = value;
+ }
+ static final class JsonAdapter extends TypeAdapter<A> {
+ @Override public void write(JsonWriter out, A value) throws IOException {
+ out.value("jsonAdapter");
+ }
+ @Override public A read(JsonReader in) throws IOException {
+ in.nextString();
+ return new A("jsonAdapter");
+ }
+ }
+ }
+
+ @JsonAdapter(C.JsonAdapterFactory.class)
+ private static class C {
+ final String value;
+ C(String value) {
+ this.value = value;
+ }
+ static final class JsonAdapterFactory implements TypeAdapterFactory {
+ public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
+ return new TypeAdapter<T>() {
+ @Override public void write(JsonWriter out, T value) throws IOException {
+ out.value("jsonAdapterFactory");
+ }
+ @SuppressWarnings("unchecked")
+ @Override public T read(JsonReader in) throws IOException {
+ in.nextString();
+ return (T) new C("jsonAdapterFactory");
+ }
+ };
+ }
+ }
+ }
+
+ private static final class B extends A {
+ B(String value) {
+ super(value);
+ }
+ }
+ // Note that the type is NOT TypeAdapter<ClassWithIncorrectJsonAdapter> so this
+ // should cause error
+ @JsonAdapter(A.JsonAdapter.class)
+ private static final class ClassWithIncorrectJsonAdapter {
+ @SuppressWarnings("unused") final String value;
+ ClassWithIncorrectJsonAdapter(String value) {
+ this.value = value;
+ }
+ }
+
+ // This class is used in JsonAdapter Javadoc as an example
+ @JsonAdapter(UserJsonAdapter.class)
+ private static class User {
+ final String firstName, lastName;
+ User(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+ }
+ private static class UserJsonAdapter extends TypeAdapter<User> {
+ @Override public void write(JsonWriter out, User user) throws IOException {
+ // implement write: combine firstName and lastName into name
+ out.beginObject();
+ out.name("name");
+ out.value(user.firstName + " " + user.lastName);
+ out.endObject();
+ // implement the write method
+ }
+ @Override public User read(JsonReader in) throws IOException {
+ // implement read: split name into firstName and lastName
+ in.beginObject();
+ in.nextName();
+ String[] nameParts = in.nextString().split(" ");
+ in.endObject();
+ return new User(nameParts[0], nameParts[1]);
+ }
+ }
+
+ @JsonAdapter(FooJsonAdapter.class)
+ private static enum Foo { BAR, BAZ }
+ private static class FooJsonAdapter extends TypeAdapter<Foo> {
+ @Override public void write(JsonWriter out, Foo value) throws IOException {
+ out.value(value.name().toLowerCase(Locale.US));
+ }
+
+ @Override public Foo read(JsonReader in) throws IOException {
+ return Foo.valueOf(in.nextString().toUpperCase(Locale.US));
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java
new file mode 100644
index 00000000..d3f097ea
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 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.GsonBuilder;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.annotations.JsonAdapter;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import junit.framework.TestCase;
+
+/**
+ * Functional tests for the {@link com.google.gson.annotations.JsonAdapter} annotation on fields.
+ */
+public final class JsonAdapterAnnotationOnFieldsTest extends TestCase {
+ public void testClassAnnotationAdapterTakesPrecedenceOverDefault() {
+ Gson gson = new Gson();
+ String json = gson.toJson(new Computer(new User("Inderjeet Singh")));
+ assertEquals("{\"user\":\"UserClassAnnotationAdapter\"}", json);
+ Computer computer = gson.fromJson("{'user':'Inderjeet Singh'}", Computer.class);
+ assertEquals("UserClassAnnotationAdapter", computer.user.name);
+ }
+
+ public void testClassAnnotationAdapterFactoryTakesPrecedenceOverDefault() {
+ Gson gson = new Gson();
+ String json = gson.toJson(new Gizmo(new Part("Part")));
+ assertEquals("{\"part\":\"GizmoPartTypeAdapterFactory\"}", json);
+ Gizmo computer = gson.fromJson("{'part':'Part'}", Gizmo.class);
+ assertEquals("GizmoPartTypeAdapterFactory", computer.part.name);
+ }
+
+ public void testRegisteredTypeAdapterTakesPrecedenceOverClassAnnotationAdapter() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(User.class, new RegisteredUserAdapter())
+ .create();
+ String json = gson.toJson(new Computer(new User("Inderjeet Singh")));
+ assertEquals("{\"user\":\"RegisteredUserAdapter\"}", json);
+ Computer computer = gson.fromJson("{'user':'Inderjeet Singh'}", Computer.class);
+ assertEquals("RegisteredUserAdapter", computer.user.name);
+ }
+
+ public void testFieldAnnotationTakesPrecedenceOverRegisteredTypeAdapter() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Part.class, new TypeAdapter<Part>() {
+ @Override public void write(JsonWriter out, Part part) throws IOException {
+ throw new AssertionError();
+ }
+
+ @Override public Part read(JsonReader in) throws IOException {
+ throw new AssertionError();
+ }
+ }).create();
+ String json = gson.toJson(new Gadget(new Part("screen")));
+ assertEquals("{\"part\":\"PartJsonFieldAnnotationAdapter\"}", json);
+ Gadget gadget = gson.fromJson("{'part':'screen'}", Gadget.class);
+ assertEquals("PartJsonFieldAnnotationAdapter", gadget.part.name);
+ }
+
+ public void testFieldAnnotationTakesPrecedenceOverClassAnnotation() {
+ Gson gson = new Gson();
+ String json = gson.toJson(new Computer2(new User("Inderjeet Singh")));
+ assertEquals("{\"user\":\"UserFieldAnnotationAdapter\"}", json);
+ Computer2 target = gson.fromJson("{'user':'Interjeet Singh'}", Computer2.class);
+ assertEquals("UserFieldAnnotationAdapter", target.user.name);
+ }
+
+ private static final class Gadget {
+ @JsonAdapter(PartJsonFieldAnnotationAdapter.class)
+ final Part part;
+ Gadget(Part part) {
+ this.part = part;
+ }
+ }
+
+ private static final class Gizmo {
+ @JsonAdapter(GizmoPartTypeAdapterFactory.class)
+ final Part part;
+ Gizmo(Part part) {
+ this.part = part;
+ }
+ }
+
+ private static final class Part {
+ final String name;
+ public Part(String name) {
+ this.name = name;
+ }
+ }
+
+ private static class PartJsonFieldAnnotationAdapter extends TypeAdapter<Part> {
+ @Override public void write(JsonWriter out, Part part) throws IOException {
+ out.value("PartJsonFieldAnnotationAdapter");
+ }
+ @Override public Part read(JsonReader in) throws IOException {
+ in.nextString();
+ return new Part("PartJsonFieldAnnotationAdapter");
+ }
+ }
+
+ private static class GizmoPartTypeAdapterFactory implements TypeAdapterFactory {
+ public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
+ return new TypeAdapter<T>() {
+ @Override public void write(JsonWriter out, T value) throws IOException {
+ out.value("GizmoPartTypeAdapterFactory");
+ }
+ @SuppressWarnings("unchecked")
+ @Override public T read(JsonReader in) throws IOException {
+ in.nextString();
+ return (T) new Part("GizmoPartTypeAdapterFactory");
+ }
+ };
+ }
+ }
+
+ private static final class Computer {
+ final User user;
+ Computer(User user) {
+ this.user = user;
+ }
+ }
+
+ @JsonAdapter(UserClassAnnotationAdapter.class)
+ private static class User {
+ public final String name;
+ private User(String name) {
+ this.name = name;
+ }
+ }
+
+ private static class UserClassAnnotationAdapter extends TypeAdapter<User> {
+ @Override public void write(JsonWriter out, User user) throws IOException {
+ out.value("UserClassAnnotationAdapter");
+ }
+ @Override public User read(JsonReader in) throws IOException {
+ in.nextString();
+ return new User("UserClassAnnotationAdapter");
+ }
+ }
+
+ private static final class Computer2 {
+ // overrides the JsonAdapter annotation of User with this
+ @JsonAdapter(UserFieldAnnotationAdapter.class)
+ final User user;
+ Computer2(User user) {
+ this.user = user;
+ }
+ }
+
+ private static final class UserFieldAnnotationAdapter extends TypeAdapter<User> {
+ @Override public void write(JsonWriter out, User user) throws IOException {
+ out.value("UserFieldAnnotationAdapter");
+ }
+ @Override public User read(JsonReader in) throws IOException {
+ in.nextString();
+ return new User("UserFieldAnnotationAdapter");
+ }
+ }
+
+ private static final class RegisteredUserAdapter extends TypeAdapter<User> {
+ @Override public void write(JsonWriter out, User user) throws IOException {
+ out.value("RegisteredUserAdapter");
+ }
+ @Override public User read(JsonReader in) throws IOException {
+ in.nextString();
+ return new User("RegisteredUserAdapter");
+ }
+ }
+
+ public void testJsonAdapterInvokedOnlyForAnnotatedFields() {
+ Gson gson = new Gson();
+ String json = "{'part1':'name','part2':{'name':'name2'}}";
+ GadgetWithTwoParts gadget = gson.fromJson(json, GadgetWithTwoParts.class);
+ assertEquals("PartJsonFieldAnnotationAdapter", gadget.part1.name);
+ assertEquals("name2", gadget.part2.name);
+ }
+
+ private static final class GadgetWithTwoParts {
+ @JsonAdapter(PartJsonFieldAnnotationAdapter.class) final Part part1;
+ final Part part2; // Doesn't have the JsonAdapter annotation
+ @SuppressWarnings("unused") GadgetWithTwoParts(Part part1, Part part2) {
+ this.part1 = part1;
+ this.part2 = part2;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/JsonArrayTest.java b/gson/src/test/java/com/google/gson/functional/JsonArrayTest.java
new file mode 100644
index 00000000..22a479b8
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/JsonArrayTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.JsonArray;
+import junit.framework.TestCase;
+
+import java.math.BigInteger;
+
+/**
+ * Functional tests for adding primitives to a JsonArray.
+ *
+ * @author Dillon Dixon
+ */
+public class JsonArrayTest extends TestCase {
+
+ public void testStringPrimitiveAddition() {
+ JsonArray jsonArray = new JsonArray();
+
+ jsonArray.add("Hello");
+ jsonArray.add("Goodbye");
+ jsonArray.add("Thank you");
+ jsonArray.add((String) null);
+ jsonArray.add("Yes");
+
+ assertEquals("[\"Hello\",\"Goodbye\",\"Thank you\",null,\"Yes\"]", jsonArray.toString());
+ }
+
+ public void testIntegerPrimitiveAddition() {
+ JsonArray jsonArray = new JsonArray();
+
+ int x = 1;
+ jsonArray.add(x);
+
+ x = 2;
+ jsonArray.add(x);
+
+ x = -3;
+ jsonArray.add(x);
+
+ jsonArray.add((Integer) null);
+
+ x = 4;
+ jsonArray.add(x);
+
+ x = 0;
+ jsonArray.add(x);
+
+ assertEquals("[1,2,-3,null,4,0]", jsonArray.toString());
+ }
+
+ public void testDoublePrimitiveAddition() {
+ JsonArray jsonArray = new JsonArray();
+
+ double x = 1.0;
+ jsonArray.add(x);
+
+ x = 2.13232;
+ jsonArray.add(x);
+
+ x = 0.121;
+ jsonArray.add(x);
+
+ jsonArray.add((Double) null);
+
+ x = -0.00234;
+ jsonArray.add(x);
+
+ jsonArray.add((Double) null);
+
+ assertEquals("[1.0,2.13232,0.121,null,-0.00234,null]", jsonArray.toString());
+ }
+
+ public void testBooleanPrimitiveAddition() {
+ JsonArray jsonArray = new JsonArray();
+
+ jsonArray.add(true);
+ jsonArray.add(true);
+ jsonArray.add(false);
+ jsonArray.add(false);
+ jsonArray.add((Boolean) null);
+ jsonArray.add(true);
+
+ assertEquals("[true,true,false,false,null,true]", jsonArray.toString());
+ }
+
+ public void testCharPrimitiveAddition() {
+ JsonArray jsonArray = new JsonArray();
+
+ jsonArray.add('a');
+ jsonArray.add('e');
+ jsonArray.add('i');
+ jsonArray.add((char) 111);
+ jsonArray.add((Character) null);
+ jsonArray.add('u');
+ jsonArray.add("and sometimes Y");
+
+ assertEquals("[\"a\",\"e\",\"i\",\"o\",null,\"u\",\"and sometimes Y\"]", jsonArray.toString());
+ }
+
+ public void testMixedPrimitiveAddition() {
+ JsonArray jsonArray = new JsonArray();
+
+ jsonArray.add('a');
+ jsonArray.add("apple");
+ jsonArray.add(12121);
+ jsonArray.add((char) 111);
+ jsonArray.add((Boolean) null);
+ jsonArray.add((Character) null);
+ jsonArray.add(12.232);
+ jsonArray.add(BigInteger.valueOf(2323));
+
+ assertEquals("[\"a\",\"apple\",12121,\"o\",null,null,12.232,2323]", jsonArray.toString());
+ }
+
+ public void testNullPrimitiveAddition() {
+ JsonArray jsonArray = new JsonArray();
+
+ jsonArray.add((Character) null);
+ jsonArray.add((Boolean) null);
+ jsonArray.add((Integer) null);
+ jsonArray.add((Double) null);
+ jsonArray.add((Float) null);
+ jsonArray.add((BigInteger) null);
+ jsonArray.add((String) null);
+ jsonArray.add((Boolean) null);
+ jsonArray.add((Number) null);
+
+ assertEquals("[null,null,null,null,null,null,null,null,null]", jsonArray.toString());
+ }
+
+ public void testSameAddition() {
+ JsonArray jsonArray = new JsonArray();
+
+ jsonArray.add('a');
+ jsonArray.add('a');
+ jsonArray.add(true);
+ jsonArray.add(true);
+ jsonArray.add(1212);
+ jsonArray.add(1212);
+ jsonArray.add(34.34);
+ jsonArray.add(34.34);
+ jsonArray.add((Boolean) null);
+ jsonArray.add((Boolean) null);
+
+ assertEquals("[\"a\",\"a\",true,true,1212,1212,34.34,34.34,null,null]", jsonArray.toString());
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/JsonParserTest.java b/gson/src/test/java/com/google/gson/functional/JsonParserTest.java
new file mode 100644
index 00000000..44f4477c
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/JsonParserTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.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.common.TestTypes.BagOfPrimitives;
+import com.google.gson.common.TestTypes.Nested;
+import com.google.gson.reflect.TypeToken;
+
+import junit.framework.TestCase;
+
+import java.io.StringReader;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Functional tests for that use JsonParser and related Gson methods
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class JsonParserTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testParseInvalidJson() {
+ try {
+ gson.fromJson("[[]", Object[].class);
+ fail();
+ } catch (JsonSyntaxException expected) { }
+ }
+
+ public void testDeserializingCustomTree() {
+ JsonObject obj = new JsonObject();
+ obj.addProperty("stringValue", "foo");
+ obj.addProperty("intValue", 11);
+ BagOfPrimitives target = gson.fromJson(obj, BagOfPrimitives.class);
+ assertEquals(11, target.intValue);
+ assertEquals("foo", target.stringValue);
+ }
+
+ public void testBadTypeForDeserializingCustomTree() {
+ JsonObject obj = new JsonObject();
+ obj.addProperty("stringValue", "foo");
+ obj.addProperty("intValue", 11);
+ JsonArray array = new JsonArray();
+ array.add(obj);
+ try {
+ gson.fromJson(array, BagOfPrimitives.class);
+ fail("BagOfPrimitives is not an array");
+ } catch (JsonParseException expected) { }
+ }
+
+ public void testBadFieldTypeForCustomDeserializerCustomTree() {
+ JsonArray array = new JsonArray();
+ array.add(new JsonPrimitive("blah"));
+ JsonObject obj = new JsonObject();
+ obj.addProperty("stringValue", "foo");
+ obj.addProperty("intValue", 11);
+ obj.add("longValue", array);
+
+ try {
+ gson.fromJson(obj, BagOfPrimitives.class);
+ fail("BagOfPrimitives is not an array");
+ } catch (JsonParseException expected) { }
+ }
+
+ public void testBadFieldTypeForDeserializingCustomTree() {
+ JsonArray array = new JsonArray();
+ array.add(new JsonPrimitive("blah"));
+ JsonObject primitive1 = new JsonObject();
+ primitive1.addProperty("string", "foo");
+ primitive1.addProperty("intValue", 11);
+
+ JsonObject obj = new JsonObject();
+ obj.add("primitive1", primitive1);
+ obj.add("primitive2", array);
+
+ try {
+ gson.fromJson(obj, Nested.class);
+ fail("Nested has field BagOfPrimitives which is not an array");
+ } catch (JsonParseException expected) { }
+ }
+
+ public void testChangingCustomTreeAndDeserializing() {
+ StringReader json =
+ new StringReader("{'stringValue':'no message','intValue':10,'longValue':20}");
+ JsonObject obj = (JsonObject) new JsonParser().parse(json);
+ obj.remove("stringValue");
+ obj.addProperty("stringValue", "fooBar");
+ BagOfPrimitives target = gson.fromJson(obj, BagOfPrimitives.class);
+ assertEquals(10, target.intValue);
+ assertEquals(20, target.longValue);
+ assertEquals("fooBar", target.stringValue);
+ }
+
+ public void testExtraCommasInArrays() {
+ Type type = new TypeToken<List<String>>() {}.getType();
+ assertEquals(list("a", null, "b", null, null), gson.fromJson("[a,,b,,]", type));
+ assertEquals(list(null, null), gson.fromJson("[,]", type));
+ assertEquals(list("a", null), gson.fromJson("[a,]", type));
+ }
+
+ public void testExtraCommasInMaps() {
+ Type type = new TypeToken<Map<String, String>>() {}.getType();
+ try {
+ gson.fromJson("{a:b,}", type);
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ private <T> List<T> list(T... elements) {
+ return Arrays.asList(elements);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/JsonTreeTest.java b/gson/src/test/java/com/google/gson/functional/JsonTreeTest.java
new file mode 100644
index 00000000..a6479403
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/JsonTreeTest.java
@@ -0,0 +1,89 @@
+package com.google.gson.functional;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import junit.framework.TestCase;
+
+/**
+ * Functional tests for {@link Gson#toJsonTree(Object)} and
+ * {@link Gson#toJsonTree(Object, java.lang.reflect.Type)}
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class JsonTreeTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testToJsonTree() {
+ BagOfPrimitives bag = new BagOfPrimitives(10L, 5, false, "foo");
+ JsonElement json = gson.toJsonTree(bag);
+ assertTrue(json.isJsonObject());
+ JsonObject obj = json.getAsJsonObject();
+ Set<Entry<String, JsonElement>> children = obj.entrySet();
+ assertEquals(4, children.size());
+ assertContains(obj, new JsonPrimitive(10L));
+ assertContains(obj, new JsonPrimitive(5));
+ assertContains(obj, new JsonPrimitive(false));
+ assertContains(obj, new JsonPrimitive("foo"));
+ }
+
+ public void testToJsonTreeObjectType() {
+ SubTypeOfBagOfPrimitives bag = new SubTypeOfBagOfPrimitives(10L, 5, false, "foo", 1.4F);
+ JsonElement json = gson.toJsonTree(bag, BagOfPrimitives.class);
+ assertTrue(json.isJsonObject());
+ JsonObject obj = json.getAsJsonObject();
+ Set<Entry<String, JsonElement>> children = obj.entrySet();
+ assertEquals(4, children.size());
+ assertContains(obj, new JsonPrimitive(10L));
+ assertContains(obj, new JsonPrimitive(5));
+ assertContains(obj, new JsonPrimitive(false));
+ assertContains(obj, new JsonPrimitive("foo"));
+ }
+
+ public void testJsonTreeToString() {
+ SubTypeOfBagOfPrimitives bag = new SubTypeOfBagOfPrimitives(10L, 5, false, "foo", 1.4F);
+ String json1 = gson.toJson(bag);
+ JsonElement jsonElement = gson.toJsonTree(bag, SubTypeOfBagOfPrimitives.class);
+ String json2 = gson.toJson(jsonElement);
+ assertEquals(json1, json2);
+ }
+
+ public void testJsonTreeNull() {
+ BagOfPrimitives bag = new BagOfPrimitives(10L, 5, false, null);
+ JsonObject jsonElement = (JsonObject) gson.toJsonTree(bag, BagOfPrimitives.class);
+ assertFalse(jsonElement.has("stringValue"));
+ }
+
+ private void assertContains(JsonObject json, JsonPrimitive child) {
+ for (Map.Entry<String, JsonElement> entry : json.entrySet()) {
+ JsonElement node = entry.getValue();
+ if (node.isJsonPrimitive()) {
+ if (node.getAsJsonPrimitive().equals(child)) {
+ return;
+ }
+ }
+ }
+ fail();
+ }
+
+ private static class SubTypeOfBagOfPrimitives extends BagOfPrimitives {
+ @SuppressWarnings("unused")
+ float f = 1.2F;
+ public SubTypeOfBagOfPrimitives(long l, int i, boolean b, String string, float f) {
+ super(l, i, b, string);
+ this.f = f;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java b/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java
new file mode 100644
index 00000000..c7cfcdf9
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/MapAsArrayTypeAdapterTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2010 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.GsonBuilder;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import junit.framework.TestCase;
+
+public class MapAsArrayTypeAdapterTest extends TestCase {
+
+ public void testSerializeComplexMapWithTypeAdapter() {
+ Type type = new TypeToken<Map<Point, String>>() {}.getType();
+ Gson gson = new GsonBuilder()
+ .enableComplexMapKeySerialization()
+ .create();
+
+ Map<Point, String> original = new LinkedHashMap<Point, String>();
+ original.put(new Point(5, 5), "a");
+ original.put(new Point(8, 8), "b");
+ String json = gson.toJson(original, type);
+ assertEquals("[[{\"x\":5,\"y\":5},\"a\"],[{\"x\":8,\"y\":8},\"b\"]]", json);
+ assertEquals(original, gson.<Map<Point, String>>fromJson(json, type));
+
+ // test that registering a type adapter for one map doesn't interfere with others
+ Map<String, Boolean> otherMap = new LinkedHashMap<String, Boolean>();
+ otherMap.put("t", true);
+ otherMap.put("f", false);
+ assertEquals("{\"t\":true,\"f\":false}",
+ gson.toJson(otherMap, Map.class));
+ assertEquals("{\"t\":true,\"f\":false}",
+ gson.toJson(otherMap, new TypeToken<Map<String, Boolean>>() {}.getType()));
+ assertEquals(otherMap, gson.<Object>fromJson("{\"t\":true,\"f\":false}",
+ new TypeToken<Map<String, Boolean>>() {}.getType()));
+ }
+
+ public void disabled_testTwoTypesCollapseToOneSerialize() {
+ Gson gson = new GsonBuilder()
+ .enableComplexMapKeySerialization()
+ .create();
+
+ Map<Number, String> original = new LinkedHashMap<Number, String>();
+ original.put(new Double(1.0), "a");
+ original.put(new Float(1.0), "b");
+ try {
+ gson.toJson(original, new TypeToken<Map<Number, String>>() {}.getType());
+ fail(); // we no longer hash keys at serialization time
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testTwoTypesCollapseToOneDeserialize() {
+ Gson gson = new GsonBuilder()
+ .enableComplexMapKeySerialization()
+ .create();
+
+ String s = "[[\"1.00\",\"a\"],[\"1.0\",\"b\"]]";
+ try {
+ gson.fromJson(s, new TypeToken<Map<Double, String>>() {}.getType());
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testMultipleEnableComplexKeyRegistrationHasNoEffect() throws Exception {
+ Type type = new TypeToken<Map<Point, String>>() {}.getType();
+ Gson gson = new GsonBuilder()
+ .enableComplexMapKeySerialization()
+ .enableComplexMapKeySerialization()
+ .create();
+
+ Map<Point, String> original = new LinkedHashMap<Point, String>();
+ original.put(new Point(6, 5), "abc");
+ original.put(new Point(1, 8), "def");
+ String json = gson.toJson(original, type);
+ assertEquals("[[{\"x\":6,\"y\":5},\"abc\"],[{\"x\":1,\"y\":8},\"def\"]]", json);
+ assertEquals(original, gson.<Map<Point, String>>fromJson(json, type));
+ }
+
+ public void testMapWithTypeVariableSerialization() {
+ Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
+ PointWithProperty<Point> map = new PointWithProperty<Point>();
+ map.map.put(new Point(2, 3), new Point(4, 5));
+ Type type = new TypeToken<PointWithProperty<Point>>(){}.getType();
+ String json = gson.toJson(map, type);
+ assertEquals("{\"map\":[[{\"x\":2,\"y\":3},{\"x\":4,\"y\":5}]]}", json);
+ }
+
+ public void testMapWithTypeVariableDeserialization() {
+ Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
+ String json = "{map:[[{x:2,y:3},{x:4,y:5}]]}";
+ Type type = new TypeToken<PointWithProperty<Point>>(){}.getType();
+ PointWithProperty<Point> map = gson.fromJson(json, type);
+ Point key = map.map.keySet().iterator().next();
+ Point value = map.map.values().iterator().next();
+ assertEquals(new Point(2, 3), key);
+ assertEquals(new Point(4, 5), value);
+ }
+
+ static class Point {
+ int x;
+ int y;
+ Point(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+ Point() {}
+ @Override public boolean equals(Object o) {
+ return o instanceof Point && ((Point) o).x == x && ((Point) o).y == y;
+ }
+ @Override public int hashCode() {
+ return x * 37 + y;
+ }
+ @Override public String toString() {
+ return "(" + x + "," + y + ")";
+ }
+ }
+
+ static class PointWithProperty<T> {
+ Map<Point, T> map = new HashMap<Point, T>();
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/MapTest.java b/gson/src/test/java/com/google/gson/functional/MapTest.java
new file mode 100755
index 00000000..c175bae5
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/MapTest.java
@@ -0,0 +1,582 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.InstanceCreator;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.common.TestTypes;
+import com.google.gson.internal.$Gson$Types;
+import com.google.gson.reflect.TypeToken;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Functional test for Json serialization and deserialization for Maps
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class MapTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testMapSerialization() {
+ Map<String, Integer> map = new LinkedHashMap<String, Integer>();
+ map.put("a", 1);
+ map.put("b", 2);
+ Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
+ String json = gson.toJson(map, typeOfMap);
+ assertTrue(json.contains("\"a\":1"));
+ assertTrue(json.contains("\"b\":2"));
+ }
+
+ public void testMapDeserialization() {
+ String json = "{\"a\":1,\"b\":2}";
+ Type typeOfMap = new TypeToken<Map<String,Integer>>(){}.getType();
+ Map<String, Integer> target = gson.fromJson(json, typeOfMap);
+ assertEquals(1, target.get("a").intValue());
+ assertEquals(2, target.get("b").intValue());
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void testRawMapSerialization() {
+ Map map = new LinkedHashMap();
+ map.put("a", 1);
+ map.put("b", "string");
+ String json = gson.toJson(map);
+ assertTrue(json.contains("\"a\":1"));
+ assertTrue(json.contains("\"b\":\"string\""));
+ }
+
+ public void testMapSerializationEmpty() {
+ Map<String, Integer> map = new LinkedHashMap<String, Integer>();
+ Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
+ String json = gson.toJson(map, typeOfMap);
+ assertEquals("{}", json);
+ }
+
+ public void testMapDeserializationEmpty() {
+ Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
+ Map<String, Integer> map = gson.fromJson("{}", typeOfMap);
+ assertTrue(map.isEmpty());
+ }
+
+ public void testMapSerializationWithNullValue() {
+ Map<String, Integer> map = new LinkedHashMap<String, Integer>();
+ map.put("abc", null);
+ Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
+ String json = gson.toJson(map, typeOfMap);
+
+ // Maps are represented as JSON objects, so ignoring null field
+ assertEquals("{}", json);
+ }
+
+ public void testMapDeserializationWithNullValue() {
+ Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
+ Map<String, Integer> map = gson.fromJson("{\"abc\":null}", typeOfMap);
+ assertEquals(1, map.size());
+ assertNull(map.get("abc"));
+ }
+
+ public void testMapSerializationWithNullValueButSerializeNulls() {
+ gson = new GsonBuilder().serializeNulls().create();
+ Map<String, Integer> map = new LinkedHashMap<String, Integer>();
+ map.put("abc", null);
+ Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
+ String json = gson.toJson(map, typeOfMap);
+
+ assertEquals("{\"abc\":null}", json);
+ }
+
+ public void testMapSerializationWithNullKey() {
+ Map<String, Integer> map = new LinkedHashMap<String, Integer>();
+ map.put(null, 123);
+ Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
+ String json = gson.toJson(map, typeOfMap);
+
+ assertEquals("{\"null\":123}", json);
+ }
+
+ public void testMapDeserializationWithNullKey() {
+ Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
+ Map<String, Integer> map = gson.fromJson("{\"null\":123}", typeOfMap);
+ assertEquals(1, map.size());
+ assertEquals(123, map.get("null").intValue());
+ assertNull(map.get(null));
+
+ map = gson.fromJson("{null:123}", typeOfMap);
+ assertEquals(1, map.size());
+ assertEquals(123, map.get("null").intValue());
+ assertNull(map.get(null));
+ }
+
+ public void testMapSerializationWithIntegerKeys() {
+ Map<Integer, String> map = new LinkedHashMap<Integer, String>();
+ map.put(123, "456");
+ Type typeOfMap = new TypeToken<Map<Integer, String>>() {}.getType();
+ String json = gson.toJson(map, typeOfMap);
+
+ assertEquals("{\"123\":\"456\"}", json);
+ }
+
+ public void testMapDeserializationWithIntegerKeys() {
+ Type typeOfMap = new TypeToken<Map<Integer, String>>() {}.getType();
+ Map<Integer, String> map = gson.fromJson("{\"123\":\"456\"}", typeOfMap);
+ assertEquals(1, map.size());
+ assertTrue(map.containsKey(123));
+ assertEquals("456", map.get(123));
+ }
+
+ public void testHashMapDeserialization() throws Exception {
+ Type typeOfMap = new TypeToken<HashMap<Integer, String>>() {}.getType();
+ HashMap<Integer, String> map = gson.fromJson("{\"123\":\"456\"}", typeOfMap);
+ assertEquals(1, map.size());
+ assertTrue(map.containsKey(123));
+ assertEquals("456", map.get(123));
+ }
+
+ public void testSortedMap() throws Exception {
+ Type typeOfMap = new TypeToken<SortedMap<Integer, String>>() {}.getType();
+ SortedMap<Integer, String> map = gson.fromJson("{\"123\":\"456\"}", typeOfMap);
+ assertEquals(1, map.size());
+ assertTrue(map.containsKey(123));
+ assertEquals("456", map.get(123));
+ }
+
+ public void testParameterizedMapSubclassSerialization() {
+ MyParameterizedMap<String, String> map = new MyParameterizedMap<String, String>(10);
+ map.put("a", "b");
+ Type type = new TypeToken<MyParameterizedMap<String, String>>() {}.getType();
+ String json = gson.toJson(map, type);
+ assertTrue(json.contains("\"a\":\"b\""));
+ }
+
+ @SuppressWarnings({ "unused", "serial" })
+ private static class MyParameterizedMap<K, V> extends LinkedHashMap<K, V> {
+ final int foo;
+ MyParameterizedMap(int foo) {
+ this.foo = foo;
+ }
+ }
+
+ public void testMapSubclassSerialization() {
+ MyMap map = new MyMap();
+ map.put("a", "b");
+ String json = gson.toJson(map, MyMap.class);
+ assertTrue(json.contains("\"a\":\"b\""));
+ }
+
+ public void testMapStandardSubclassDeserialization() {
+ String json = "{a:'1',b:'2'}";
+ Type type = new TypeToken<LinkedHashMap<String, String>>() {}.getType();
+ LinkedHashMap<String, Integer> map = gson.fromJson(json, type);
+ assertEquals("1", map.get("a"));
+ assertEquals("2", map.get("b"));
+ }
+
+ public void testMapSubclassDeserialization() {
+ Gson gson = new GsonBuilder().registerTypeAdapter(MyMap.class, new InstanceCreator<MyMap>() {
+ public MyMap createInstance(Type type) {
+ return new MyMap();
+ }
+ }).create();
+ String json = "{\"a\":1,\"b\":2}";
+ MyMap map = gson.fromJson(json, MyMap.class);
+ assertEquals("1", map.get("a"));
+ assertEquals("2", map.get("b"));
+ }
+
+ public void testCustomSerializerForSpecificMapType() {
+ Type type = $Gson$Types.newParameterizedTypeWithOwner(
+ null, Map.class, String.class, Long.class);
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(type, new JsonSerializer<Map<String, Long>>() {
+ public JsonElement serialize(Map<String, Long> src, Type typeOfSrc,
+ JsonSerializationContext context) {
+ JsonArray array = new JsonArray();
+ for (long value : src.values()) {
+ array.add(new JsonPrimitive(value));
+ }
+ return array;
+ }
+ }).create();
+
+ Map<String, Long> src = new LinkedHashMap<String, Long>();
+ src.put("one", 1L);
+ src.put("two", 2L);
+ src.put("three", 3L);
+
+ assertEquals("[1,2,3]", gson.toJson(src, type));
+ }
+
+ /**
+ * Created in response to http://code.google.com/p/google-gson/issues/detail?id=99
+ */
+ private static class ClassWithAMap {
+ Map<String, String> map = new TreeMap<String, String>();
+ }
+
+ /**
+ * Created in response to http://code.google.com/p/google-gson/issues/detail?id=99
+ */
+ public void testMapSerializationWithNullValues() {
+ ClassWithAMap target = new ClassWithAMap();
+ target.map.put("name1", null);
+ target.map.put("name2", "value2");
+ String json = gson.toJson(target);
+ assertFalse(json.contains("name1"));
+ assertTrue(json.contains("name2"));
+ }
+
+ /**
+ * Created in response to http://code.google.com/p/google-gson/issues/detail?id=99
+ */
+ public void testMapSerializationWithNullValuesSerialized() {
+ Gson gson = new GsonBuilder().serializeNulls().create();
+ ClassWithAMap target = new ClassWithAMap();
+ target.map.put("name1", null);
+ target.map.put("name2", "value2");
+ String json = gson.toJson(target);
+ assertTrue(json.contains("name1"));
+ assertTrue(json.contains("name2"));
+ }
+
+ public void testMapSerializationWithWildcardValues() {
+ Map<String, ? extends Collection<? extends Integer>> map =
+ new LinkedHashMap<String, Collection<Integer>>();
+ map.put("test", null);
+ Type typeOfMap =
+ new TypeToken<Map<String, ? extends Collection<? extends Integer>>>() {}.getType();
+ String json = gson.toJson(map, typeOfMap);
+
+ assertEquals("{}", json);
+ }
+
+ public void testMapDeserializationWithWildcardValues() {
+ Type typeOfMap = new TypeToken<Map<String, ? extends Long>>() {}.getType();
+ Map<String, ? extends Long> map = gson.fromJson("{\"test\":123}", typeOfMap);
+ assertEquals(1, map.size());
+ assertEquals(new Long(123L), map.get("test"));
+ }
+
+
+ private static class MyMap extends LinkedHashMap<String, String> {
+ private static final long serialVersionUID = 1L;
+
+ @SuppressWarnings("unused")
+ int foo = 10;
+ }
+
+ /**
+ * From bug report http://code.google.com/p/google-gson/issues/detail?id=95
+ */
+ public void testMapOfMapSerialization() {
+ Map<String, Map<String, String>> map = new HashMap<String, Map<String, String>>();
+ Map<String, String> nestedMap = new HashMap<String, String>();
+ nestedMap.put("1", "1");
+ nestedMap.put("2", "2");
+ map.put("nestedMap", nestedMap);
+ String json = gson.toJson(map);
+ assertTrue(json.contains("nestedMap"));
+ assertTrue(json.contains("\"1\":\"1\""));
+ assertTrue(json.contains("\"2\":\"2\""));
+ }
+
+ /**
+ * From bug report http://code.google.com/p/google-gson/issues/detail?id=95
+ */
+ public void testMapOfMapDeserialization() {
+ String json = "{nestedMap:{'2':'2','1':'1'}}";
+ Type type = new TypeToken<Map<String, Map<String, String>>>(){}.getType();
+ Map<String, Map<String, String>> map = gson.fromJson(json, type);
+ Map<String, String> nested = map.get("nestedMap");
+ assertEquals("1", nested.get("1"));
+ assertEquals("2", nested.get("2"));
+ }
+
+ /**
+ * From bug report http://code.google.com/p/google-gson/issues/detail?id=178
+ */
+ public void testMapWithQuotes() {
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("a\"b", "c\"d");
+ String json = gson.toJson(map);
+ assertEquals("{\"a\\\"b\":\"c\\\"d\"}", json);
+ }
+
+ /**
+ * From issue 227.
+ */
+ public void testWriteMapsWithEmptyStringKey() {
+ Map<String, Boolean> map = new HashMap<String, Boolean>();
+ map.put("", true);
+ assertEquals("{\"\":true}", gson.toJson(map));
+
+ }
+
+ public void testReadMapsWithEmptyStringKey() {
+ Map<String, Boolean> map = gson.fromJson("{\"\":true}", new TypeToken<Map<String, Boolean>>() {}.getType());
+ assertEquals(Boolean.TRUE, map.get(""));
+ }
+
+ /**
+ * From bug report http://code.google.com/p/google-gson/issues/detail?id=204
+ */
+ public void testSerializeMaps() {
+ Map<String, Object> map = new LinkedHashMap<String, Object>();
+ map.put("a", 12);
+ map.put("b", null);
+
+ LinkedHashMap<String, Object> innerMap = new LinkedHashMap<String, Object>();
+ innerMap.put("test", 1);
+ innerMap.put("TestStringArray", new String[] { "one", "two" });
+ map.put("c", innerMap);
+
+ assertEquals("{\"a\":12,\"b\":null,\"c\":{\"test\":1,\"TestStringArray\":[\"one\",\"two\"]}}",
+ new GsonBuilder().serializeNulls().create().toJson(map));
+ assertEquals("{\n \"a\": 12,\n \"b\": null,\n \"c\": "
+ + "{\n \"test\": 1,\n \"TestStringArray\": "
+ + "[\n \"one\",\n \"two\"\n ]\n }\n}",
+ new GsonBuilder().setPrettyPrinting().serializeNulls().create().toJson(map));
+ assertEquals("{\"a\":12,\"c\":{\"test\":1,\"TestStringArray\":[\"one\",\"two\"]}}",
+ new GsonBuilder().create().toJson(map));
+ assertEquals("{\n \"a\": 12,\n \"c\": "
+ + "{\n \"test\": 1,\n \"TestStringArray\": "
+ + "[\n \"one\",\n \"two\"\n ]\n }\n}",
+ new GsonBuilder().setPrettyPrinting().create().toJson(map));
+
+ innerMap.put("d", "e");
+ assertEquals("{\"a\":12,\"c\":{\"test\":1,\"TestStringArray\":[\"one\",\"two\"],\"d\":\"e\"}}",
+ new Gson().toJson(map));
+ }
+
+ public final void testInterfaceTypeMap() {
+ MapClass element = new MapClass();
+ TestTypes.Sub subType = new TestTypes.Sub();
+ element.addBase("Test", subType);
+ element.addSub("Test", subType);
+
+ String subTypeJson = new Gson().toJson(subType);
+ String expected = "{\"bases\":{\"Test\":" + subTypeJson + "},"
+ + "\"subs\":{\"Test\":" + subTypeJson + "}}";
+
+ Gson gsonWithComplexKeys = new GsonBuilder()
+ .enableComplexMapKeySerialization()
+ .create();
+ String json = gsonWithComplexKeys.toJson(element);
+ assertEquals(expected, json);
+
+ Gson gson = new Gson();
+ json = gson.toJson(element);
+ assertEquals(expected, json);
+ }
+
+ public final void testInterfaceTypeMapWithSerializer() {
+ MapClass element = new MapClass();
+ TestTypes.Sub subType = new TestTypes.Sub();
+ element.addBase("Test", subType);
+ element.addSub("Test", subType);
+
+ Gson tempGson = new Gson();
+ String subTypeJson = tempGson.toJson(subType);
+ final JsonElement baseTypeJsonElement = tempGson.toJsonTree(subType, TestTypes.Base.class);
+ String baseTypeJson = tempGson.toJson(baseTypeJsonElement);
+ String expected = "{\"bases\":{\"Test\":" + baseTypeJson + "},"
+ + "\"subs\":{\"Test\":" + subTypeJson + "}}";
+
+ JsonSerializer<TestTypes.Base> baseTypeAdapter = new JsonSerializer<TestTypes.Base>() {
+ public JsonElement serialize(TestTypes.Base src, Type typeOfSrc,
+ JsonSerializationContext context) {
+ return baseTypeJsonElement;
+ }
+ };
+
+ Gson gson = new GsonBuilder()
+ .enableComplexMapKeySerialization()
+ .registerTypeAdapter(TestTypes.Base.class, baseTypeAdapter)
+ .create();
+ String json = gson.toJson(element);
+ assertEquals(expected, json);
+
+ gson = new GsonBuilder()
+ .registerTypeAdapter(TestTypes.Base.class, baseTypeAdapter)
+ .create();
+ json = gson.toJson(element);
+ assertEquals(expected, json);
+ }
+
+ public void testGeneralMapField() throws Exception {
+ MapWithGeneralMapParameters map = new MapWithGeneralMapParameters();
+ map.map.put("string", "testString");
+ map.map.put("stringArray", new String[]{"one", "two"});
+ map.map.put("objectArray", new Object[]{1, 2L, "three"});
+
+ String expected = "{\"map\":{\"string\":\"testString\",\"stringArray\":"
+ + "[\"one\",\"two\"],\"objectArray\":[1,2,\"three\"]}}";
+ assertEquals(expected, gson.toJson(map));
+
+ gson = new GsonBuilder()
+ .enableComplexMapKeySerialization()
+ .create();
+ assertEquals(expected, gson.toJson(map));
+ }
+
+ public void testComplexKeysSerialization() {
+ Map<Point, String> map = new LinkedHashMap<Point, String>();
+ map.put(new Point(2, 3), "a");
+ map.put(new Point(5, 7), "b");
+ String json = "{\"2,3\":\"a\",\"5,7\":\"b\"}";
+ assertEquals(json, gson.toJson(map, new TypeToken<Map<Point, String>>() {}.getType()));
+ assertEquals(json, gson.toJson(map, Map.class));
+ }
+
+ public void testComplexKeysDeserialization() {
+ String json = "{'2,3':'a','5,7':'b'}";
+ try {
+ gson.fromJson(json, new TypeToken<Map<Point, String>>() {}.getType());
+ fail();
+ } catch (JsonParseException expected) {
+ }
+ }
+
+ public void testStringKeyDeserialization() {
+ String json = "{'2,3':'a','5,7':'b'}";
+ Map<String, String> map = new LinkedHashMap<String, String>();
+ map.put("2,3", "a");
+ map.put("5,7", "b");
+ assertEquals(map, gson.fromJson(json, new TypeToken<Map<String, String>>() {}.getType()));
+ }
+
+ public void testNumberKeyDeserialization() {
+ String json = "{'2.3':'a','5.7':'b'}";
+ Map<Double, String> map = new LinkedHashMap<Double, String>();
+ map.put(2.3, "a");
+ map.put(5.7, "b");
+ assertEquals(map, gson.fromJson(json, new TypeToken<Map<Double, String>>() {}.getType()));
+ }
+
+ public void testBooleanKeyDeserialization() {
+ String json = "{'true':'a','false':'b'}";
+ Map<Boolean, String> map = new LinkedHashMap<Boolean, String>();
+ map.put(true, "a");
+ map.put(false, "b");
+ assertEquals(map, gson.fromJson(json, new TypeToken<Map<Boolean, String>>() {}.getType()));
+ }
+
+ public void testMapDeserializationWithDuplicateKeys() {
+ try {
+ gson.fromJson("{'a':1,'a':2}", new TypeToken<Map<String, Integer>>() {}.getType());
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testSerializeMapOfMaps() {
+ Type type = new TypeToken<Map<String, Map<String, String>>>() {}.getType();
+ Map<String, Map<String, String>> map = newMap(
+ "a", newMap("ka1", "va1", "ka2", "va2"),
+ "b", newMap("kb1", "vb1", "kb2", "vb2"));
+ assertEquals("{'a':{'ka1':'va1','ka2':'va2'},'b':{'kb1':'vb1','kb2':'vb2'}}",
+ gson.toJson(map, type).replace('"', '\''));
+ }
+
+ public void testDeerializeMapOfMaps() {
+ Type type = new TypeToken<Map<String, Map<String, String>>>() {}.getType();
+ Map<String, Map<String, String>> map = newMap(
+ "a", newMap("ka1", "va1", "ka2", "va2"),
+ "b", newMap("kb1", "vb1", "kb2", "vb2"));
+ String json = "{'a':{'ka1':'va1','ka2':'va2'},'b':{'kb1':'vb1','kb2':'vb2'}}";
+ assertEquals(map, gson.fromJson(json, type));
+ }
+
+ private <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
+ Map<K, V> result = new LinkedHashMap<K, V>();
+ result.put(key1, value1);
+ result.put(key2, value2);
+ return result;
+ }
+
+ public void testMapNamePromotionWithJsonElementReader() {
+ String json = "{'2.3':'a'}";
+ Map<Double, String> map = new LinkedHashMap<Double, String>();
+ map.put(2.3, "a");
+ JsonElement tree = new JsonParser().parse(json);
+ assertEquals(map, gson.fromJson(tree, new TypeToken<Map<Double, String>>() {}.getType()));
+ }
+
+ static class Point {
+ private final int x;
+ private final int y;
+
+ Point(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ @Override public boolean equals(Object o) {
+ return o instanceof Point && x == ((Point) o).x && y == ((Point) o).y;
+ }
+
+ @Override public int hashCode() {
+ return x * 37 + y;
+ }
+
+ @Override public String toString() {
+ return x + "," + y;
+ }
+ }
+
+ static final class MapClass {
+ private final Map<String, TestTypes.Base> bases = new HashMap<String, TestTypes.Base>();
+ private final Map<String, TestTypes.Sub> subs = new HashMap<String, TestTypes.Sub>();
+
+ public final void addBase(String name, TestTypes.Base value) {
+ bases.put(name, value);
+ }
+
+ public final void addSub(String name, TestTypes.Sub value) {
+ subs.put(name, value);
+ }
+ }
+
+ static final class MapWithGeneralMapParameters {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ final Map<String, Object> map = new LinkedHashMap();
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java b/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java
new file mode 100644
index 00000000..7ecbffc8
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2011 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.JsonObject;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tests for Gson serialization of a sub-class object while encountering a base-class type
+ *
+ * @author Inderjeet Singh
+ */
+@SuppressWarnings("unused")
+public class MoreSpecificTypeSerializationTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testSubclassFields() {
+ ClassWithBaseFields target = new ClassWithBaseFields(new Sub(1, 2));
+ String json = gson.toJson(target);
+ assertTrue(json.contains("\"b\":1"));
+ assertTrue(json.contains("\"s\":2"));
+ }
+
+ public void testListOfSubclassFields() {
+ Collection<Base> list = new ArrayList<Base>();
+ list.add(new Base(1));
+ list.add(new Sub(2, 3));
+ ClassWithContainersOfBaseFields target = new ClassWithContainersOfBaseFields(list, null);
+ String json = gson.toJson(target);
+ assertTrue(json, json.contains("{\"b\":1}"));
+ assertTrue(json, json.contains("{\"s\":3,\"b\":2}"));
+ }
+
+ public void testMapOfSubclassFields() {
+ Map<String, Base> map = new HashMap<String, Base>();
+ map.put("base", new Base(1));
+ map.put("sub", new Sub(2, 3));
+ ClassWithContainersOfBaseFields target = new ClassWithContainersOfBaseFields(null, map);
+ JsonObject json = gson.toJsonTree(target).getAsJsonObject().get("map").getAsJsonObject();
+ assertEquals(1, json.get("base").getAsJsonObject().get("b").getAsInt());
+ JsonObject sub = json.get("sub").getAsJsonObject();
+ assertEquals(2, sub.get("b").getAsInt());
+ assertEquals(3, sub.get("s").getAsInt());
+ }
+
+ /**
+ * For parameterized type, Gson ignores the more-specific type and sticks to the declared type
+ */
+ public void testParameterizedSubclassFields() {
+ ClassWithParameterizedBaseFields target = new ClassWithParameterizedBaseFields(
+ new ParameterizedSub<String>("one", "two"));
+ String json = gson.toJson(target);
+ assertTrue(json.contains("\"t\":\"one\""));
+ assertFalse(json.contains("\"s\""));
+ }
+
+ /**
+ * For parameterized type in a List, Gson ignores the more-specific type and sticks to
+ * the declared type
+ */
+ public void testListOfParameterizedSubclassFields() {
+ Collection<ParameterizedBase<String>> list = new ArrayList<ParameterizedBase<String>>();
+ list.add(new ParameterizedBase<String>("one"));
+ list.add(new ParameterizedSub<String>("two", "three"));
+ ClassWithContainersOfParameterizedBaseFields target =
+ new ClassWithContainersOfParameterizedBaseFields(list, null);
+ String json = gson.toJson(target);
+ assertTrue(json, json.contains("{\"t\":\"one\"}"));
+ assertFalse(json, json.contains("\"s\":"));
+ }
+
+ /**
+ * For parameterized type in a map, Gson ignores the more-specific type and sticks to the
+ * declared type
+ */
+ public void testMapOfParameterizedSubclassFields() {
+ Map<String, ParameterizedBase<String>> map = new HashMap<String, ParameterizedBase<String>>();
+ map.put("base", new ParameterizedBase<String>("one"));
+ map.put("sub", new ParameterizedSub<String>("two", "three"));
+ ClassWithContainersOfParameterizedBaseFields target =
+ new ClassWithContainersOfParameterizedBaseFields(null, map);
+ JsonObject json = gson.toJsonTree(target).getAsJsonObject().get("map").getAsJsonObject();
+ assertEquals("one", json.get("base").getAsJsonObject().get("t").getAsString());
+ JsonObject sub = json.get("sub").getAsJsonObject();
+ assertEquals("two", sub.get("t").getAsString());
+ assertNull(sub.get("s"));
+ }
+
+ private static class Base {
+ int b;
+ Base(int b) {
+ this.b = b;
+ }
+ }
+
+ private static class Sub extends Base {
+ int s;
+ Sub(int b, int s) {
+ super(b);
+ this.s = s;
+ }
+ }
+
+ private static class ClassWithBaseFields {
+ Base b;
+ ClassWithBaseFields(Base b) {
+ this.b = b;
+ }
+ }
+
+ private static class ClassWithContainersOfBaseFields {
+ Collection<Base> collection;
+ Map<String, Base> map;
+ ClassWithContainersOfBaseFields(Collection<Base> collection, Map<String, Base> map) {
+ this.collection = collection;
+ this.map = map;
+ }
+ }
+
+ private static class ParameterizedBase<T> {
+ T t;
+ ParameterizedBase(T t) {
+ this.t = t;
+ }
+ }
+
+ private static class ParameterizedSub<T> extends ParameterizedBase<T> {
+ T s;
+ ParameterizedSub(T t, T s) {
+ super(t);
+ this.s = s;
+ }
+ }
+
+ private static class ClassWithParameterizedBaseFields {
+ ParameterizedBase<String> b;
+ ClassWithParameterizedBaseFields(ParameterizedBase<String> b) {
+ this.b = b;
+ }
+ }
+
+ private static class ClassWithContainersOfParameterizedBaseFields {
+ Collection<ParameterizedBase<String>> collection;
+ Map<String, ParameterizedBase<String>> map;
+ ClassWithContainersOfParameterizedBaseFields(Collection<ParameterizedBase<String>> collection,
+ Map<String, ParameterizedBase<String>> map) {
+ this.collection = collection;
+ this.map = map;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java b/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java
new file mode 100644
index 00000000..8975c155
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.FieldNamingPolicy;
+import com.google.gson.FieldNamingStrategy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.annotations.SerializedName;
+import com.google.gson.common.TestTypes.ClassWithSerializedNameFields;
+import com.google.gson.common.TestTypes.StringWrapper;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Field;
+
+/**
+ * Functional tests for naming policies.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class NamingPolicyTest extends TestCase {
+ private GsonBuilder builder;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ builder = new GsonBuilder();
+ }
+
+ public void testGsonWithNonDefaultFieldNamingPolicySerialization() {
+ Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
+ StringWrapper target = new StringWrapper("blah");
+ assertEquals("{\"SomeConstantStringInstanceField\":\""
+ + target.someConstantStringInstanceField + "\"}", gson.toJson(target));
+ }
+
+ public void testGsonWithNonDefaultFieldNamingPolicyDeserialiation() {
+ Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
+ String target = "{\"SomeConstantStringInstanceField\":\"someValue\"}";
+ StringWrapper deserializedObject = gson.fromJson(target, StringWrapper.class);
+ assertEquals("someValue", deserializedObject.someConstantStringInstanceField);
+ }
+
+ public void testGsonWithLowerCaseDashPolicySerialization() {
+ Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES).create();
+ StringWrapper target = new StringWrapper("blah");
+ assertEquals("{\"some-constant-string-instance-field\":\""
+ + target.someConstantStringInstanceField + "\"}", gson.toJson(target));
+ }
+
+ public void testGsonWithLowerCaseDashPolicyDeserialiation() {
+ Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES).create();
+ String target = "{\"some-constant-string-instance-field\":\"someValue\"}";
+ StringWrapper deserializedObject = gson.fromJson(target, StringWrapper.class);
+ assertEquals("someValue", deserializedObject.someConstantStringInstanceField);
+ }
+
+ public void testGsonWithLowerCaseUnderscorePolicySerialization() {
+ Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .create();
+ StringWrapper target = new StringWrapper("blah");
+ assertEquals("{\"some_constant_string_instance_field\":\""
+ + target.someConstantStringInstanceField + "\"}", gson.toJson(target));
+ }
+
+ public void testGsonWithLowerCaseUnderscorePolicyDeserialiation() {
+ Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .create();
+ String target = "{\"some_constant_string_instance_field\":\"someValue\"}";
+ StringWrapper deserializedObject = gson.fromJson(target, StringWrapper.class);
+ assertEquals("someValue", deserializedObject.someConstantStringInstanceField);
+ }
+
+ public void testGsonWithSerializedNameFieldNamingPolicySerialization() {
+ Gson gson = builder.create();
+ ClassWithSerializedNameFields expected = new ClassWithSerializedNameFields(5, 6);
+ String actual = gson.toJson(expected);
+ assertEquals(expected.getExpectedJson(), actual);
+ }
+
+ public void testGsonWithSerializedNameFieldNamingPolicyDeserialization() {
+ Gson gson = builder.create();
+ ClassWithSerializedNameFields expected = new ClassWithSerializedNameFields(5, 7);
+ ClassWithSerializedNameFields actual =
+ gson.fromJson(expected.getExpectedJson(), ClassWithSerializedNameFields.class);
+ assertEquals(expected.f, actual.f);
+ }
+
+ public void testGsonDuplicateNameUsingSerializedNameFieldNamingPolicySerialization() {
+ Gson gson = builder.create();
+ try {
+ ClassWithDuplicateFields target = new ClassWithDuplicateFields(10);
+ gson.toJson(target);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testGsonWithUpperCamelCaseSpacesPolicySerialiation() {
+ Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES)
+ .create();
+ StringWrapper target = new StringWrapper("blah");
+ assertEquals("{\"Some Constant String Instance Field\":\""
+ + target.someConstantStringInstanceField + "\"}", gson.toJson(target));
+ }
+
+ public void testGsonWithUpperCamelCaseSpacesPolicyDeserialiation() {
+ Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES)
+ .create();
+ String target = "{\"Some Constant String Instance Field\":\"someValue\"}";
+ StringWrapper deserializedObject = gson.fromJson(target, StringWrapper.class);
+ assertEquals("someValue", deserializedObject.someConstantStringInstanceField);
+ }
+
+ public void testDeprecatedNamingStrategy() throws Exception {
+ Gson gson = builder.setFieldNamingStrategy(new UpperCaseNamingStrategy()).create();
+ ClassWithDuplicateFields target = new ClassWithDuplicateFields(10);
+ String actual = gson.toJson(target);
+ assertEquals("{\"A\":10}", actual);
+ }
+
+ public void testComplexFieldNameStrategy() throws Exception {
+ Gson gson = new Gson();
+ String json = gson.toJson(new ClassWithComplexFieldName(10));
+ String escapedFieldName = "@value\\\"_s$\\\\";
+ assertEquals("{\"" + escapedFieldName + "\":10}", json);
+
+ ClassWithComplexFieldName obj = gson.fromJson(json, ClassWithComplexFieldName.class);
+ assertEquals(10, obj.value);
+ }
+
+ /** http://code.google.com/p/google-gson/issues/detail?id=349 */
+ public void testAtSignInSerializedName() {
+ assertEquals("{\"@foo\":\"bar\"}", new Gson().toJson(new AtName()));
+ }
+
+ static class AtName {
+ @SerializedName("@foo") String f = "bar";
+ }
+
+ private static class UpperCaseNamingStrategy implements FieldNamingStrategy {
+ public String translateName(Field f) {
+ return f.getName().toUpperCase();
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static class ClassWithDuplicateFields {
+ public Integer a;
+ @SerializedName("a") public Double b;
+
+ public ClassWithDuplicateFields(Integer a) {
+ this(a, null);
+ }
+
+ public ClassWithDuplicateFields(Double b) {
+ this(null, b);
+ }
+
+ public ClassWithDuplicateFields(Integer a, Double b) {
+ this.a = a;
+ this.b = b;
+ }
+ }
+
+ private static class ClassWithComplexFieldName {
+ @SerializedName("@value\"_s$\\") public final long value;
+
+ ClassWithComplexFieldName(long value) {
+ this.value = value;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java
new file mode 100755
index 00000000..742ee221
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java
@@ -0,0 +1,240 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.common.TestTypes.ClassWithObjects;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * Functional tests for the different cases for serializing (or ignoring) null fields and object.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class NullObjectAndFieldTest extends TestCase {
+ private GsonBuilder gsonBuilder;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gsonBuilder = new GsonBuilder().serializeNulls();
+ }
+
+ public void testTopLevelNullObjectSerialization() {
+ Gson gson = gsonBuilder.create();
+ String actual = gson.toJson(null);
+ assertEquals("null", actual);
+
+ actual = gson.toJson(null, String.class);
+ assertEquals("null", actual);
+ }
+
+ public void testTopLevelNullObjectDeserialization() throws Exception {
+ Gson gson = gsonBuilder.create();
+ String actual = gson.fromJson("null", String.class);
+ assertNull(actual);
+ }
+
+ public void testExplicitSerializationOfNulls() {
+ Gson gson = gsonBuilder.create();
+ ClassWithObjects target = new ClassWithObjects(null);
+ String actual = gson.toJson(target);
+ String expected = "{\"bag\":null}";
+ assertEquals(expected, actual);
+ }
+
+ public void testExplicitDeserializationOfNulls() throws Exception {
+ Gson gson = gsonBuilder.create();
+ ClassWithObjects target = gson.fromJson("{\"bag\":null}", ClassWithObjects.class);
+ assertNull(target.bag);
+ }
+
+ public void testExplicitSerializationOfNullArrayMembers() {
+ Gson gson = gsonBuilder.create();
+ ClassWithMembers target = new ClassWithMembers();
+ String json = gson.toJson(target);
+ assertTrue(json.contains("\"array\":null"));
+ }
+
+ /**
+ * Added to verify http://code.google.com/p/google-gson/issues/detail?id=68
+ */
+ public void testNullWrappedPrimitiveMemberSerialization() {
+ Gson gson = gsonBuilder.serializeNulls().create();
+ ClassWithNullWrappedPrimitive target = new ClassWithNullWrappedPrimitive();
+ String json = gson.toJson(target);
+ assertTrue(json.contains("\"value\":null"));
+ }
+
+ /**
+ * Added to verify http://code.google.com/p/google-gson/issues/detail?id=68
+ */
+ public void testNullWrappedPrimitiveMemberDeserialization() {
+ Gson gson = gsonBuilder.create();
+ String json = "{'value':null}";
+ ClassWithNullWrappedPrimitive target = gson.fromJson(json, ClassWithNullWrappedPrimitive.class);
+ assertNull(target.value);
+ }
+
+ public void testExplicitSerializationOfNullCollectionMembers() {
+ Gson gson = gsonBuilder.create();
+ ClassWithMembers target = new ClassWithMembers();
+ String json = gson.toJson(target);
+ assertTrue(json.contains("\"col\":null"));
+ }
+
+ public void testExplicitSerializationOfNullStringMembers() {
+ Gson gson = gsonBuilder.create();
+ ClassWithMembers target = new ClassWithMembers();
+ String json = gson.toJson(target);
+ assertTrue(json.contains("\"str\":null"));
+ }
+
+ public void testCustomSerializationOfNulls() {
+ gsonBuilder.registerTypeAdapter(ClassWithObjects.class, new ClassWithObjectsSerializer());
+ Gson gson = gsonBuilder.create();
+ ClassWithObjects target = new ClassWithObjects(new BagOfPrimitives());
+ String actual = gson.toJson(target);
+ String expected = "{\"bag\":null}";
+ assertEquals(expected, actual);
+ }
+
+ public void testPrintPrintingObjectWithNulls() throws Exception {
+ gsonBuilder = new GsonBuilder();
+ Gson gson = gsonBuilder.create();
+ String result = gson.toJson(new ClassWithMembers());
+ assertEquals("{}", result);
+
+ gson = gsonBuilder.serializeNulls().create();
+ result = gson.toJson(new ClassWithMembers());
+ assertTrue(result.contains("\"str\":null"));
+ }
+
+ public void testPrintPrintingArraysWithNulls() throws Exception {
+ gsonBuilder = new GsonBuilder();
+ Gson gson = gsonBuilder.create();
+ String result = gson.toJson(new String[] { "1", null, "3" });
+ assertEquals("[\"1\",null,\"3\"]", result);
+
+ gson = gsonBuilder.serializeNulls().create();
+ result = gson.toJson(new String[] { "1", null, "3" });
+ assertEquals("[\"1\",null,\"3\"]", result);
+ }
+
+ // test for issue 389
+ public void testAbsentJsonElementsAreSetToNull() {
+ Gson gson = new Gson();
+ ClassWithInitializedMembers target =
+ gson.fromJson("{array:[1,2,3]}", ClassWithInitializedMembers.class);
+ assertTrue(target.array.length == 3 && target.array[1] == 2);
+ assertEquals(ClassWithInitializedMembers.MY_STRING_DEFAULT, target.str1);
+ assertNull(target.str2);
+ assertEquals(ClassWithInitializedMembers.MY_INT_DEFAULT, target.int1);
+ assertEquals(0, target.int2); // test the default value of a primitive int field per JVM spec
+ assertEquals(ClassWithInitializedMembers.MY_BOOLEAN_DEFAULT, target.bool1);
+ assertFalse(target.bool2); // test the default value of a primitive boolean field per JVM spec
+ }
+
+ public static class ClassWithInitializedMembers {
+ // Using a mix of no-args constructor and field initializers
+ // Also, some fields are intialized and some are not (so initialized per JVM spec)
+ public static final String MY_STRING_DEFAULT = "string";
+ private static final int MY_INT_DEFAULT = 2;
+ private static final boolean MY_BOOLEAN_DEFAULT = true;
+ int[] array;
+ String str1, str2;
+ int int1 = MY_INT_DEFAULT;
+ int int2;
+ boolean bool1 = MY_BOOLEAN_DEFAULT;
+ boolean bool2;
+ public ClassWithInitializedMembers() {
+ str1 = MY_STRING_DEFAULT;
+ }
+ }
+
+ private static class ClassWithNullWrappedPrimitive {
+ private Long value;
+ }
+
+ @SuppressWarnings("unused")
+ private static class ClassWithMembers {
+ String str;
+ int[] array;
+ Collection<String> col;
+ }
+
+ private static class ClassWithObjectsSerializer implements JsonSerializer<ClassWithObjects> {
+ public JsonElement serialize(ClassWithObjects src, Type typeOfSrc,
+ JsonSerializationContext context) {
+ JsonObject obj = new JsonObject();
+ obj.add("bag", JsonNull.INSTANCE);
+ return obj;
+ }
+ }
+
+ public void testExplicitNullSetsFieldToNullDuringDeserialization() {
+ Gson gson = new Gson();
+ String json = "{value:null}";
+ ObjectWithField obj = gson.fromJson(json, ObjectWithField.class);
+ assertNull(obj.value);
+ }
+
+ public void testCustomTypeAdapterPassesNullSerialization() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(ObjectWithField.class, new JsonSerializer<ObjectWithField>() {
+ public JsonElement serialize(ObjectWithField src, Type typeOfSrc,
+ JsonSerializationContext context) {
+ return context.serialize(null);
+ }
+ }).create();
+ ObjectWithField target = new ObjectWithField();
+ target.value = "value1";
+ String json = gson.toJson(target);
+ assertFalse(json.contains("value1"));
+ }
+
+ public void testCustomTypeAdapterPassesNullDesrialization() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(ObjectWithField.class, new JsonDeserializer<ObjectWithField>() {
+ public ObjectWithField deserialize(JsonElement json, Type type,
+ JsonDeserializationContext context) {
+ return context.deserialize(null, type);
+ }
+ }).create();
+ String json = "{value:'value1'}";
+ ObjectWithField target = gson.fromJson(json, ObjectWithField.class);
+ assertNull(target);
+ }
+
+ private static class ObjectWithField {
+ String value = "";
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/ObjectTest.java b/gson/src/test/java/com/google/gson/functional/ObjectTest.java
new file mode 100644
index 00000000..de1219a6
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/ObjectTest.java
@@ -0,0 +1,501 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.InstanceCreator;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.common.TestTypes.ArrayOfObjects;
+import com.google.gson.common.TestTypes.BagOfPrimitiveWrappers;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.common.TestTypes.ClassWithArray;
+import com.google.gson.common.TestTypes.ClassWithNoFields;
+import com.google.gson.common.TestTypes.ClassWithObjects;
+import com.google.gson.common.TestTypes.ClassWithTransientFields;
+import com.google.gson.common.TestTypes.Nested;
+import com.google.gson.common.TestTypes.PrimitiveArray;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import junit.framework.TestCase;
+
+/**
+ * Functional tests for Json serialization and deserialization of regular classes.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class ObjectTest extends TestCase {
+ private Gson gson;
+ private TimeZone oldTimeZone = TimeZone.getDefault();
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+
+ TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
+ Locale.setDefault(Locale.US);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ TimeZone.setDefault(oldTimeZone);
+ super.tearDown();
+ }
+ public void testJsonInSingleQuotesDeserialization() {
+ String json = "{'stringValue':'no message','intValue':10,'longValue':20}";
+ BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals("no message", target.stringValue);
+ assertEquals(10, target.intValue);
+ assertEquals(20, target.longValue);
+ }
+
+ public void testJsonInMixedQuotesDeserialization() {
+ String json = "{\"stringValue\":'no message','intValue':10,'longValue':20}";
+ BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals("no message", target.stringValue);
+ assertEquals(10, target.intValue);
+ assertEquals(20, target.longValue);
+ }
+
+ public void testBagOfPrimitivesSerialization() throws Exception {
+ BagOfPrimitives target = new BagOfPrimitives(10, 20, false, "stringValue");
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testBagOfPrimitivesDeserialization() throws Exception {
+ BagOfPrimitives src = new BagOfPrimitives(10, 20, false, "stringValue");
+ String json = src.getExpectedJson();
+ BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals(json, target.getExpectedJson());
+ }
+
+ public void testBagOfPrimitiveWrappersSerialization() throws Exception {
+ BagOfPrimitiveWrappers target = new BagOfPrimitiveWrappers(10L, 20, false);
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testBagOfPrimitiveWrappersDeserialization() throws Exception {
+ BagOfPrimitiveWrappers target = new BagOfPrimitiveWrappers(10L, 20, false);
+ String jsonString = target.getExpectedJson();
+ target = gson.fromJson(jsonString, BagOfPrimitiveWrappers.class);
+ assertEquals(jsonString, target.getExpectedJson());
+ }
+
+ public void testClassWithTransientFieldsSerialization() throws Exception {
+ ClassWithTransientFields<Long> target = new ClassWithTransientFields<Long>(1L);
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void testClassWithTransientFieldsDeserialization() throws Exception {
+ String json = "{\"longValue\":[1]}";
+ ClassWithTransientFields target = gson.fromJson(json, ClassWithTransientFields.class);
+ assertEquals(json, target.getExpectedJson());
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void testClassWithTransientFieldsDeserializationTransientFieldsPassedInJsonAreIgnored()
+ throws Exception {
+ String json = "{\"transientLongValue\":1,\"longValue\":[1]}";
+ ClassWithTransientFields target = gson.fromJson(json, ClassWithTransientFields.class);
+ assertFalse(target.transientLongValue != 1);
+ }
+
+ public void testClassWithNoFieldsSerialization() throws Exception {
+ assertEquals("{}", gson.toJson(new ClassWithNoFields()));
+ }
+
+ public void testClassWithNoFieldsDeserialization() throws Exception {
+ String json = "{}";
+ ClassWithNoFields target = gson.fromJson(json, ClassWithNoFields.class);
+ ClassWithNoFields expected = new ClassWithNoFields();
+ assertEquals(expected, target);
+ }
+
+ public void testNestedSerialization() throws Exception {
+ Nested target = new Nested(new BagOfPrimitives(10, 20, false, "stringValue"),
+ new BagOfPrimitives(30, 40, true, "stringValue"));
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testNestedDeserialization() throws Exception {
+ String json = "{\"primitive1\":{\"longValue\":10,\"intValue\":20,\"booleanValue\":false,"
+ + "\"stringValue\":\"stringValue\"},\"primitive2\":{\"longValue\":30,\"intValue\":40,"
+ + "\"booleanValue\":true,\"stringValue\":\"stringValue\"}}";
+ Nested target = gson.fromJson(json, Nested.class);
+ assertEquals(json, target.getExpectedJson());
+ }
+ public void testNullSerialization() throws Exception {
+ assertEquals("null", gson.toJson(null));
+ }
+
+ public void testEmptyStringDeserialization() throws Exception {
+ Object object = gson.fromJson("", Object.class);
+ assertNull(object);
+ }
+
+ public void testTruncatedDeserialization() {
+ try {
+ gson.fromJson("[\"a\", \"b\",", new TypeToken<List<String>>() {}.getType());
+ fail();
+ } catch (JsonParseException expected) {
+ }
+ }
+
+ public void testNullDeserialization() throws Exception {
+ String myNullObject = null;
+ Object object = gson.fromJson(myNullObject, Object.class);
+ assertNull(object);
+ }
+
+ public void testNullFieldsSerialization() throws Exception {
+ Nested target = new Nested(new BagOfPrimitives(10, 20, false, "stringValue"), null);
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testNullFieldsDeserialization() throws Exception {
+ String json = "{\"primitive1\":{\"longValue\":10,\"intValue\":20,\"booleanValue\":false"
+ + ",\"stringValue\":\"stringValue\"}}";
+ Nested target = gson.fromJson(json, Nested.class);
+ assertEquals(json, target.getExpectedJson());
+ }
+
+ public void testArrayOfObjectsSerialization() throws Exception {
+ ArrayOfObjects target = new ArrayOfObjects();
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testArrayOfObjectsDeserialization() throws Exception {
+ String json = new ArrayOfObjects().getExpectedJson();
+ ArrayOfObjects target = gson.fromJson(json, ArrayOfObjects.class);
+ assertEquals(json, target.getExpectedJson());
+ }
+
+ public void testArrayOfArraysSerialization() throws Exception {
+ ArrayOfArrays target = new ArrayOfArrays();
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testArrayOfArraysDeserialization() throws Exception {
+ String json = new ArrayOfArrays().getExpectedJson();
+ ArrayOfArrays target = gson.fromJson(json, ArrayOfArrays.class);
+ assertEquals(json, target.getExpectedJson());
+ }
+
+ public void testArrayOfObjectsAsFields() throws Exception {
+ ClassWithObjects classWithObjects = new ClassWithObjects();
+ BagOfPrimitives bagOfPrimitives = new BagOfPrimitives();
+ String stringValue = "someStringValueInArray";
+ String classWithObjectsJson = gson.toJson(classWithObjects);
+ String bagOfPrimitivesJson = gson.toJson(bagOfPrimitives);
+
+ ClassWithArray classWithArray = new ClassWithArray(
+ new Object[] { stringValue, classWithObjects, bagOfPrimitives });
+ String json = gson.toJson(classWithArray);
+
+ assertTrue(json.contains(classWithObjectsJson));
+ assertTrue(json.contains(bagOfPrimitivesJson));
+ assertTrue(json.contains("\"" + stringValue + "\""));
+ }
+
+ /**
+ * Created in response to Issue 14: http://code.google.com/p/google-gson/issues/detail?id=14
+ */
+ public void testNullArraysDeserialization() throws Exception {
+ String json = "{\"array\": null}";
+ ClassWithArray target = gson.fromJson(json, ClassWithArray.class);
+ assertNull(target.array);
+ }
+
+ /**
+ * Created in response to Issue 14: http://code.google.com/p/google-gson/issues/detail?id=14
+ */
+ public void testNullObjectFieldsDeserialization() throws Exception {
+ String json = "{\"bag\": null}";
+ ClassWithObjects target = gson.fromJson(json, ClassWithObjects.class);
+ assertNull(target.bag);
+ }
+
+ public void testEmptyCollectionInAnObjectDeserialization() throws Exception {
+ String json = "{\"children\":[]}";
+ ClassWithCollectionField target = gson.fromJson(json, ClassWithCollectionField.class);
+ assertNotNull(target);
+ assertTrue(target.children.isEmpty());
+ }
+
+ private static class ClassWithCollectionField {
+ Collection<String> children = new ArrayList<String>();
+ }
+
+ public void testPrimitiveArrayInAnObjectDeserialization() throws Exception {
+ String json = "{\"longArray\":[0,1,2,3,4,5,6,7,8,9]}";
+ PrimitiveArray target = gson.fromJson(json, PrimitiveArray.class);
+ assertEquals(json, target.getExpectedJson());
+ }
+
+ /**
+ * Created in response to Issue 14: http://code.google.com/p/google-gson/issues/detail?id=14
+ */
+ public void testNullPrimitiveFieldsDeserialization() throws Exception {
+ String json = "{\"longValue\":null}";
+ BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals(BagOfPrimitives.DEFAULT_VALUE, target.longValue);
+ }
+
+ public void testEmptyCollectionInAnObjectSerialization() throws Exception {
+ ClassWithCollectionField target = new ClassWithCollectionField();
+ assertEquals("{\"children\":[]}", gson.toJson(target));
+ }
+
+ public void testPrivateNoArgConstructorDeserialization() throws Exception {
+ ClassWithPrivateNoArgsConstructor target =
+ gson.fromJson("{\"a\":20}", ClassWithPrivateNoArgsConstructor.class);
+ assertEquals(20, target.a);
+ }
+
+ public void testAnonymousLocalClassesSerialization() throws Exception {
+ assertEquals("null", gson.toJson(new ClassWithNoFields() {
+ // empty anonymous class
+ }));
+ }
+
+ public void testAnonymousLocalClassesCustomSerialization() throws Exception {
+ gson = new GsonBuilder()
+ .registerTypeHierarchyAdapter(ClassWithNoFields.class,
+ new JsonSerializer<ClassWithNoFields>() {
+ public JsonElement serialize(
+ ClassWithNoFields src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonObject();
+ }
+ }).create();
+
+ assertEquals("null", gson.toJson(new ClassWithNoFields() {
+ // empty anonymous class
+ }));
+ }
+
+ public void testPrimitiveArrayFieldSerialization() {
+ PrimitiveArray target = new PrimitiveArray(new long[] { 1L, 2L, 3L });
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ /**
+ * Tests that a class field with type Object can be serialized properly.
+ * See issue 54
+ */
+ public void testClassWithObjectFieldSerialization() {
+ ClassWithObjectField obj = new ClassWithObjectField();
+ obj.member = "abc";
+ String json = gson.toJson(obj);
+ assertTrue(json.contains("abc"));
+ }
+
+ private static class ClassWithObjectField {
+ @SuppressWarnings("unused")
+ Object member;
+ }
+
+ public void testInnerClassSerialization() {
+ Parent p = new Parent();
+ Parent.Child c = p.new Child();
+ String json = gson.toJson(c);
+ assertTrue(json.contains("value2"));
+ assertFalse(json.contains("value1"));
+ }
+
+ public void testInnerClassDeserialization() {
+ final Parent p = new Parent();
+ Gson gson = new GsonBuilder().registerTypeAdapter(
+ Parent.Child.class, new InstanceCreator<Parent.Child>() {
+ public Parent.Child createInstance(Type type) {
+ return p.new Child();
+ }
+ }).create();
+ String json = "{'value2':3}";
+ Parent.Child c = gson.fromJson(json, Parent.Child.class);
+ assertEquals(3, c.value2);
+ }
+
+ private static class Parent {
+ @SuppressWarnings("unused")
+ int value1 = 1;
+ private class Child {
+ int value2 = 2;
+ }
+ }
+
+ private static class ArrayOfArrays {
+ private final BagOfPrimitives[][] elements;
+ public ArrayOfArrays() {
+ elements = new BagOfPrimitives[3][2];
+ for (int i = 0; i < elements.length; ++i) {
+ BagOfPrimitives[] row = elements[i];
+ for (int j = 0; j < row.length; ++j) {
+ row[j] = new BagOfPrimitives(i+j, i*j, false, i+"_"+j);
+ }
+ }
+ }
+ public String getExpectedJson() {
+ StringBuilder sb = new StringBuilder("{\"elements\":[");
+ boolean first = true;
+ for (BagOfPrimitives[] row : elements) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ boolean firstOfRow = true;
+ sb.append("[");
+ for (BagOfPrimitives element : row) {
+ if (firstOfRow) {
+ firstOfRow = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(element.getExpectedJson());
+ }
+ sb.append("]");
+ }
+ sb.append("]}");
+ return sb.toString();
+ }
+ }
+
+ private static class ClassWithPrivateNoArgsConstructor {
+ public int a;
+ private ClassWithPrivateNoArgsConstructor() {
+ a = 10;
+ }
+ }
+
+ /**
+ * In response to Issue 41 http://code.google.com/p/google-gson/issues/detail?id=41
+ */
+ public void testObjectFieldNamesWithoutQuotesDeserialization() {
+ String json = "{longValue:1,'booleanValue':true,\"stringValue\":'bar'}";
+ BagOfPrimitives bag = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals(1, bag.longValue);
+ assertTrue(bag.booleanValue);
+ assertEquals("bar", bag.stringValue);
+ }
+
+ public void testStringFieldWithNumberValueDeserialization() {
+ String json = "{\"stringValue\":1}";
+ BagOfPrimitives bag = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals("1", bag.stringValue);
+
+ json = "{\"stringValue\":1.5E+6}";
+ bag = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals("1.5E+6", bag.stringValue);
+
+ json = "{\"stringValue\":true}";
+ bag = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals("true", bag.stringValue);
+ }
+
+ /**
+ * Created to reproduce issue 140
+ */
+ public void testStringFieldWithEmptyValueSerialization() {
+ ClassWithEmptyStringFields target = new ClassWithEmptyStringFields();
+ target.a = "5794749";
+ String json = gson.toJson(target);
+ assertTrue(json.contains("\"a\":\"5794749\""));
+ assertTrue(json.contains("\"b\":\"\""));
+ assertTrue(json.contains("\"c\":\"\""));
+ }
+
+ /**
+ * Created to reproduce issue 140
+ */
+ public void testStringFieldWithEmptyValueDeserialization() {
+ String json = "{a:\"5794749\",b:\"\",c:\"\"}";
+ ClassWithEmptyStringFields target = gson.fromJson(json, ClassWithEmptyStringFields.class);
+ assertEquals("5794749", target.a);
+ assertEquals("", target.b);
+ assertEquals("", target.c);
+ }
+
+ private static class ClassWithEmptyStringFields {
+ String a = "";
+ String b = "";
+ String c = "";
+ }
+
+ public void testJsonObjectSerialization() {
+ Gson gson = new GsonBuilder().serializeNulls().create();
+ JsonObject obj = new JsonObject();
+ String json = gson.toJson(obj);
+ assertEquals("{}", json);
+ }
+
+ /**
+ * Test for issue 215.
+ */
+ public void testSingletonLists() {
+ Gson gson = new Gson();
+ Product product = new Product();
+ assertEquals("{\"attributes\":[],\"departments\":[]}",
+ gson.toJson(product));
+ gson.fromJson(gson.toJson(product), Product.class);
+
+ product.departments.add(new Department());
+ assertEquals("{\"attributes\":[],\"departments\":[{\"name\":\"abc\",\"code\":\"123\"}]}",
+ gson.toJson(product));
+ gson.fromJson(gson.toJson(product), Product.class);
+
+ product.attributes.add("456");
+ assertEquals("{\"attributes\":[\"456\"],\"departments\":[{\"name\":\"abc\",\"code\":\"123\"}]}",
+ gson.toJson(product));
+ gson.fromJson(gson.toJson(product), Product.class);
+ }
+
+ // http://code.google.com/p/google-gson/issues/detail?id=270
+ public void testDateAsMapObjectField() {
+ HasObjectMap a = new HasObjectMap();
+ a.map.put("date", new Date(0));
+ assertEquals("{\"map\":{\"date\":\"Dec 31, 1969 4:00:00 PM\"}}", gson.toJson(a));
+ }
+
+ public class HasObjectMap {
+ Map<String, Object> map = new HashMap<String, Object>();
+ }
+
+ static final class Department {
+ public String name = "abc";
+ public String code = "123";
+ }
+
+ static final class Product {
+ private List<String> attributes = new ArrayList<String>();
+ private List<Department> departments = new ArrayList<Department>();
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java b/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java
new file mode 100644
index 00000000..190603de
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java
@@ -0,0 +1,503 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.ParameterizedTypeFixtures.MyParameterizedType;
+import com.google.gson.ParameterizedTypeFixtures.MyParameterizedTypeAdapter;
+import com.google.gson.ParameterizedTypeFixtures.MyParameterizedTypeInstanceCreator;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.reflect.TypeToken;
+
+import junit.framework.TestCase;
+
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Functional tests for the serialization and deserialization of parameterized types in Gson.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class ParameterizedTypesTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testParameterizedTypesSerialization() throws Exception {
+ MyParameterizedType<Integer> src = new MyParameterizedType<Integer>(10);
+ Type typeOfSrc = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
+ String json = gson.toJson(src, typeOfSrc);
+ assertEquals(src.getExpectedJson(), json);
+ }
+
+ public void testParameterizedTypeDeserialization() throws Exception {
+ BagOfPrimitives bag = new BagOfPrimitives();
+ MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<BagOfPrimitives>(bag);
+ Type expectedType = new TypeToken<MyParameterizedType<BagOfPrimitives>>() {}.getType();
+ BagOfPrimitives bagDefaultInstance = new BagOfPrimitives();
+ Gson gson = new GsonBuilder().registerTypeAdapter(
+ expectedType, new MyParameterizedTypeInstanceCreator<BagOfPrimitives>(bagDefaultInstance))
+ .create();
+
+ String json = expected.getExpectedJson();
+ MyParameterizedType<BagOfPrimitives> actual = gson.fromJson(json, expectedType);
+ assertEquals(expected, actual);
+ }
+
+ public void testTypesWithMultipleParametersSerialization() throws Exception {
+ MultiParameters<Integer, Float, Double, String, BagOfPrimitives> src =
+ new MultiParameters<Integer, Float, Double, String, BagOfPrimitives>(10, 1.0F, 2.1D,
+ "abc", new BagOfPrimitives());
+ Type typeOfSrc = new TypeToken<MultiParameters<Integer, Float, Double, String,
+ BagOfPrimitives>>() {}.getType();
+ String json = gson.toJson(src, typeOfSrc);
+ String expected = "{\"a\":10,\"b\":1.0,\"c\":2.1,\"d\":\"abc\","
+ + "\"e\":{\"longValue\":0,\"intValue\":0,\"booleanValue\":false,\"stringValue\":\"\"}}";
+ assertEquals(expected, json);
+ }
+
+ public void testTypesWithMultipleParametersDeserialization() throws Exception {
+ Type typeOfTarget = new TypeToken<MultiParameters<Integer, Float, Double, String,
+ BagOfPrimitives>>() {}.getType();
+ String json = "{\"a\":10,\"b\":1.0,\"c\":2.1,\"d\":\"abc\","
+ + "\"e\":{\"longValue\":0,\"intValue\":0,\"booleanValue\":false,\"stringValue\":\"\"}}";
+ MultiParameters<Integer, Float, Double, String, BagOfPrimitives> target =
+ gson.fromJson(json, typeOfTarget);
+ MultiParameters<Integer, Float, Double, String, BagOfPrimitives> expected =
+ new MultiParameters<Integer, Float, Double, String, BagOfPrimitives>(10, 1.0F, 2.1D,
+ "abc", new BagOfPrimitives());
+ assertEquals(expected, target);
+ }
+
+ public void testParameterizedTypeWithCustomSerializer() {
+ Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
+ Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(ptIntegerType, new MyParameterizedTypeAdapter<Integer>())
+ .registerTypeAdapter(ptStringType, new MyParameterizedTypeAdapter<String>())
+ .create();
+ MyParameterizedType<Integer> intTarget = new MyParameterizedType<Integer>(10);
+ String json = gson.toJson(intTarget, ptIntegerType);
+ assertEquals(MyParameterizedTypeAdapter.<Integer>getExpectedJson(intTarget), json);
+
+ MyParameterizedType<String> stringTarget = new MyParameterizedType<String>("abc");
+ json = gson.toJson(stringTarget, ptStringType);
+ assertEquals(MyParameterizedTypeAdapter.<String>getExpectedJson(stringTarget), json);
+ }
+
+ public void testParameterizedTypesWithCustomDeserializer() {
+ Type ptIntegerType = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
+ Type ptStringType = new TypeToken<MyParameterizedType<String>>() {}.getType();
+ Gson gson = new GsonBuilder().registerTypeAdapter(
+ ptIntegerType, new MyParameterizedTypeAdapter<Integer>())
+ .registerTypeAdapter(ptStringType, new MyParameterizedTypeAdapter<String>())
+ .registerTypeAdapter(ptStringType, new MyParameterizedTypeInstanceCreator<String>(""))
+ .registerTypeAdapter(ptIntegerType,
+ new MyParameterizedTypeInstanceCreator<Integer>(new Integer(0)))
+ .create();
+
+ MyParameterizedType<Integer> src = new MyParameterizedType<Integer>(10);
+ String json = MyParameterizedTypeAdapter.<Integer>getExpectedJson(src);
+ MyParameterizedType<Integer> intTarget = gson.fromJson(json, ptIntegerType);
+ assertEquals(10, intTarget.value.intValue());
+
+ MyParameterizedType<String> srcStr = new MyParameterizedType<String>("abc");
+ json = MyParameterizedTypeAdapter.<String>getExpectedJson(srcStr);
+ MyParameterizedType<String> stringTarget = gson.fromJson(json, ptStringType);
+ assertEquals("abc", stringTarget.value);
+ }
+
+ public void testParameterizedTypesWithWriterSerialization() throws Exception {
+ Writer writer = new StringWriter();
+ MyParameterizedType<Integer> src = new MyParameterizedType<Integer>(10);
+ Type typeOfSrc = new TypeToken<MyParameterizedType<Integer>>() {}.getType();
+ gson.toJson(src, typeOfSrc, writer);
+ assertEquals(src.getExpectedJson(), writer.toString());
+ }
+
+ public void testParameterizedTypeWithReaderDeserialization() throws Exception {
+ BagOfPrimitives bag = new BagOfPrimitives();
+ MyParameterizedType<BagOfPrimitives> expected = new MyParameterizedType<BagOfPrimitives>(bag);
+ Type expectedType = new TypeToken<MyParameterizedType<BagOfPrimitives>>() {}.getType();
+ BagOfPrimitives bagDefaultInstance = new BagOfPrimitives();
+ Gson gson = new GsonBuilder().registerTypeAdapter(
+ expectedType, new MyParameterizedTypeInstanceCreator<BagOfPrimitives>(bagDefaultInstance))
+ .create();
+
+ Reader json = new StringReader(expected.getExpectedJson());
+ MyParameterizedType<Integer> actual = gson.fromJson(json, expectedType);
+ assertEquals(expected, actual);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testVariableTypeFieldsAndGenericArraysSerialization() throws Exception {
+ Integer obj = 0;
+ Integer[] array = { 1, 2, 3 };
+ List<Integer> list = new ArrayList<Integer>();
+ list.add(4);
+ list.add(5);
+ List<Integer>[] arrayOfLists = new List[] { list, list };
+
+ Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
+ ObjectWithTypeVariables<Integer> objToSerialize =
+ new ObjectWithTypeVariables<Integer>(obj, array, list, arrayOfLists, list, arrayOfLists);
+ String json = gson.toJson(objToSerialize, typeOfSrc);
+
+ assertEquals(objToSerialize.getExpectedJson(), json);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testVariableTypeFieldsAndGenericArraysDeserialization() throws Exception {
+ Integer obj = 0;
+ Integer[] array = { 1, 2, 3 };
+ List<Integer> list = new ArrayList<Integer>();
+ list.add(4);
+ list.add(5);
+ List<Integer>[] arrayOfLists = new List[] { list, list };
+
+ Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
+ ObjectWithTypeVariables<Integer> objToSerialize =
+ new ObjectWithTypeVariables<Integer>(obj, array, list, arrayOfLists, list, arrayOfLists);
+ String json = gson.toJson(objToSerialize, typeOfSrc);
+ ObjectWithTypeVariables<Integer> objAfterDeserialization = gson.fromJson(json, typeOfSrc);
+
+ assertEquals(objAfterDeserialization.getExpectedJson(), json);
+ }
+
+ public void testVariableTypeDeserialization() throws Exception {
+ Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
+ ObjectWithTypeVariables<Integer> objToSerialize =
+ new ObjectWithTypeVariables<Integer>(0, null, null, null, null, null);
+ String json = gson.toJson(objToSerialize, typeOfSrc);
+ ObjectWithTypeVariables<Integer> objAfterDeserialization = gson.fromJson(json, typeOfSrc);
+
+ assertEquals(objAfterDeserialization.getExpectedJson(), json);
+ }
+
+ public void testVariableTypeArrayDeserialization() throws Exception {
+ Integer[] array = { 1, 2, 3 };
+
+ Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
+ ObjectWithTypeVariables<Integer> objToSerialize =
+ new ObjectWithTypeVariables<Integer>(null, array, null, null, null, null);
+ String json = gson.toJson(objToSerialize, typeOfSrc);
+ ObjectWithTypeVariables<Integer> objAfterDeserialization = gson.fromJson(json, typeOfSrc);
+
+ assertEquals(objAfterDeserialization.getExpectedJson(), json);
+ }
+
+ public void testParameterizedTypeWithVariableTypeDeserialization() throws Exception {
+ List<Integer> list = new ArrayList<Integer>();
+ list.add(4);
+ list.add(5);
+
+ Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
+ ObjectWithTypeVariables<Integer> objToSerialize =
+ new ObjectWithTypeVariables<Integer>(null, null, list, null, null, null);
+ String json = gson.toJson(objToSerialize, typeOfSrc);
+ ObjectWithTypeVariables<Integer> objAfterDeserialization = gson.fromJson(json, typeOfSrc);
+
+ assertEquals(objAfterDeserialization.getExpectedJson(), json);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testParameterizedTypeGenericArraysSerialization() throws Exception {
+ List<Integer> list = new ArrayList<Integer>();
+ list.add(1);
+ list.add(2);
+ List<Integer>[] arrayOfLists = new List[] { list, list };
+
+ Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
+ ObjectWithTypeVariables<Integer> objToSerialize =
+ new ObjectWithTypeVariables<Integer>(null, null, null, arrayOfLists, null, null);
+ String json = gson.toJson(objToSerialize, typeOfSrc);
+ assertEquals("{\"arrayOfListOfTypeParameters\":[[1,2],[1,2]]}", json);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testParameterizedTypeGenericArraysDeserialization() throws Exception {
+ List<Integer> list = new ArrayList<Integer>();
+ list.add(1);
+ list.add(2);
+ List<Integer>[] arrayOfLists = new List[] { list, list };
+
+ Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
+ ObjectWithTypeVariables<Integer> objToSerialize =
+ new ObjectWithTypeVariables<Integer>(null, null, null, arrayOfLists, null, null);
+ String json = gson.toJson(objToSerialize, typeOfSrc);
+ ObjectWithTypeVariables<Integer> objAfterDeserialization = gson.fromJson(json, typeOfSrc);
+
+ assertEquals(objAfterDeserialization.getExpectedJson(), json);
+ }
+
+ /**
+ * An test object that has fields that are type variables.
+ *
+ * @param <T> Enforce T to be a string to make writing the "toExpectedJson" method easier.
+ */
+ private static class ObjectWithTypeVariables<T extends Number> {
+ private final T typeParameterObj;
+ private final T[] typeParameterArray;
+ private final List<T> listOfTypeParameters;
+ private final List<T>[] arrayOfListOfTypeParameters;
+ private final List<? extends T> listOfWildcardTypeParameters;
+ private final List<? extends T>[] arrayOfListOfWildcardTypeParameters;
+
+ // For use by Gson
+ @SuppressWarnings("unused")
+ private ObjectWithTypeVariables() {
+ this(null, null, null, null, null, null);
+ }
+
+ public ObjectWithTypeVariables(T obj, T[] array, List<T> list, List<T>[] arrayOfList,
+ List<? extends T> wildcardList, List<? extends T>[] arrayOfWildcardList) {
+ this.typeParameterObj = obj;
+ this.typeParameterArray = array;
+ this.listOfTypeParameters = list;
+ this.arrayOfListOfTypeParameters = arrayOfList;
+ this.listOfWildcardTypeParameters = wildcardList;
+ this.arrayOfListOfWildcardTypeParameters = arrayOfWildcardList;
+ }
+
+ public String getExpectedJson() {
+ StringBuilder sb = new StringBuilder().append("{");
+
+ boolean needsComma = false;
+ if (typeParameterObj != null) {
+ sb.append("\"typeParameterObj\":").append(toString(typeParameterObj));
+ needsComma = true;
+ }
+
+ if (typeParameterArray != null) {
+ if (needsComma) {
+ sb.append(',');
+ }
+ sb.append("\"typeParameterArray\":[");
+ appendObjectsToBuilder(sb, Arrays.asList(typeParameterArray));
+ sb.append(']');
+ needsComma = true;
+ }
+
+ if (listOfTypeParameters != null) {
+ if (needsComma) {
+ sb.append(',');
+ }
+ sb.append("\"listOfTypeParameters\":[");
+ appendObjectsToBuilder(sb, listOfTypeParameters);
+ sb.append(']');
+ needsComma = true;
+ }
+
+ if (arrayOfListOfTypeParameters != null) {
+ if (needsComma) {
+ sb.append(',');
+ }
+ sb.append("\"arrayOfListOfTypeParameters\":[");
+ appendObjectsToBuilder(sb, arrayOfListOfTypeParameters);
+ sb.append(']');
+ needsComma = true;
+ }
+
+ if (listOfWildcardTypeParameters != null) {
+ if (needsComma) {
+ sb.append(',');
+ }
+ sb.append("\"listOfWildcardTypeParameters\":[");
+ appendObjectsToBuilder(sb, listOfWildcardTypeParameters);
+ sb.append(']');
+ needsComma = true;
+ }
+
+ if (arrayOfListOfWildcardTypeParameters != null) {
+ if (needsComma) {
+ sb.append(',');
+ }
+ sb.append("\"arrayOfListOfWildcardTypeParameters\":[");
+ appendObjectsToBuilder(sb, arrayOfListOfWildcardTypeParameters);
+ sb.append(']');
+ needsComma = true;
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ private void appendObjectsToBuilder(StringBuilder sb, Iterable<? extends T> iterable) {
+ boolean isFirst = true;
+ for (T obj : iterable) {
+ if (!isFirst) {
+ sb.append(',');
+ }
+ isFirst = false;
+ sb.append(toString(obj));
+ }
+ }
+
+ private void appendObjectsToBuilder(StringBuilder sb, List<? extends T>[] arrayOfList) {
+ boolean isFirst = true;
+ for (List<? extends T> list : arrayOfList) {
+ if (!isFirst) {
+ sb.append(',');
+ }
+ isFirst = false;
+ if (list != null) {
+ sb.append('[');
+ appendObjectsToBuilder(sb, list);
+ sb.append(']');
+ } else {
+ sb.append("null");
+ }
+ }
+ }
+
+ public String toString(T obj) {
+ return obj.toString();
+ }
+ }
+
+ private static class MultiParameters<A, B, C, D, E> {
+ A a;
+ B b;
+ C c;
+ D d;
+ E e;
+ // For use by Gson
+ @SuppressWarnings("unused")
+ private MultiParameters() {
+ }
+ MultiParameters(A a, B b, C c, D d, E e) {
+ super();
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = d;
+ this.e = e;
+ }
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((a == null) ? 0 : a.hashCode());
+ result = prime * result + ((b == null) ? 0 : b.hashCode());
+ result = prime * result + ((c == null) ? 0 : c.hashCode());
+ result = prime * result + ((d == null) ? 0 : d.hashCode());
+ result = prime * result + ((e == null) ? 0 : e.hashCode());
+ return result;
+ }
+ @Override
+ @SuppressWarnings("unchecked")
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ MultiParameters<A, B, C, D, E> other = (MultiParameters<A, B, C, D, E>) obj;
+ if (a == null) {
+ if (other.a != null) {
+ return false;
+ }
+ } else if (!a.equals(other.a)) {
+ return false;
+ }
+ if (b == null) {
+ if (other.b != null) {
+ return false;
+ }
+ } else if (!b.equals(other.b)) {
+ return false;
+ }
+ if (c == null) {
+ if (other.c != null) {
+ return false;
+ }
+ } else if (!c.equals(other.c)) {
+ return false;
+ }
+ if (d == null) {
+ if (other.d != null) {
+ return false;
+ }
+ } else if (!d.equals(other.d)) {
+ return false;
+ }
+ if (e == null) {
+ if (other.e != null) {
+ return false;
+ }
+ } else if (!e.equals(other.e)) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ // Begin: tests to reproduce issue 103
+ private static class Quantity {
+ @SuppressWarnings("unused")
+ int q = 10;
+ }
+ private static class MyQuantity extends Quantity {
+ @SuppressWarnings("unused")
+ int q2 = 20;
+ }
+ private interface Measurable<T> {
+ }
+ private interface Field<T> {
+ }
+ private interface Immutable {
+ }
+
+ public static final class Amount<Q extends Quantity>
+ implements Measurable<Q>, Field<Amount<?>>, Serializable, Immutable {
+ private static final long serialVersionUID = -7560491093120970437L;
+
+ int value = 30;
+ }
+
+ public void testDeepParameterizedTypeSerialization() {
+ Amount<MyQuantity> amount = new Amount<MyQuantity>();
+ String json = gson.toJson(amount);
+ assertTrue(json.contains("value"));
+ assertTrue(json.contains("30"));
+ }
+
+ public void testDeepParameterizedTypeDeserialization() {
+ String json = "{value:30}";
+ Type type = new TypeToken<Amount<MyQuantity>>() {}.getType();
+ Amount<MyQuantity> amount = gson.fromJson(json, type);
+ assertEquals(30, amount.value);
+ }
+ // End: tests to reproduce issue 103
+}
diff --git a/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java b/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java
new file mode 100644
index 00000000..0aacc9e2
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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 java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.common.TestTypes.ArrayOfObjects;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Functional tests for pretty printing option.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class PrettyPrintingTest extends TestCase {
+
+ private static final boolean DEBUG = false;
+
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new GsonBuilder().setPrettyPrinting().create();
+ }
+
+ public void testPrettyPrintList() {
+ BagOfPrimitives b = new BagOfPrimitives();
+ List<BagOfPrimitives> listOfB = new LinkedList<BagOfPrimitives>();
+ for (int i = 0; i < 15; ++i) {
+ listOfB.add(b);
+ }
+ Type typeOfSrc = new TypeToken<List<BagOfPrimitives>>() {}.getType();
+ String json = gson.toJson(listOfB, typeOfSrc);
+ print(json);
+ }
+
+ public void testPrettyPrintArrayOfObjects() {
+ ArrayOfObjects target = new ArrayOfObjects();
+ String json = gson.toJson(target);
+ print(json);
+ }
+
+ public void testPrettyPrintArrayOfPrimitives() {
+ int[] ints = new int[] { 1, 2, 3, 4, 5 };
+ String json = gson.toJson(ints);
+ assertEquals("[\n 1,\n 2,\n 3,\n 4,\n 5\n]", json);
+ }
+
+ public void testPrettyPrintArrayOfPrimitiveArrays() {
+ int[][] ints = new int[][] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 },
+ { 9, 0 }, { 10 } };
+ String json = gson.toJson(ints);
+ assertEquals("[\n [\n 1,\n 2\n ],\n [\n 3,\n 4\n ],\n [\n 5,\n 6\n ],"
+ + "\n [\n 7,\n 8\n ],\n [\n 9,\n 0\n ],\n [\n 10\n ]\n]", json);
+ }
+
+ public void testPrettyPrintListOfPrimitiveArrays() {
+ List<Integer[]> list = Arrays.asList(new Integer[][] { { 1, 2 }, { 3, 4 },
+ { 5, 6 }, { 7, 8 }, { 9, 0 }, { 10 } });
+ String json = gson.toJson(list);
+ assertEquals("[\n [\n 1,\n 2\n ],\n [\n 3,\n 4\n ],\n [\n 5,\n 6\n ],"
+ + "\n [\n 7,\n 8\n ],\n [\n 9,\n 0\n ],\n [\n 10\n ]\n]", json);
+ }
+
+ public void testMap() {
+ Map<String, Integer> map = new LinkedHashMap<String, Integer>();
+ map.put("abc", 1);
+ map.put("def", 5);
+ String json = gson.toJson(map);
+ assertEquals("{\n \"abc\": 1,\n \"def\": 5\n}", json);
+ }
+
+ // In response to bug 153
+ public void testEmptyMapField() {
+ ClassWithMap obj = new ClassWithMap();
+ obj.map = new LinkedHashMap<String, Integer>();
+ String json = gson.toJson(obj);
+ assertTrue(json.contains("{\n \"map\": {},\n \"value\": 2\n}"));
+ }
+
+ @SuppressWarnings("unused")
+ private static class ClassWithMap {
+ Map<String, Integer> map;
+ int value = 2;
+ }
+
+ public void testMultipleArrays() {
+ int[][][] ints = new int[][][] { { { 1 }, { 2 } } };
+ String json = gson.toJson(ints);
+ assertEquals("[\n [\n [\n 1\n ],\n [\n 2\n ]\n ]\n]", json);
+ }
+
+ private void print(String msg) {
+ if (DEBUG) {
+ System.out.println(msg);
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java
new file mode 100644
index 00000000..69ff1f3f
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import com.google.gson.Gson;
+
+/**
+ * Functional tests for Java Character values.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class PrimitiveCharacterTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testPrimitiveCharacterAutoboxedSerialization() {
+ assertEquals("\"A\"", gson.toJson('A'));
+ assertEquals("\"A\"", gson.toJson('A', char.class));
+ assertEquals("\"A\"", gson.toJson('A', Character.class));
+ }
+
+ public void testPrimitiveCharacterAutoboxedDeserialization() {
+ char expected = 'a';
+ char actual = gson.fromJson("a", char.class);
+ assertEquals(expected, actual);
+
+ actual = gson.fromJson("\"a\"", char.class);
+ assertEquals(expected, actual);
+
+ actual = gson.fromJson("a", Character.class);
+ assertEquals(expected, actual);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java
new file mode 100644
index 00000000..bb28ed1e
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java
@@ -0,0 +1,821 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.LongSerializationPolicy;
+import com.google.gson.reflect.TypeToken;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
+import junit.framework.TestCase;
+
+/**
+ * Functional tests for Json primitive values: integers, and floating point numbers.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class PrimitiveTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testPrimitiveIntegerAutoboxedSerialization() {
+ assertEquals("1", gson.toJson(1));
+ }
+
+ public void testPrimitiveIntegerAutoboxedDeserialization() {
+ int expected = 1;
+ int actual = gson.fromJson("1", int.class);
+ assertEquals(expected, actual);
+
+ actual = gson.fromJson("1", Integer.class);
+ assertEquals(expected, actual);
+ }
+
+ public void testByteSerialization() {
+ assertEquals("1", gson.toJson(1, byte.class));
+ assertEquals("1", gson.toJson(1, Byte.class));
+ }
+
+ public void testShortSerialization() {
+ assertEquals("1", gson.toJson(1, short.class));
+ assertEquals("1", gson.toJson(1, Short.class));
+ }
+
+ public void testByteDeserialization() {
+ Byte target = gson.fromJson("1", Byte.class);
+ assertEquals(1, (byte)target);
+ byte primitive = gson.fromJson("1", byte.class);
+ assertEquals(1, primitive);
+ }
+
+ public void testPrimitiveIntegerAutoboxedInASingleElementArraySerialization() {
+ int target[] = {-9332};
+ assertEquals("[-9332]", gson.toJson(target));
+ assertEquals("[-9332]", gson.toJson(target, int[].class));
+ assertEquals("[-9332]", gson.toJson(target, Integer[].class));
+ }
+
+ public void testReallyLongValuesSerialization() {
+ long value = 333961828784581L;
+ assertEquals("333961828784581", gson.toJson(value));
+ }
+
+ public void testReallyLongValuesDeserialization() {
+ String json = "333961828784581";
+ long value = gson.fromJson(json, Long.class);
+ assertEquals(333961828784581L, value);
+ }
+
+ public void testPrimitiveLongAutoboxedSerialization() {
+ assertEquals("1", gson.toJson(1L, long.class));
+ assertEquals("1", gson.toJson(1L, Long.class));
+ }
+
+ public void testPrimitiveLongAutoboxedDeserialization() {
+ long expected = 1L;
+ long actual = gson.fromJson("1", long.class);
+ assertEquals(expected, actual);
+
+ actual = gson.fromJson("1", Long.class);
+ assertEquals(expected, actual);
+ }
+
+ public void testPrimitiveLongAutoboxedInASingleElementArraySerialization() {
+ long[] target = {-23L};
+ assertEquals("[-23]", gson.toJson(target));
+ assertEquals("[-23]", gson.toJson(target, long[].class));
+ assertEquals("[-23]", gson.toJson(target, Long[].class));
+ }
+
+ public void testPrimitiveBooleanAutoboxedSerialization() {
+ assertEquals("true", gson.toJson(true));
+ assertEquals("false", gson.toJson(false));
+ }
+
+ public void testBooleanDeserialization() {
+ boolean value = gson.fromJson("false", boolean.class);
+ assertEquals(false, value);
+ value = gson.fromJson("true", boolean.class);
+ assertEquals(true, value);
+ }
+
+ public void testPrimitiveBooleanAutoboxedInASingleElementArraySerialization() {
+ boolean target[] = {false};
+ assertEquals("[false]", gson.toJson(target));
+ assertEquals("[false]", gson.toJson(target, boolean[].class));
+ assertEquals("[false]", gson.toJson(target, Boolean[].class));
+ }
+
+ public void testNumberSerialization() {
+ Number expected = 1L;
+ String json = gson.toJson(expected);
+ assertEquals(expected.toString(), json);
+
+ json = gson.toJson(expected, Number.class);
+ assertEquals(expected.toString(), json);
+ }
+
+ public void testNumberDeserialization() {
+ String json = "1";
+ Number expected = new Integer(json);
+ Number actual = gson.fromJson(json, Number.class);
+ assertEquals(expected.intValue(), actual.intValue());
+
+ json = String.valueOf(Long.MAX_VALUE);
+ expected = new Long(json);
+ actual = gson.fromJson(json, Number.class);
+ assertEquals(expected.longValue(), actual.longValue());
+
+ json = "1.0";
+ actual = gson.fromJson(json, Number.class);
+ assertEquals(1L, actual.longValue());
+ }
+
+ public void testPrimitiveDoubleAutoboxedSerialization() {
+ assertEquals("-122.08234335", gson.toJson(-122.08234335));
+ assertEquals("122.08112002", gson.toJson(new Double(122.08112002)));
+ }
+
+ public void testPrimitiveDoubleAutoboxedDeserialization() {
+ double actual = gson.fromJson("-122.08858585", double.class);
+ assertEquals(-122.08858585, actual);
+
+ actual = gson.fromJson("122.023900008000", Double.class);
+ assertEquals(122.023900008, actual);
+ }
+
+ public void testPrimitiveDoubleAutoboxedInASingleElementArraySerialization() {
+ double[] target = {-122.08D};
+ assertEquals("[-122.08]", gson.toJson(target));
+ assertEquals("[-122.08]", gson.toJson(target, double[].class));
+ assertEquals("[-122.08]", gson.toJson(target, Double[].class));
+ }
+
+ public void testDoubleAsStringRepresentationDeserialization() {
+ String doubleValue = "1.0043E+5";
+ Double expected = Double.valueOf(doubleValue);
+ Double actual = gson.fromJson(doubleValue, Double.class);
+ assertEquals(expected, actual);
+
+ double actual1 = gson.fromJson(doubleValue, double.class);
+ assertEquals(expected.doubleValue(), actual1);
+ }
+
+ public void testDoubleNoFractAsStringRepresentationDeserialization() {
+ String doubleValue = "1E+5";
+ Double expected = Double.valueOf(doubleValue);
+ Double actual = gson.fromJson(doubleValue, Double.class);
+ assertEquals(expected, actual);
+
+ double actual1 = gson.fromJson(doubleValue, double.class);
+ assertEquals(expected.doubleValue(), actual1);
+ }
+
+ public void testDoubleArrayDeserialization() {
+ String json = "[0.0, 0.004761904761904762, 3.4013606962703525E-4, 7.936508173034305E-4,"
+ + "0.0011904761904761906, 0.0]";
+ double[] values = gson.fromJson(json, double[].class);
+ assertEquals(6, values.length);
+ assertEquals(0.0, values[0]);
+ assertEquals(0.004761904761904762, values[1]);
+ assertEquals(3.4013606962703525E-4, values[2]);
+ assertEquals(7.936508173034305E-4, values[3]);
+ assertEquals(0.0011904761904761906, values[4]);
+ assertEquals(0.0, values[5]);
+ }
+
+ public void testLargeDoubleDeserialization() {
+ String doubleValue = "1.234567899E8";
+ Double expected = Double.valueOf(doubleValue);
+ Double actual = gson.fromJson(doubleValue, Double.class);
+ assertEquals(expected, actual);
+
+ double actual1 = gson.fromJson(doubleValue, double.class);
+ assertEquals(expected.doubleValue(), actual1);
+ }
+
+ public void testBigDecimalSerialization() {
+ BigDecimal target = new BigDecimal("-122.0e-21");
+ String json = gson.toJson(target);
+ assertEquals(target, new BigDecimal(json));
+ }
+
+ public void testBigDecimalDeserialization() {
+ BigDecimal target = new BigDecimal("-122.0e-21");
+ String json = "-122.0e-21";
+ assertEquals(target, gson.fromJson(json, BigDecimal.class));
+ }
+
+ public void testBigDecimalInASingleElementArraySerialization() {
+ BigDecimal[] target = {new BigDecimal("-122.08e-21")};
+ String json = gson.toJson(target);
+ String actual = extractElementFromArray(json);
+ assertEquals(target[0], new BigDecimal(actual));
+
+ json = gson.toJson(target, BigDecimal[].class);
+ actual = extractElementFromArray(json);
+ assertEquals(target[0], new BigDecimal(actual));
+ }
+
+ public void testSmallValueForBigDecimalSerialization() {
+ BigDecimal target = new BigDecimal("1.55");
+ String actual = gson.toJson(target);
+ assertEquals(target.toString(), actual);
+ }
+
+ public void testSmallValueForBigDecimalDeserialization() {
+ BigDecimal expected = new BigDecimal("1.55");
+ BigDecimal actual = gson.fromJson("1.55", BigDecimal.class);
+ assertEquals(expected, actual);
+ }
+
+ public void testBigDecimalPreservePrecisionSerialization() {
+ String expectedValue = "1.000";
+ BigDecimal obj = new BigDecimal(expectedValue);
+ String actualValue = gson.toJson(obj);
+
+ assertEquals(expectedValue, actualValue);
+ }
+
+ public void testBigDecimalPreservePrecisionDeserialization() {
+ String json = "1.000";
+ BigDecimal expected = new BigDecimal(json);
+ BigDecimal actual = gson.fromJson(json, BigDecimal.class);
+
+ assertEquals(expected, actual);
+ }
+
+ public void testBigDecimalAsStringRepresentationDeserialization() {
+ String doubleValue = "0.05E+5";
+ BigDecimal expected = new BigDecimal(doubleValue);
+ BigDecimal actual = gson.fromJson(doubleValue, BigDecimal.class);
+ assertEquals(expected, actual);
+ }
+
+ public void testBigDecimalNoFractAsStringRepresentationDeserialization() {
+ String doubleValue = "5E+5";
+ BigDecimal expected = new BigDecimal(doubleValue);
+ BigDecimal actual = gson.fromJson(doubleValue, BigDecimal.class);
+ assertEquals(expected, actual);
+ }
+
+ public void testBigIntegerSerialization() {
+ BigInteger target = new BigInteger("12121211243123245845384534687435634558945453489543985435");
+ assertEquals(target.toString(), gson.toJson(target));
+ }
+
+ public void testBigIntegerDeserialization() {
+ String json = "12121211243123245845384534687435634558945453489543985435";
+ BigInteger target = new BigInteger(json);
+ assertEquals(target, gson.fromJson(json, BigInteger.class));
+ }
+
+ public void testBigIntegerInASingleElementArraySerialization() {
+ BigInteger[] target = {new BigInteger("1212121243434324323254365345367456456456465464564564")};
+ String json = gson.toJson(target);
+ String actual = extractElementFromArray(json);
+ assertEquals(target[0], new BigInteger(actual));
+
+ json = gson.toJson(target, BigInteger[].class);
+ actual = extractElementFromArray(json);
+ assertEquals(target[0], new BigInteger(actual));
+ }
+
+ public void testSmallValueForBigIntegerSerialization() {
+ BigInteger target = new BigInteger("15");
+ String actual = gson.toJson(target);
+ assertEquals(target.toString(), actual);
+ }
+
+ public void testSmallValueForBigIntegerDeserialization() {
+ BigInteger expected = new BigInteger("15");
+ BigInteger actual = gson.fromJson("15", BigInteger.class);
+ assertEquals(expected, actual);
+ }
+
+ public void testBadValueForBigIntegerDeserialization() {
+ try {
+ gson.fromJson("15.099", BigInteger.class);
+ fail("BigInteger can not be decimal values.");
+ } catch (JsonSyntaxException expected) { }
+ }
+
+ public void testMoreSpecificSerialization() {
+ Gson gson = new Gson();
+ String expected = "This is a string";
+ String expectedJson = gson.toJson(expected);
+
+ Serializable serializableString = expected;
+ String actualJson = gson.toJson(serializableString, Serializable.class);
+ assertFalse(expectedJson.equals(actualJson));
+ }
+
+ private String extractElementFromArray(String json) {
+ return json.substring(json.indexOf('[') + 1, json.indexOf(']'));
+ }
+
+ public void testDoubleNaNSerializationNotSupportedByDefault() {
+ try {
+ double nan = Double.NaN;
+ gson.toJson(nan);
+ fail("Gson should not accept NaN for serialization");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ gson.toJson(Double.NaN);
+ fail("Gson should not accept NaN for serialization");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testDoubleNaNSerialization() {
+ Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
+ double nan = Double.NaN;
+ assertEquals("NaN", gson.toJson(nan));
+ assertEquals("NaN", gson.toJson(Double.NaN));
+ }
+
+ public void testDoubleNaNDeserialization() {
+ assertTrue(Double.isNaN(gson.fromJson("NaN", Double.class)));
+ assertTrue(Double.isNaN(gson.fromJson("NaN", double.class)));
+ }
+
+ public void testFloatNaNSerializationNotSupportedByDefault() {
+ try {
+ float nan = Float.NaN;
+ gson.toJson(nan);
+ fail("Gson should not accept NaN for serialization");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ gson.toJson(Float.NaN);
+ fail("Gson should not accept NaN for serialization");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testFloatNaNSerialization() {
+ Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
+ float nan = Float.NaN;
+ assertEquals("NaN", gson.toJson(nan));
+ assertEquals("NaN", gson.toJson(Float.NaN));
+ }
+
+ public void testFloatNaNDeserialization() {
+ assertTrue(Float.isNaN(gson.fromJson("NaN", Float.class)));
+ assertTrue(Float.isNaN(gson.fromJson("NaN", float.class)));
+ }
+
+ public void testBigDecimalNaNDeserializationNotSupported() {
+ try {
+ gson.fromJson("NaN", BigDecimal.class);
+ fail("Gson should not accept NaN for deserialization by default.");
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testDoubleInfinitySerializationNotSupportedByDefault() {
+ try {
+ double infinity = Double.POSITIVE_INFINITY;
+ gson.toJson(infinity);
+ fail("Gson should not accept positive infinity for serialization by default.");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ gson.toJson(Double.POSITIVE_INFINITY);
+ fail("Gson should not accept positive infinity for serialization by default.");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testDoubleInfinitySerialization() {
+ Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
+ double infinity = Double.POSITIVE_INFINITY;
+ assertEquals("Infinity", gson.toJson(infinity));
+ assertEquals("Infinity", gson.toJson(Double.POSITIVE_INFINITY));
+ }
+
+ public void testDoubleInfinityDeserialization() {
+ assertTrue(Double.isInfinite(gson.fromJson("Infinity", Double.class)));
+ assertTrue(Double.isInfinite(gson.fromJson("Infinity", double.class)));
+ }
+
+ public void testFloatInfinitySerializationNotSupportedByDefault() {
+ try {
+ float infinity = Float.POSITIVE_INFINITY;
+ gson.toJson(infinity);
+ fail("Gson should not accept positive infinity for serialization by default");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ gson.toJson(Float.POSITIVE_INFINITY);
+ fail("Gson should not accept positive infinity for serialization by default");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testFloatInfinitySerialization() {
+ Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
+ float infinity = Float.POSITIVE_INFINITY;
+ assertEquals("Infinity", gson.toJson(infinity));
+ assertEquals("Infinity", gson.toJson(Float.POSITIVE_INFINITY));
+ }
+
+ public void testFloatInfinityDeserialization() {
+ assertTrue(Float.isInfinite(gson.fromJson("Infinity", Float.class)));
+ assertTrue(Float.isInfinite(gson.fromJson("Infinity", float.class)));
+ }
+
+ public void testBigDecimalInfinityDeserializationNotSupported() {
+ try {
+ gson.fromJson("Infinity", BigDecimal.class);
+ fail("Gson should not accept positive infinity for deserialization with BigDecimal");
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testNegativeInfinitySerializationNotSupportedByDefault() {
+ try {
+ double negativeInfinity = Double.NEGATIVE_INFINITY;
+ gson.toJson(negativeInfinity);
+ fail("Gson should not accept negative infinity for serialization by default");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ gson.toJson(Double.NEGATIVE_INFINITY);
+ fail("Gson should not accept negative infinity for serialization by default");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testNegativeInfinitySerialization() {
+ Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
+ double negativeInfinity = Double.NEGATIVE_INFINITY;
+ assertEquals("-Infinity", gson.toJson(negativeInfinity));
+ assertEquals("-Infinity", gson.toJson(Double.NEGATIVE_INFINITY));
+ }
+
+ public void testNegativeInfinityDeserialization() {
+ assertTrue(Double.isInfinite(gson.fromJson("-Infinity", double.class)));
+ assertTrue(Double.isInfinite(gson.fromJson("-Infinity", Double.class)));
+ }
+
+ public void testNegativeInfinityFloatSerializationNotSupportedByDefault() {
+ try {
+ float negativeInfinity = Float.NEGATIVE_INFINITY;
+ gson.toJson(negativeInfinity);
+ fail("Gson should not accept negative infinity for serialization by default");
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ gson.toJson(Float.NEGATIVE_INFINITY);
+ fail("Gson should not accept negative infinity for serialization by default");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testNegativeInfinityFloatSerialization() {
+ Gson gson = new GsonBuilder().serializeSpecialFloatingPointValues().create();
+ float negativeInfinity = Float.NEGATIVE_INFINITY;
+ assertEquals("-Infinity", gson.toJson(negativeInfinity));
+ assertEquals("-Infinity", gson.toJson(Float.NEGATIVE_INFINITY));
+ }
+
+ public void testNegativeInfinityFloatDeserialization() {
+ assertTrue(Float.isInfinite(gson.fromJson("-Infinity", float.class)));
+ assertTrue(Float.isInfinite(gson.fromJson("-Infinity", Float.class)));
+ }
+
+ public void testBigDecimalNegativeInfinityDeserializationNotSupported() {
+ try {
+ gson.fromJson("-Infinity", BigDecimal.class);
+ fail("Gson should not accept positive infinity for deserialization");
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testLongAsStringSerialization() throws Exception {
+ gson = new GsonBuilder().setLongSerializationPolicy(LongSerializationPolicy.STRING).create();
+ String result = gson.toJson(15L);
+ assertEquals("\"15\"", result);
+
+ // Test with an integer and ensure its still a number
+ result = gson.toJson(2);
+ assertEquals("2", result);
+ }
+
+ public void testLongAsStringDeserialization() throws Exception {
+ long value = gson.fromJson("\"15\"", long.class);
+ assertEquals(15, value);
+
+ gson = new GsonBuilder().setLongSerializationPolicy(LongSerializationPolicy.STRING).create();
+ value = gson.fromJson("\"25\"", long.class);
+ assertEquals(25, value);
+ }
+
+ public void testQuotedStringSerializationAndDeserialization() throws Exception {
+ String value = "String Blah Blah Blah...1, 2, 3";
+ String serializedForm = gson.toJson(value);
+ assertEquals("\"" + value + "\"", serializedForm);
+
+ String actual = gson.fromJson(serializedForm, String.class);
+ assertEquals(value, actual);
+ }
+
+ public void testUnquotedStringDeserializationFails() throws Exception {
+ assertEquals("UnquotedSingleWord", gson.fromJson("UnquotedSingleWord", String.class));
+
+ String value = "String Blah Blah Blah...1, 2, 3";
+ try {
+ gson.fromJson(value, String.class);
+ fail();
+ } catch (JsonSyntaxException expected) { }
+ }
+
+ public void testHtmlCharacterSerialization() throws Exception {
+ String target = "<script>var a = 12;</script>";
+ String result = gson.toJson(target);
+ assertFalse(result.equals('"' + target + '"'));
+
+ gson = new GsonBuilder().disableHtmlEscaping().create();
+ result = gson.toJson(target);
+ assertTrue(result.equals('"' + target + '"'));
+ }
+
+ public void testDeserializePrimitiveWrapperAsObjectField() {
+ String json = "{i:10}";
+ ClassWithIntegerField target = gson.fromJson(json, ClassWithIntegerField.class);
+ assertEquals(10, target.i.intValue());
+ }
+
+ private static class ClassWithIntegerField {
+ Integer i;
+ }
+
+ public void testPrimitiveClassLiteral() {
+ assertEquals(1, gson.fromJson("1", int.class).intValue());
+ assertEquals(1, gson.fromJson(new StringReader("1"), int.class).intValue());
+ assertEquals(1, gson.fromJson(new JsonPrimitive(1), int.class).intValue());
+ }
+
+ public void testDeserializeJsonObjectAsLongPrimitive() {
+ try {
+ gson.fromJson("{'abc':1}", long.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonArrayAsLongWrapper() {
+ try {
+ gson.fromJson("[1,2,3]", Long.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonArrayAsInt() {
+ try {
+ gson.fromJson("[1, 2, 3, 4]", int.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonObjectAsInteger() {
+ try {
+ gson.fromJson("{}", Integer.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonObjectAsShortPrimitive() {
+ try {
+ gson.fromJson("{'abc':1}", short.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonArrayAsShortWrapper() {
+ try {
+ gson.fromJson("['a','b']", Short.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonArrayAsDoublePrimitive() {
+ try {
+ gson.fromJson("[1,2]", double.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonObjectAsDoubleWrapper() {
+ try {
+ gson.fromJson("{'abc':1}", Double.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonObjectAsFloatPrimitive() {
+ try {
+ gson.fromJson("{'abc':1}", float.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonArrayAsFloatWrapper() {
+ try {
+ gson.fromJson("[1,2,3]", Float.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonObjectAsBytePrimitive() {
+ try {
+ gson.fromJson("{'abc':1}", byte.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonArrayAsByteWrapper() {
+ try {
+ gson.fromJson("[1,2,3,4]", Byte.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonObjectAsBooleanPrimitive() {
+ try {
+ gson.fromJson("{'abc':1}", boolean.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonArrayAsBooleanWrapper() {
+ try {
+ gson.fromJson("[1,2,3,4]", Boolean.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonArrayAsBigDecimal() {
+ try {
+ gson.fromJson("[1,2,3,4]", BigDecimal.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonObjectAsBigDecimal() {
+ try {
+ gson.fromJson("{'a':1}", BigDecimal.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonArrayAsBigInteger() {
+ try {
+ gson.fromJson("[1,2,3,4]", BigInteger.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonObjectAsBigInteger() {
+ try {
+ gson.fromJson("{'c':2}", BigInteger.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonArrayAsNumber() {
+ try {
+ gson.fromJson("[1,2,3,4]", Number.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializeJsonObjectAsNumber() {
+ try {
+ gson.fromJson("{'c':2}", Number.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ }
+
+ public void testDeserializingDecimalPointValueZeroSucceeds() {
+ assertEquals(1, (int) gson.fromJson("1.0", Integer.class));
+ }
+
+ public void testDeserializingNonZeroDecimalPointValuesAsIntegerFails() {
+ try {
+ gson.fromJson("1.02", Byte.class);
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ try {
+ gson.fromJson("1.02", Short.class);
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ try {
+ gson.fromJson("1.02", Integer.class);
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ try {
+ gson.fromJson("1.02", Long.class);
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testDeserializingBigDecimalAsIntegerFails() {
+ try {
+ gson.fromJson("-122.08e-213", Integer.class);
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testDeserializingBigIntegerAsInteger() {
+ try {
+ gson.fromJson("12121211243123245845384534687435634558945453489543985435", Integer.class);
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testDeserializingBigIntegerAsLong() {
+ try {
+ gson.fromJson("12121211243123245845384534687435634558945453489543985435", Long.class);
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testValueVeryCloseToZeroIsZero() {
+ assertEquals(0, (byte) gson.fromJson("-122.08e-2132", byte.class));
+ assertEquals(0, (short) gson.fromJson("-122.08e-2132", short.class));
+ assertEquals(0, (int) gson.fromJson("-122.08e-2132", int.class));
+ assertEquals(0, (long) gson.fromJson("-122.08e-2132", long.class));
+ assertEquals(-0.0f, gson.fromJson("-122.08e-2132", float.class));
+ assertEquals(-0.0, gson.fromJson("-122.08e-2132", double.class));
+ assertEquals(0.0f, gson.fromJson("122.08e-2132", float.class));
+ assertEquals(0.0, gson.fromJson("122.08e-2132", double.class));
+ }
+
+ public void testDeserializingBigDecimalAsFloat() {
+ String json = "-122.08e-2132332";
+ float actual = gson.fromJson(json, float.class);
+ assertEquals(-0.0f, actual);
+ }
+
+ public void testDeserializingBigDecimalAsDouble() {
+ String json = "-122.08e-2132332";
+ double actual = gson.fromJson(json, double.class);
+ assertEquals(-0.0d, actual);
+ }
+
+ public void testDeserializingBigDecimalAsBigIntegerFails() {
+ try {
+ gson.fromJson("-122.08e-213", BigInteger.class);
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testDeserializingBigIntegerAsBigDecimal() {
+ BigDecimal actual =
+ gson.fromJson("12121211243123245845384534687435634558945453489543985435", BigDecimal.class);
+ assertEquals("12121211243123245845384534687435634558945453489543985435", actual.toPlainString());
+ }
+
+ public void testStringsAsBooleans() {
+ String json = "['true', 'false', 'TRUE', 'yes', '1']";
+ assertEquals(Arrays.asList(true, false, true, false, false),
+ gson.<List<Boolean>>fromJson(json, new TypeToken<List<Boolean>>() {}.getType()));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java b/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java
new file mode 100644
index 00000000..7dcbc23c
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.JsonObject;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.common.TestTypes.ClassWithTransientFields;
+import com.google.gson.common.TestTypes.Nested;
+import com.google.gson.common.TestTypes.PrimitiveArray;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Functional tests for print formatting.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class PrintFormattingTest extends TestCase {
+
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void testCompactFormattingLeavesNoWhiteSpace() {
+ List list = new ArrayList();
+ list.add(new BagOfPrimitives());
+ list.add(new Nested());
+ list.add(new PrimitiveArray());
+ list.add(new ClassWithTransientFields());
+
+ String json = gson.toJson(list);
+ assertContainsNoWhiteSpace(json);
+ }
+
+ public void testJsonObjectWithNullValues() {
+ JsonObject obj = new JsonObject();
+ obj.addProperty("field1", "value1");
+ obj.addProperty("field2", (String) null);
+ String json = gson.toJson(obj);
+ assertTrue(json.contains("field1"));
+ assertFalse(json.contains("field2"));
+ }
+
+ public void testJsonObjectWithNullValuesSerialized() {
+ gson = new GsonBuilder().serializeNulls().create();
+ JsonObject obj = new JsonObject();
+ obj.addProperty("field1", "value1");
+ obj.addProperty("field2", (String) null);
+ String json = gson.toJson(obj);
+ assertTrue(json.contains("field1"));
+ assertTrue(json.contains("field2"));
+ }
+
+ private static void assertContainsNoWhiteSpace(String str) {
+ for (char c : str.toCharArray()) {
+ assertFalse(Character.isWhitespace(c));
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/RawSerializationTest.java b/gson/src/test/java/com/google/gson/functional/RawSerializationTest.java
new file mode 100644
index 00000000..d5e8883e
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/RawSerializationTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 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 java.util.Arrays;
+import java.util.Collection;
+
+import junit.framework.TestCase;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Unit tests to validate serialization of parameterized types without explicit types
+ *
+ * @author Inderjeet Singh
+ */
+public class RawSerializationTest extends TestCase {
+
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testCollectionOfPrimitives() {
+ Collection<Integer> ints = Arrays.asList(1, 2, 3, 4, 5);
+ String json = gson.toJson(ints);
+ assertEquals("[1,2,3,4,5]", json);
+ }
+
+ public void testCollectionOfObjects() {
+ Collection<Foo> foos = Arrays.asList(new Foo(1), new Foo(2));
+ String json = gson.toJson(foos);
+ assertEquals("[{\"b\":1},{\"b\":2}]", json);
+ }
+
+ public void testParameterizedObject() {
+ Bar<Foo> bar = new Bar<Foo>(new Foo(1));
+ String expectedJson = "{\"t\":{\"b\":1}}";
+ // Ensure that serialization works without specifying the type explicitly
+ String json = gson.toJson(bar);
+ assertEquals(expectedJson, json);
+ // Ensure that serialization also works when the type is specified explicitly
+ json = gson.toJson(bar, new TypeToken<Bar<Foo>>(){}.getType());
+ assertEquals(expectedJson, json);
+ }
+
+ public void testTwoLevelParameterizedObject() {
+ Bar<Bar<Foo>> bar = new Bar<Bar<Foo>>(new Bar<Foo>(new Foo(1)));
+ String expectedJson = "{\"t\":{\"t\":{\"b\":1}}}";
+ // Ensure that serialization works without specifying the type explicitly
+ String json = gson.toJson(bar);
+ assertEquals(expectedJson, json);
+ // Ensure that serialization also works when the type is specified explicitly
+ json = gson.toJson(bar, new TypeToken<Bar<Bar<Foo>>>(){}.getType());
+ assertEquals(expectedJson, json);
+ }
+
+ public void testThreeLevelParameterizedObject() {
+ Bar<Bar<Bar<Foo>>> bar = new Bar<Bar<Bar<Foo>>>(new Bar<Bar<Foo>>(new Bar<Foo>(new Foo(1))));
+ String expectedJson = "{\"t\":{\"t\":{\"t\":{\"b\":1}}}}";
+ // Ensure that serialization works without specifying the type explicitly
+ String json = gson.toJson(bar);
+ assertEquals(expectedJson, json);
+ // Ensure that serialization also works when the type is specified explicitly
+ json = gson.toJson(bar, new TypeToken<Bar<Bar<Bar<Foo>>>>(){}.getType());
+ assertEquals(expectedJson, json);
+ }
+
+ private static class Foo {
+ @SuppressWarnings("unused")
+ int b;
+ Foo(int b) {
+ this.b = b;
+ }
+ }
+
+ private static class Bar<T> {
+ @SuppressWarnings("unused")
+ T t;
+ Bar(T t) {
+ this.t = t;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/ReadersWritersTest.java b/gson/src/test/java/com/google/gson/functional/ReadersWritersTest.java
new file mode 100644
index 00000000..e21fb903
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/ReadersWritersTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.JsonStreamParser;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+
+import com.google.gson.reflect.TypeToken;
+import java.util.Map;
+import junit.framework.TestCase;
+
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Functional tests for the support of {@link Reader}s and {@link Writer}s.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class ReadersWritersTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testWriterForSerialization() throws Exception {
+ Writer writer = new StringWriter();
+ BagOfPrimitives src = new BagOfPrimitives();
+ gson.toJson(src, writer);
+ assertEquals(src.getExpectedJson(), writer.toString());
+ }
+
+ public void testReaderForDeserialization() throws Exception {
+ BagOfPrimitives expected = new BagOfPrimitives();
+ Reader json = new StringReader(expected.getExpectedJson());
+ BagOfPrimitives actual = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals(expected, actual);
+ }
+
+ public void testTopLevelNullObjectSerializationWithWriter() {
+ StringWriter writer = new StringWriter();
+ gson.toJson(null, writer);
+ assertEquals("null", writer.toString());
+ }
+
+ public void testTopLevelNullObjectDeserializationWithReader() {
+ StringReader reader = new StringReader("null");
+ Integer nullIntObject = gson.fromJson(reader, Integer.class);
+ assertNull(nullIntObject);
+ }
+
+ public void testTopLevelNullObjectSerializationWithWriterAndSerializeNulls() {
+ Gson gson = new GsonBuilder().serializeNulls().create();
+ StringWriter writer = new StringWriter();
+ gson.toJson(null, writer);
+ assertEquals("null", writer.toString());
+ }
+
+ public void testTopLevelNullObjectDeserializationWithReaderAndSerializeNulls() {
+ Gson gson = new GsonBuilder().serializeNulls().create();
+ StringReader reader = new StringReader("null");
+ Integer nullIntObject = gson.fromJson(reader, Integer.class);
+ assertNull(nullIntObject);
+ }
+
+ public void testReadWriteTwoStrings() throws IOException {
+ Gson gson= new Gson();
+ CharArrayWriter writer= new CharArrayWriter();
+ writer.write(gson.toJson("one").toCharArray());
+ writer.write(gson.toJson("two").toCharArray());
+ CharArrayReader reader = new CharArrayReader(writer.toCharArray());
+ JsonStreamParser parser = new JsonStreamParser(reader);
+ String actualOne = gson.fromJson(parser.next(), String.class);
+ assertEquals("one", actualOne);
+ String actualTwo = gson.fromJson(parser.next(), String.class);
+ assertEquals("two", actualTwo);
+ }
+
+ public void testReadWriteTwoObjects() throws IOException {
+ Gson gson= new Gson();
+ CharArrayWriter writer= new CharArrayWriter();
+ BagOfPrimitives expectedOne = new BagOfPrimitives(1, 1, true, "one");
+ writer.write(gson.toJson(expectedOne).toCharArray());
+ BagOfPrimitives expectedTwo = new BagOfPrimitives(2, 2, false, "two");
+ writer.write(gson.toJson(expectedTwo).toCharArray());
+ CharArrayReader reader = new CharArrayReader(writer.toCharArray());
+ JsonStreamParser parser = new JsonStreamParser(reader);
+ BagOfPrimitives actualOne = gson.fromJson(parser.next(), BagOfPrimitives.class);
+ assertEquals("one", actualOne.stringValue);
+ BagOfPrimitives actualTwo = gson.fromJson(parser.next(), BagOfPrimitives.class);
+ assertEquals("two", actualTwo.stringValue);
+ assertFalse(parser.hasNext());
+ }
+
+ public void testTypeMismatchThrowsJsonSyntaxExceptionForStrings() {
+ try {
+ gson.fromJson("true", new TypeToken<Map<String, String>>() {}.getType());
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+
+ public void testTypeMismatchThrowsJsonSyntaxExceptionForReaders() {
+ try {
+ gson.fromJson(new StringReader("true"), new TypeToken<Map<String, String>>() {}.getType());
+ fail();
+ } catch (JsonSyntaxException expected) {
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/RuntimeTypeAdapterFactoryFunctionalTest.java b/gson/src/test/java/com/google/gson/functional/RuntimeTypeAdapterFactoryFunctionalTest.java
new file mode 100644
index 00000000..c3b0898d
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/RuntimeTypeAdapterFactoryFunctionalTest.java
@@ -0,0 +1,205 @@
+/*
+ * 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 java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.annotations.JsonAdapter;
+import com.google.gson.internal.Streams;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Functional tests for the RuntimeTypeAdapterFactory feature in extras.
+ */
+public final class RuntimeTypeAdapterFactoryFunctionalTest extends TestCase {
+
+ private final Gson gson = new Gson();
+
+ /**
+ * This test also ensures that {@link TypeAdapterFactory} registered through {@link JsonAdapter}
+ * work correctly for {@link Gson#getDelegateAdapter(TypeAdapterFactory, TypeToken)}.
+ */
+ public void testSubclassesAutomaticallySerialzed() throws Exception {
+ Shape shape = new Circle(25);
+ String json = gson.toJson(shape);
+ shape = gson.fromJson(json, Shape.class);
+ assertEquals(25, ((Circle)shape).radius);
+
+ shape = new Square(15);
+ json = gson.toJson(shape);
+ shape = gson.fromJson(json, Shape.class);
+ assertEquals(15, ((Square)shape).side);
+ assertEquals(ShapeType.SQUARE, shape.type);
+ }
+
+ @JsonAdapter(Shape.JsonAdapterFactory.class)
+ static class Shape {
+ final ShapeType type;
+ Shape(ShapeType type) { this.type = type; }
+ private static final class JsonAdapterFactory extends RuntimeTypeAdapterFactory<Shape> {
+ public JsonAdapterFactory() {
+ super(Shape.class, "type");
+ registerSubtype(Circle.class, ShapeType.CIRCLE.toString());
+ registerSubtype(Square.class, ShapeType.SQUARE.toString());
+ }
+ }
+ }
+
+ public enum ShapeType {
+ SQUARE, CIRCLE
+ }
+
+ private static final class Circle extends Shape {
+ final int radius;
+ Circle(int radius) { super(ShapeType.CIRCLE); this.radius = radius; }
+ }
+
+ private static final class Square extends Shape {
+ final int side;
+ Square(int side) { super(ShapeType.SQUARE); this.side = side; }
+ }
+
+ // Copied from the extras package
+ static class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
+ private final Class<?> baseType;
+ private final String typeFieldName;
+ private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();
+ private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();
+
+ protected RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName) {
+ if (typeFieldName == null || baseType == null) {
+ throw new NullPointerException();
+ }
+ this.baseType = baseType;
+ this.typeFieldName = typeFieldName;
+ }
+
+ /**
+ * Creates a new runtime type adapter using for {@code baseType} using {@code
+ * typeFieldName} as the type field name. Type field names are case sensitive.
+ */
+ public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
+ return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName);
+ }
+
+ /**
+ * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
+ * the type field name.
+ */
+ public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
+ return new RuntimeTypeAdapterFactory<T>(baseType, "type");
+ }
+
+ /**
+ * Registers {@code type} identified by {@code label}. Labels are case
+ * sensitive.
+ *
+ * @throws IllegalArgumentException if either {@code type} or {@code label}
+ * have already been registered on this type adapter.
+ */
+ public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
+ if (type == null || label == null) {
+ throw new NullPointerException();
+ }
+ if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
+ throw new IllegalArgumentException("types and labels must be unique");
+ }
+ labelToSubtype.put(label, type);
+ subtypeToLabel.put(type, label);
+ return this;
+ }
+
+ /**
+ * Registers {@code type} identified by its {@link Class#getSimpleName simple
+ * name}. Labels are case sensitive.
+ *
+ * @throws IllegalArgumentException if either {@code type} or its simple name
+ * have already been registered on this type adapter.
+ */
+ public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
+ return registerSubtype(type, type.getSimpleName());
+ }
+
+ public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
+ if (type.getRawType() != baseType) {
+ return null;
+ }
+
+ final Map<String, TypeAdapter<?>> labelToDelegate
+ = new LinkedHashMap<String, TypeAdapter<?>>();
+ final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate
+ = new LinkedHashMap<Class<?>, TypeAdapter<?>>();
+ for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
+ TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
+ labelToDelegate.put(entry.getKey(), delegate);
+ subtypeToDelegate.put(entry.getValue(), delegate);
+ }
+
+ return new TypeAdapter<R>() {
+ @Override public R read(JsonReader in) throws IOException {
+ JsonElement jsonElement = Streams.parse(in);
+ JsonElement labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
+ if (labelJsonElement == null) {
+ throw new JsonParseException("cannot deserialize " + baseType
+ + " because it does not define a field named " + typeFieldName);
+ }
+ String label = labelJsonElement.getAsString();
+ @SuppressWarnings("unchecked") // registration requires that subtype extends T
+ TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
+ if (delegate == null) {
+ throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
+ + label + "; did you forget to register a subtype?");
+ }
+ return delegate.fromJsonTree(jsonElement);
+ }
+
+ @Override public void write(JsonWriter out, R value) throws IOException {
+ Class<?> srcType = value.getClass();
+ String label = subtypeToLabel.get(srcType);
+ @SuppressWarnings("unchecked") // registration requires that subtype extends T
+ TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
+ if (delegate == null) {
+ throw new JsonParseException("cannot serialize " + srcType.getName()
+ + "; did you forget to register a subtype?");
+ }
+ JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
+ if (!jsonObject.has(typeFieldName)) {
+ JsonObject clone = new JsonObject();
+ clone.add(typeFieldName, new JsonPrimitive(label));
+ for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
+ clone.add(e.getKey(), e.getValue());
+ }
+ jsonObject = clone;
+ }
+ Streams.write(jsonObject, out);
+ }
+ };
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/SecurityTest.java b/gson/src/test/java/com/google/gson/functional/SecurityTest.java
new file mode 100644
index 00000000..aa1c2d45
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/SecurityTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for security-related aspects of Gson
+ *
+ * @author Inderjeet Singh
+ */
+public class SecurityTest extends TestCase {
+ /**
+ * Keep this in sync with Gson.JSON_NON_EXECUTABLE_PREFIX
+ */
+ private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
+
+ private GsonBuilder gsonBuilder;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gsonBuilder = new GsonBuilder();
+ }
+
+ public void testNonExecutableJsonSerialization() {
+ Gson gson = gsonBuilder.generateNonExecutableJson().create();
+ String json = gson.toJson(new BagOfPrimitives());
+ assertTrue(json.startsWith(JSON_NON_EXECUTABLE_PREFIX));
+ }
+
+ public void testNonExecutableJsonDeserialization() {
+ String json = JSON_NON_EXECUTABLE_PREFIX + "{longValue:1}";
+ Gson gson = gsonBuilder.create();
+ BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals(1, target.longValue);
+ }
+
+ public void testJsonWithNonExectuableTokenSerialization() {
+ Gson gson = gsonBuilder.generateNonExecutableJson().create();
+ String json = gson.toJson(JSON_NON_EXECUTABLE_PREFIX);
+ assertTrue(json.contains(")]}'\n"));
+ }
+
+ /**
+ * Gson should be able to deserialize a stream with non-exectuable token even if it is created
+ * without {@link GsonBuilder#generateNonExecutableJson()}.
+ */
+ public void testJsonWithNonExectuableTokenWithRegularGsonDeserialization() {
+ Gson gson = gsonBuilder.create();
+ String json = JSON_NON_EXECUTABLE_PREFIX + "{stringValue:')]}\\u0027\\n'}";
+ BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals(")]}'\n", target.stringValue);
+ }
+
+ /**
+ * Gson should be able to deserialize a stream with non-exectuable token if it is created
+ * with {@link GsonBuilder#generateNonExecutableJson()}.
+ */
+ public void testJsonWithNonExectuableTokenWithConfiguredGsonDeserialization() {
+ // Gson should be able to deserialize a stream with non-exectuable token even if it is created
+ Gson gson = gsonBuilder.generateNonExecutableJson().create();
+ String json = JSON_NON_EXECUTABLE_PREFIX + "{intValue:2,stringValue:')]}\\u0027\\n'}";
+ BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals(")]}'\n", target.stringValue);
+ assertEquals(2, target.intValue);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/SerializedNameTest.java b/gson/src/test/java/com/google/gson/functional/SerializedNameTest.java
new file mode 100644
index 00000000..38ad8242
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/SerializedNameTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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;
+
+public final class SerializedNameTest extends TestCase {
+ private final Gson gson = new Gson();
+
+ public void testFirstNameIsChosenForSerialization() {
+ MyClass target = new MyClass("v1", "v2");
+ // Ensure name1 occurs exactly once, and name2 and name3 dont appear
+ assertEquals("{\"name\":\"v1\",\"name1\":\"v2\"}", gson.toJson(target));
+ }
+
+ public void testMultipleNamesDeserializedCorrectly() {
+ assertEquals("v1", gson.fromJson("{'name':'v1'}", MyClass.class).a);
+
+ // Both name1 and name2 gets deserialized to b
+ assertEquals("v11", gson.fromJson("{'name1':'v11'}", MyClass.class).b);
+ assertEquals("v2", gson.fromJson("{'name2':'v2'}", MyClass.class).b);
+ assertEquals("v3", gson.fromJson("{'name3':'v3'}", MyClass.class).b);
+ }
+
+ public void testMultipleNamesInTheSameString() {
+ // The last value takes precedence
+ assertEquals("v3", gson.fromJson("{'name1':'v1','name2':'v2','name3':'v3'}", MyClass.class).b);
+ }
+
+ private static final class MyClass {
+ @SerializedName("name") String a;
+ @SerializedName(value="name1", alternate={"name2", "name3"}) String b;
+ MyClass(String a, String b) {
+ this.a = a;
+ this.b = b;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java
new file mode 100644
index 00000000..551ceffc
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/StreamingTypeAdaptersTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2011 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.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import junit.framework.TestCase;
+
+public final class StreamingTypeAdaptersTest extends TestCase {
+ private Gson miniGson = new GsonBuilder().create();
+ private TypeAdapter<Truck> truckAdapter = miniGson.getAdapter(Truck.class);
+ private TypeAdapter<Map<String, Double>> mapAdapter
+ = miniGson.getAdapter(new TypeToken<Map<String, Double>>() {});
+
+ public void testSerialize() throws IOException {
+ Truck truck = new Truck();
+ truck.passengers = Arrays.asList(new Person("Jesse", 29), new Person("Jodie", 29));
+ truck.horsePower = 300;
+
+ assertEquals("{'horsePower':300.0,"
+ + "'passengers':[{'age':29,'name':'Jesse'},{'age':29,'name':'Jodie'}]}",
+ toJson(truckAdapter, truck).replace('\"', '\''));
+ }
+
+ public void testDeserialize() throws IOException {
+ String json = "{'horsePower':300.0,"
+ + "'passengers':[{'age':29,'name':'Jesse'},{'age':29,'name':'Jodie'}]}";
+ Truck truck = fromJson(truckAdapter, json);
+ assertEquals(300.0, truck.horsePower);
+ assertEquals(Arrays.asList(new Person("Jesse", 29), new Person("Jodie", 29)), truck.passengers);
+ }
+
+ public void testSerializeNullField() throws IOException {
+ Truck truck = new Truck();
+ truck.passengers = null;
+ assertEquals("{'horsePower':0.0,'passengers':null}",
+ toJson(truckAdapter, truck).replace('\"', '\''));
+ }
+
+ public void testDeserializeNullField() throws IOException {
+ Truck truck = fromJson(truckAdapter, "{'horsePower':0.0,'passengers':null}");
+ assertNull(truck.passengers);
+ }
+
+ public void testSerializeNullObject() throws IOException {
+ Truck truck = new Truck();
+ truck.passengers = Arrays.asList((Person) null);
+ assertEquals("{'horsePower':0.0,'passengers':[null]}",
+ toJson(truckAdapter, truck).replace('\"', '\''));
+ }
+
+ public void testDeserializeNullObject() throws IOException {
+ Truck truck = fromJson(truckAdapter, "{'horsePower':0.0,'passengers':[null]}");
+ assertEquals(Arrays.asList((Person) null), truck.passengers);
+ }
+
+ public void testSerializeWithCustomTypeAdapter() throws IOException {
+ usePersonNameAdapter();
+ Truck truck = new Truck();
+ truck.passengers = Arrays.asList(new Person("Jesse", 29), new Person("Jodie", 29));
+ assertEquals("{'horsePower':0.0,'passengers':['Jesse','Jodie']}",
+ toJson(truckAdapter, truck).replace('\"', '\''));
+ }
+
+ public void testDeserializeWithCustomTypeAdapter() throws IOException {
+ usePersonNameAdapter();
+ Truck truck = fromJson(truckAdapter, "{'horsePower':0.0,'passengers':['Jesse','Jodie']}");
+ assertEquals(Arrays.asList(new Person("Jesse", -1), new Person("Jodie", -1)), truck.passengers);
+ }
+
+ private void usePersonNameAdapter() {
+ TypeAdapter<Person> personNameAdapter = new TypeAdapter<Person>() {
+ @Override public Person read(JsonReader in) throws IOException {
+ String name = in.nextString();
+ return new Person(name, -1);
+ }
+ @Override public void write(JsonWriter out, Person value) throws IOException {
+ out.value(value.name);
+ }
+ };
+ miniGson = new GsonBuilder().registerTypeAdapter(Person.class, personNameAdapter).create();
+ truckAdapter = miniGson.getAdapter(Truck.class);
+ }
+
+ public void testSerializeMap() throws IOException {
+ Map<String, Double> map = new LinkedHashMap<String, Double>();
+ map.put("a", 5.0);
+ map.put("b", 10.0);
+ assertEquals("{'a':5.0,'b':10.0}", toJson(mapAdapter, map).replace('"', '\''));
+ }
+
+ public void testDeserializeMap() throws IOException {
+ Map<String, Double> map = new LinkedHashMap<String, Double>();
+ map.put("a", 5.0);
+ map.put("b", 10.0);
+ assertEquals(map, fromJson(mapAdapter, "{'a':5.0,'b':10.0}"));
+ }
+
+ public void testSerialize1dArray() throws IOException {
+ TypeAdapter<double[]> arrayAdapter = miniGson.getAdapter(new TypeToken<double[]>() {});
+ assertEquals("[1.0,2.0,3.0]", toJson(arrayAdapter, new double[]{1.0, 2.0, 3.0}));
+ }
+
+ public void testDeserialize1dArray() throws IOException {
+ TypeAdapter<double[]> arrayAdapter = miniGson.getAdapter(new TypeToken<double[]>() {});
+ double[] array = fromJson(arrayAdapter, "[1.0,2.0,3.0]");
+ assertTrue(Arrays.toString(array), Arrays.equals(new double[]{1.0, 2.0, 3.0}, array));
+ }
+
+ public void testSerialize2dArray() throws IOException {
+ TypeAdapter<double[][]> arrayAdapter = miniGson.getAdapter(new TypeToken<double[][]>() {});
+ double[][] array = { {1.0, 2.0 }, { 3.0 } };
+ assertEquals("[[1.0,2.0],[3.0]]", toJson(arrayAdapter, array));
+ }
+
+ public void testDeserialize2dArray() throws IOException {
+ TypeAdapter<double[][]> arrayAdapter = miniGson.getAdapter(new TypeToken<double[][]>() {});
+ double[][] array = fromJson(arrayAdapter, "[[1.0,2.0],[3.0]]");
+ double[][] expected = { {1.0, 2.0 }, { 3.0 } };
+ assertTrue(Arrays.toString(array), Arrays.deepEquals(expected, array));
+ }
+
+ public void testNullSafe() {
+ TypeAdapter<Person> typeAdapter = new TypeAdapter<Person>() {
+ @Override public Person read(JsonReader in) throws IOException {
+ String[] values = in.nextString().split(",");
+ return new Person(values[0], Integer.parseInt(values[1]));
+ }
+ public void write(JsonWriter out, Person person) throws IOException {
+ out.value(person.name + "," + person.age);
+ }
+ };
+ Gson gson = new GsonBuilder().registerTypeAdapter(
+ Person.class, typeAdapter).create();
+ Truck truck = new Truck();
+ truck.horsePower = 1.0D;
+ truck.passengers = new ArrayList<Person>();
+ truck.passengers.add(null);
+ truck.passengers.add(new Person("jesse", 30));
+ try {
+ gson.toJson(truck, Truck.class);
+ fail();
+ } catch (NullPointerException expected) {}
+ String json = "{horsePower:1.0,passengers:[null,'jesse,30']}";
+ try {
+ gson.fromJson(json, Truck.class);
+ fail();
+ } catch (JsonSyntaxException expected) {}
+ gson = new GsonBuilder().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);
+ assertEquals(1.0D, truck.horsePower);
+ assertNull(truck.passengers.get(0));
+ assertEquals("jesse", truck.passengers.get(1).name);
+ }
+
+ public void testSerializeRecursive() throws IOException {
+ TypeAdapter<Node> nodeAdapter = miniGson.getAdapter(Node.class);
+ Node root = new Node("root");
+ root.left = new Node("left");
+ root.right = new Node("right");
+ assertEquals("{'label':'root',"
+ + "'left':{'label':'left','left':null,'right':null},"
+ + "'right':{'label':'right','left':null,'right':null}}",
+ toJson(nodeAdapter, root).replace('"', '\''));
+ }
+
+ public void testFromJsonTree() {
+ JsonObject truckObject = new JsonObject();
+ truckObject.add("horsePower", new JsonPrimitive(300));
+ JsonArray passengersArray = new JsonArray();
+ JsonObject jesseObject = new JsonObject();
+ jesseObject.add("age", new JsonPrimitive(30));
+ jesseObject.add("name", new JsonPrimitive("Jesse"));
+ passengersArray.add(jesseObject);
+ truckObject.add("passengers", passengersArray);
+
+ Truck truck = truckAdapter.fromJsonTree(truckObject);
+ assertEquals(300.0, truck.horsePower);
+ assertEquals(Arrays.asList(new Person("Jesse", 30)), truck.passengers);
+ }
+
+ static class Truck {
+ double horsePower;
+ List<Person> passengers = Collections.emptyList();
+ }
+
+ static class Person {
+ int age;
+ String name;
+ Person(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ @Override public boolean equals(Object o) {
+ return o instanceof Person
+ && ((Person) o).name.equals(name)
+ && ((Person) o).age == age;
+ }
+ @Override public int hashCode() {
+ return name.hashCode() ^ age;
+ }
+ }
+
+ static class Node {
+ String label;
+ Node left;
+ Node right;
+ Node(String label) {
+ this.label = label;
+ }
+ }
+
+ // TODO: remove this when TypeAdapter.toJson() is public
+ private static <T> String toJson(TypeAdapter<T> typeAdapter, T value) throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter writer = new JsonWriter(stringWriter);
+ typeAdapter.write(writer, value);
+ return stringWriter.toString();
+ }
+
+ // TODO: remove this when TypeAdapter.fromJson() is public
+ private <T> T fromJson(TypeAdapter<T> typeAdapter, String json) throws IOException {
+ JsonReader reader = new JsonReader(new StringReader(json));
+ reader.setLenient(true); // TODO: non-lenient?
+ return typeAdapter.read(reader);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/StringTest.java b/gson/src/test/java/com/google/gson/functional/StringTest.java
new file mode 100644
index 00000000..7dcf6f0f
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/StringTest.java
@@ -0,0 +1,140 @@
+package com.google.gson.functional;
+
+import com.google.gson.Gson;
+
+import junit.framework.TestCase;
+
+/**
+ * Functional tests for Json serialization and deserialization of strings.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class StringTest extends TestCase {
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testStringValueSerialization() throws Exception {
+ String value = "someRandomStringValue";
+ assertEquals('"' + value + '"', gson.toJson(value));
+ }
+
+ public void testStringValueDeserialization() throws Exception {
+ String value = "someRandomStringValue";
+ String actual = gson.fromJson("\"" + value + "\"", String.class);
+ assertEquals(value, actual);
+ }
+
+ public void testSingleQuoteInStringSerialization() throws Exception {
+ String valueWithQuotes = "beforeQuote'afterQuote";
+ String jsonRepresentation = gson.toJson(valueWithQuotes);
+ assertEquals(valueWithQuotes, gson.fromJson(jsonRepresentation, String.class));
+ }
+
+ public void testEscapedCtrlNInStringSerialization() throws Exception {
+ String value = "a\nb";
+ String json = gson.toJson(value);
+ assertEquals("\"a\\nb\"", json);
+ }
+
+ public void testEscapedCtrlNInStringDeserialization() throws Exception {
+ String json = "'a\\nb'";
+ String actual = gson.fromJson(json, String.class);
+ assertEquals("a\nb", actual);
+ }
+
+ public void testEscapedCtrlRInStringSerialization() throws Exception {
+ String value = "a\rb";
+ String json = gson.toJson(value);
+ assertEquals("\"a\\rb\"", json);
+ }
+
+ public void testEscapedCtrlRInStringDeserialization() throws Exception {
+ String json = "'a\\rb'";
+ String actual = gson.fromJson(json, String.class);
+ assertEquals("a\rb", actual);
+ }
+
+ public void testEscapedBackslashInStringSerialization() throws Exception {
+ String value = "a\\b";
+ String json = gson.toJson(value);
+ assertEquals("\"a\\\\b\"", json);
+ }
+
+ public void testEscapedBackslashInStringDeserialization() throws Exception {
+ String actual = gson.fromJson("'a\\\\b'", String.class);
+ assertEquals("a\\b", actual);
+ }
+
+ public void testSingleQuoteInStringDeserialization() throws Exception {
+ String value = "beforeQuote'afterQuote";
+ String actual = gson.fromJson("\"" + value + "\"", String.class);
+ assertEquals(value, actual);
+ }
+
+ public void testEscapingQuotesInStringSerialization() throws Exception {
+ String valueWithQuotes = "beforeQuote\"afterQuote";
+ String jsonRepresentation = gson.toJson(valueWithQuotes);
+ String target = gson.fromJson(jsonRepresentation, String.class);
+ assertEquals(valueWithQuotes, target);
+ }
+
+ public void testEscapingQuotesInStringDeserialization() throws Exception {
+ String value = "beforeQuote\\\"afterQuote";
+ String actual = gson.fromJson("\"" + value + "\"", String.class);
+ String expected = "beforeQuote\"afterQuote";
+ assertEquals(expected, actual);
+ }
+
+ public void testStringValueAsSingleElementArraySerialization() throws Exception {
+ String[] target = {"abc"};
+ assertEquals("[\"abc\"]", gson.toJson(target));
+ assertEquals("[\"abc\"]", gson.toJson(target, String[].class));
+ }
+
+ public void testStringWithEscapedSlashDeserialization() {
+ String value = "/";
+ String json = "'\\/'";
+ String actual = gson.fromJson(json, String.class);
+ assertEquals(value, actual);
+ }
+
+ /**
+ * Created in response to http://groups.google.com/group/google-gson/browse_thread/thread/2431d4a3d0d6cb23
+ */
+ public void testAssignmentCharSerialization() {
+ String value = "abc=";
+ String json = gson.toJson(value);
+ assertEquals("\"abc\\u003d\"", json);
+ }
+
+ /**
+ * Created in response to http://groups.google.com/group/google-gson/browse_thread/thread/2431d4a3d0d6cb23
+ */
+ public void testAssignmentCharDeserialization() {
+ String json = "\"abc=\"";
+ String value = gson.fromJson(json, String.class);
+ assertEquals("abc=", value);
+
+ json = "'abc\u003d'";
+ value = gson.fromJson(json, String.class);
+ assertEquals("abc=", value);
+ }
+
+ public void testJavascriptKeywordsInStringSerialization() {
+ String value = "null true false function";
+ String json = gson.toJson(value);
+ assertEquals("\"" + value + "\"", json);
+ }
+
+ public void testJavascriptKeywordsInStringDeserialization() {
+ String json = "'null true false function'";
+ String value = gson.fromJson(json, String.class);
+ assertEquals(json.substring(1, json.length() - 1), value);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/ThrowableFunctionalTest.java b/gson/src/test/java/com/google/gson/functional/ThrowableFunctionalTest.java
new file mode 100644
index 00000000..f6ae748a
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/ThrowableFunctionalTest.java
@@ -0,0 +1,65 @@
+// Copyright (C) 2014 Trymph Inc.
+package com.google.gson.functional;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import com.google.gson.Gson;
+import com.google.gson.annotations.SerializedName;
+
+@SuppressWarnings("serial")
+public final class ThrowableFunctionalTest extends TestCase {
+ private final Gson gson = new Gson();
+
+ public void testExceptionWithoutCause() {
+ RuntimeException e = new RuntimeException("hello");
+ String json = gson.toJson(e);
+ assertTrue(json.contains("hello"));
+
+ e = gson.fromJson("{'detailMessage':'hello'}", RuntimeException.class);
+ assertEquals("hello", e.getMessage());
+ }
+
+ public void testExceptionWithCause() {
+ Exception e = new Exception("top level", new IOException("io error"));
+ String json = gson.toJson(e);
+ assertTrue(json.contains("{\"detailMessage\":\"top level\",\"cause\":{\"detailMessage\":\"io error\""));
+
+ e = gson.fromJson("{'detailMessage':'top level','cause':{'detailMessage':'io error'}}", Exception.class);
+ assertEquals("top level", e.getMessage());
+ assertTrue(e.getCause() instanceof Throwable); // cause is not parameterized so type info is lost
+ assertEquals("io error", e.getCause().getMessage());
+ }
+
+ public void testSerializedNameOnExceptionFields() {
+ MyException e = new MyException();
+ String json = gson.toJson(e);
+ assertTrue(json.contains("{\"my_custom_name\":\"myCustomMessageValue\""));
+ }
+
+ public void testErrorWithoutCause() {
+ OutOfMemoryError e = new OutOfMemoryError("hello");
+ String json = gson.toJson(e);
+ assertTrue(json.contains("hello"));
+
+ e = gson.fromJson("{'detailMessage':'hello'}", OutOfMemoryError.class);
+ assertEquals("hello", e.getMessage());
+ }
+
+ public void testErrornWithCause() {
+ Error e = new Error("top level", new IOException("io error"));
+ String json = gson.toJson(e);
+ assertTrue(json.contains("top level"));
+ assertTrue(json.contains("io error"));
+
+ e = gson.fromJson("{'detailMessage':'top level','cause':{'detailMessage':'io error'}}", Error.class);
+ assertEquals("top level", e.getMessage());
+ assertTrue(e.getCause() instanceof Throwable); // cause is not parameterized so type info is lost
+ assertEquals("io error", e.getCause().getMessage());
+ }
+
+ private static final class MyException extends Throwable {
+ @SerializedName("my_custom_name") String myCustomMessage = "myCustomMessageValue";
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java
new file mode 100644
index 00000000..53d1c5cf
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/TreeTypeAdaptersTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2011 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 java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Collection of functional tests for DOM tree based type adapters.
+ */
+public class TreeTypeAdaptersTest extends TestCase {
+ private static final Id<Student> STUDENT1_ID = new Id<Student>("5", Student.class);
+ private static final Id<Student> STUDENT2_ID = new Id<Student>("6", Student.class);
+ private static final Student STUDENT1 = new Student(STUDENT1_ID, "first");
+ private static final Student STUDENT2 = new Student(STUDENT2_ID, "second");
+ private static final Type TYPE_COURSE_HISTORY =
+ new TypeToken<Course<HistoryCourse>>(){}.getType();
+ private static final Id<Course<HistoryCourse>> COURSE_ID =
+ new Id<Course<HistoryCourse>>("10", TYPE_COURSE_HISTORY);
+
+ private Gson gson;
+ private Course<HistoryCourse> course;
+
+ @Override
+ protected void setUp() {
+ gson = new GsonBuilder()
+ .registerTypeAdapter(Id.class, new IdTreeTypeAdapter())
+ .create();
+ course = new Course<HistoryCourse>(COURSE_ID, 4,
+ new Assignment<HistoryCourse>(null, null), createList(STUDENT1, STUDENT2));
+ }
+
+ public void testSerializeId() {
+ String json = gson.toJson(course, TYPE_COURSE_HISTORY);
+ assertTrue(json.contains(String.valueOf(COURSE_ID.getValue())));
+ assertTrue(json.contains(String.valueOf(STUDENT1_ID.getValue())));
+ assertTrue(json.contains(String.valueOf(STUDENT2_ID.getValue())));
+ }
+
+ public void testDeserializeId() {
+ String json = "{courseId:1,students:[{id:1,name:'first'},{id:6,name:'second'}],"
+ + "numAssignments:4,assignment:{}}";
+ Course<HistoryCourse> target = gson.fromJson(json, TYPE_COURSE_HISTORY);
+ assertEquals("1", target.getStudents().get(0).id.getValue());
+ assertEquals("6", target.getStudents().get(1).id.getValue());
+ assertEquals("1", target.getId().getValue());
+ }
+
+ private static final class Id<R> {
+ final String value;
+ @SuppressWarnings("unused")
+ final Type typeOfId;
+
+ private Id(String value, Type typeOfId) {
+ this.value = value;
+ this.typeOfId = typeOfId;
+ }
+ public String getValue() {
+ return value;
+ }
+ }
+
+ private static final class IdTreeTypeAdapter implements JsonSerializer<Id<?>>,
+ JsonDeserializer<Id<?>> {
+
+ @SuppressWarnings("rawtypes")
+ public Id<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ if (!(typeOfT instanceof ParameterizedType)) {
+ throw new JsonParseException("Id of unknown type: " + typeOfT);
+ }
+ ParameterizedType parameterizedType = (ParameterizedType) typeOfT;
+ // Since Id takes only one TypeVariable, the actual type corresponding to the first
+ // TypeVariable is the Type we are looking for
+ Type typeOfId = parameterizedType.getActualTypeArguments()[0];
+ return new Id(json.getAsString(), typeOfId);
+ }
+
+ public JsonElement serialize(Id<?> src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive(src.getValue());
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static class Student {
+ Id<Student> id;
+ String name;
+
+ private Student() {
+ this(null, null);
+ }
+ public Student(Id<Student> id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static class Course<T> {
+ final List<Student> students;
+ private final Id<Course<T>> courseId;
+ private final int numAssignments;
+ private final Assignment<T> assignment;
+
+ private Course() {
+ this(null, 0, null, new ArrayList<Student>());
+ }
+
+ public Course(Id<Course<T>> courseId, int numAssignments,
+ Assignment<T> assignment, List<Student> players) {
+ this.courseId = courseId;
+ this.numAssignments = numAssignments;
+ this.assignment = assignment;
+ this.students = players;
+ }
+ public Id<Course<T>> getId() {
+ return courseId;
+ }
+ List<Student> getStudents() {
+ return students;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static class Assignment<T> {
+ private final Id<Assignment<T>> id;
+ private final T data;
+
+ private Assignment() {
+ this(null, null);
+ }
+ public Assignment(Id<Assignment<T>> id, T data) {
+ this.id = id;
+ this.data = data;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static class HistoryCourse {
+ int numClasses;
+ }
+
+ private static <T> List<T> createList(T ...items) {
+ return Arrays.asList(items);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java b/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java
new file mode 100644
index 00000000..2f13f664
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2011 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.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import junit.framework.TestCase;
+
+public final class TypeAdapterPrecedenceTest extends TestCase {
+ public void testNonstreamingFollowedByNonstreaming() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Foo.class, newSerializer("serializer 1"))
+ .registerTypeAdapter(Foo.class, newSerializer("serializer 2"))
+ .registerTypeAdapter(Foo.class, newDeserializer("deserializer 1"))
+ .registerTypeAdapter(Foo.class, newDeserializer("deserializer 2"))
+ .create();
+ assertEquals("\"foo via serializer 2\"", gson.toJson(new Foo("foo")));
+ assertEquals("foo via deserializer 2", gson.fromJson("foo", Foo.class).name);
+ }
+
+ public void testStreamingFollowedByStreaming() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Foo.class, newTypeAdapter("type adapter 1"))
+ .registerTypeAdapter(Foo.class, newTypeAdapter("type adapter 2"))
+ .create();
+ assertEquals("\"foo via type adapter 2\"", gson.toJson(new Foo("foo")));
+ assertEquals("foo via type adapter 2", gson.fromJson("foo", Foo.class).name);
+ }
+
+ public void testSerializeNonstreamingTypeAdapterFollowedByStreamingTypeAdapter() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Foo.class, newSerializer("serializer"))
+ .registerTypeAdapter(Foo.class, newDeserializer("deserializer"))
+ .registerTypeAdapter(Foo.class, newTypeAdapter("type adapter"))
+ .create();
+ assertEquals("\"foo via type adapter\"", gson.toJson(new Foo("foo")));
+ assertEquals("foo via type adapter", gson.fromJson("foo", Foo.class).name);
+ }
+
+ public void testStreamingFollowedByNonstreaming() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Foo.class, newTypeAdapter("type adapter"))
+ .registerTypeAdapter(Foo.class, newSerializer("serializer"))
+ .registerTypeAdapter(Foo.class, newDeserializer("deserializer"))
+ .create();
+ assertEquals("\"foo via serializer\"", gson.toJson(new Foo("foo")));
+ assertEquals("foo via deserializer", gson.fromJson("foo", Foo.class).name);
+ }
+
+ public void testStreamingHierarchicalFollowedByNonstreaming() {
+ Gson gson = new GsonBuilder()
+ .registerTypeHierarchyAdapter(Foo.class, newTypeAdapter("type adapter"))
+ .registerTypeAdapter(Foo.class, newSerializer("serializer"))
+ .registerTypeAdapter(Foo.class, newDeserializer("deserializer"))
+ .create();
+ assertEquals("\"foo via serializer\"", gson.toJson(new Foo("foo")));
+ assertEquals("foo via deserializer", gson.fromJson("foo", Foo.class).name);
+ }
+
+ public void testStreamingFollowedByNonstreamingHierarchical() {
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Foo.class, newTypeAdapter("type adapter"))
+ .registerTypeHierarchyAdapter(Foo.class, newSerializer("serializer"))
+ .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);
+ }
+
+ public void testStreamingHierarchicalFollowedByNonstreamingHierarchical() {
+ Gson gson = new GsonBuilder()
+ .registerTypeHierarchyAdapter(Foo.class, newSerializer("serializer"))
+ .registerTypeHierarchyAdapter(Foo.class, newDeserializer("deserializer"))
+ .registerTypeHierarchyAdapter(Foo.class, newTypeAdapter("type adapter"))
+ .create();
+ assertEquals("\"foo via type adapter\"", gson.toJson(new Foo("foo")));
+ assertEquals("foo via type adapter", gson.fromJson("foo", Foo.class).name);
+ }
+
+ public void testNonstreamingHierarchicalFollowedByNonstreaming() {
+ Gson gson = new GsonBuilder()
+ .registerTypeHierarchyAdapter(Foo.class, newSerializer("hierarchical"))
+ .registerTypeHierarchyAdapter(Foo.class, newDeserializer("hierarchical"))
+ .registerTypeAdapter(Foo.class, newSerializer("non hierarchical"))
+ .registerTypeAdapter(Foo.class, newDeserializer("non hierarchical"))
+ .create();
+ assertEquals("\"foo via non hierarchical\"", gson.toJson(new Foo("foo")));
+ assertEquals("foo via non hierarchical", gson.fromJson("foo", Foo.class).name);
+ }
+
+ private static class Foo {
+ final String name;
+ private Foo(String name) {
+ this.name = name;
+ }
+ }
+
+ private JsonSerializer<Foo> newSerializer(final String name) {
+ return new JsonSerializer<Foo>() {
+ public JsonElement serialize(Foo src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive(src.name + " via " + name);
+ }
+ };
+ }
+
+ private JsonDeserializer<Foo> newDeserializer(final String name) {
+ return new JsonDeserializer<Foo>() {
+ public Foo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
+ return new Foo(json.getAsString() + " via " + name);
+ }
+ };
+ }
+
+ private TypeAdapter<Foo> newTypeAdapter(final String name) {
+ return new TypeAdapter<Foo>() {
+ @Override public Foo read(JsonReader in) throws IOException {
+ return new Foo(in.nextString() + " via " + name);
+ }
+ @Override public void write(JsonWriter out, Foo value) throws IOException {
+ out.value(value.name + " via " + name);
+ }
+ };
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/TypeHierarchyAdapterTest.java b/gson/src/test/java/com/google/gson/functional/TypeHierarchyAdapterTest.java
new file mode 100644
index 00000000..aa2f8f83
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/TypeHierarchyAdapterTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2011 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.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import java.lang.reflect.Type;
+import junit.framework.TestCase;
+
+/**
+ * Test that the hierarchy adapter works when subtypes are used.
+ */
+public final class TypeHierarchyAdapterTest extends TestCase {
+
+ public void testTypeHierarchy() {
+ Manager andy = new Manager();
+ andy.userid = "andy";
+ andy.startDate = 2005;
+ andy.minions = new Employee[] {
+ new Employee("inder", 2007),
+ new Employee("joel", 2006),
+ new Employee("jesse", 2006),
+ };
+
+ CEO eric = new CEO();
+ eric.userid = "eric";
+ eric.startDate = 2001;
+ eric.assistant = new Employee("jerome", 2006);
+
+ eric.minions = new Employee[] {
+ new Employee("larry", 1998),
+ new Employee("sergey", 1998),
+ andy,
+ };
+
+ Gson gson = new GsonBuilder()
+ .registerTypeHierarchyAdapter(Employee.class, new EmployeeAdapter())
+ .setPrettyPrinting()
+ .create();
+
+ Company company = new Company();
+ company.ceo = eric;
+
+ String json = gson.toJson(company, Company.class);
+ assertEquals("{\n" +
+ " \"ceo\": {\n" +
+ " \"userid\": \"eric\",\n" +
+ " \"startDate\": 2001,\n" +
+ " \"minions\": [\n" +
+ " {\n" +
+ " \"userid\": \"larry\",\n" +
+ " \"startDate\": 1998\n" +
+ " },\n" +
+ " {\n" +
+ " \"userid\": \"sergey\",\n" +
+ " \"startDate\": 1998\n" +
+ " },\n" +
+ " {\n" +
+ " \"userid\": \"andy\",\n" +
+ " \"startDate\": 2005,\n" +
+ " \"minions\": [\n" +
+ " {\n" +
+ " \"userid\": \"inder\",\n" +
+ " \"startDate\": 2007\n" +
+ " },\n" +
+ " {\n" +
+ " \"userid\": \"joel\",\n" +
+ " \"startDate\": 2006\n" +
+ " },\n" +
+ " {\n" +
+ " \"userid\": \"jesse\",\n" +
+ " \"startDate\": 2006\n" +
+ " }\n" +
+ " ]\n" +
+ " }\n" +
+ " ],\n" +
+ " \"assistant\": {\n" +
+ " \"userid\": \"jerome\",\n" +
+ " \"startDate\": 2006\n" +
+ " }\n" +
+ " }\n" +
+ "}", json);
+
+ Company copied = gson.fromJson(json, Company.class);
+ assertEquals(json, gson.toJson(copied, Company.class));
+ assertEquals(copied.ceo.userid, company.ceo.userid);
+ assertEquals(copied.ceo.assistant.userid, company.ceo.assistant.userid);
+ assertEquals(copied.ceo.minions[0].userid, company.ceo.minions[0].userid);
+ assertEquals(copied.ceo.minions[1].userid, company.ceo.minions[1].userid);
+ assertEquals(copied.ceo.minions[2].userid, company.ceo.minions[2].userid);
+ assertEquals(((Manager) copied.ceo.minions[2]).minions[0].userid,
+ ((Manager) company.ceo.minions[2]).minions[0].userid);
+ assertEquals(((Manager) copied.ceo.minions[2]).minions[1].userid,
+ ((Manager) company.ceo.minions[2]).minions[1].userid);
+ }
+
+ public void testRegisterSuperTypeFirst() {
+ Gson gson = new GsonBuilder()
+ .registerTypeHierarchyAdapter(Employee.class, new EmployeeAdapter())
+ .registerTypeHierarchyAdapter(Manager.class, new ManagerAdapter())
+ .create();
+
+ Manager manager = new Manager();
+ manager.userid = "inder";
+
+ String json = gson.toJson(manager, Manager.class);
+ assertEquals("\"inder\"", json);
+ Manager copied = gson.fromJson(json, Manager.class);
+ assertEquals(manager.userid, copied.userid);
+ }
+
+ /** This behaviour changed in Gson 2.1; it used to throw. */
+ public void testRegisterSubTypeFirstAllowed() {
+ new GsonBuilder()
+ .registerTypeHierarchyAdapter(Manager.class, new ManagerAdapter())
+ .registerTypeHierarchyAdapter(Employee.class, new EmployeeAdapter())
+ .create();
+ }
+
+ static class ManagerAdapter implements JsonSerializer<Manager>, JsonDeserializer<Manager> {
+ public Manager deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
+ Manager result = new Manager();
+ result.userid = json.getAsString();
+ return result;
+ }
+ public JsonElement serialize(Manager src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive(src.userid);
+ }
+ }
+
+ static class EmployeeAdapter implements JsonSerializer<Employee>, JsonDeserializer<Employee> {
+ public JsonElement serialize(Employee employee, Type typeOfSrc,
+ JsonSerializationContext context) {
+ JsonObject result = new JsonObject();
+ result.add("userid", context.serialize(employee.userid, String.class));
+ result.add("startDate", context.serialize(employee.startDate, long.class));
+ if (employee instanceof Manager) {
+ result.add("minions", context.serialize(((Manager) employee).minions, Employee[].class));
+ if (employee instanceof CEO) {
+ result.add("assistant", context.serialize(((CEO) employee).assistant, Employee.class));
+ }
+ }
+ return result;
+ }
+
+ public Employee deserialize(JsonElement json, Type typeOfT,
+ JsonDeserializationContext context) throws JsonParseException {
+ JsonObject object = json.getAsJsonObject();
+ Employee result = null;
+
+ // if the employee has an assistant, she must be the CEO
+ JsonElement assistant = object.get("assistant");
+ if (assistant != null) {
+ result = new CEO();
+ ((CEO) result).assistant = context.deserialize(assistant, Employee.class);
+ }
+
+ // only managers have minions
+ JsonElement minons = object.get("minions");
+ if (minons != null) {
+ if (result == null) {
+ result = new Manager();
+ }
+ ((Manager) result).minions = context.deserialize(minons, Employee[].class);
+ }
+
+ if (result == null) {
+ result = new Employee();
+ }
+ result.userid = context.deserialize(object.get("userid"), String.class);
+ result.startDate = context.<Long>deserialize(object.get("startDate"), long.class);
+ return result;
+ }
+ }
+
+ static class Employee {
+ String userid;
+ long startDate;
+
+ Employee(String userid, long startDate) {
+ this.userid = userid;
+ this.startDate = startDate;
+ }
+
+ Employee() {}
+ }
+
+ static class Manager extends Employee {
+ Employee[] minions;
+ }
+
+ static class CEO extends Manager {
+ Employee assistant;
+ }
+
+ static class Company {
+ CEO ceo;
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/TypeVariableTest.java b/gson/src/test/java/com/google/gson/functional/TypeVariableTest.java
new file mode 100644
index 00000000..2d7503eb
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/TypeVariableTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 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.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Functional test for Gson serialization and deserialization of
+ * classes with type variables.
+ *
+ * @author Joel Leitch
+ */
+public class TypeVariableTest extends TestCase {
+
+ public void testAdvancedTypeVariables() throws Exception {
+ Gson gson = new Gson();
+ Bar bar1 = new Bar("someString", 1, true);
+ ArrayList<Integer> arrayList = new ArrayList<Integer>();
+ arrayList.add(1);
+ arrayList.add(2);
+ arrayList.add(3);
+ bar1.map.put("key1", arrayList);
+ bar1.map.put("key2", new ArrayList<Integer>());
+ String json = gson.toJson(bar1);
+
+ Bar bar2 = gson.fromJson(json, Bar.class);
+ assertEquals(bar1, bar2);
+ }
+
+ public void testTypeVariablesViaTypeParameter() throws Exception {
+ Gson gson = new Gson();
+ Foo<String, Integer> original = new Foo<String, Integer>("e", 5, false);
+ original.map.put("f", Arrays.asList(6, 7));
+ Type type = new TypeToken<Foo<String, Integer>>() {}.getType();
+ String json = gson.toJson(original, type);
+ assertEquals("{\"someSField\":\"e\",\"someTField\":5,\"map\":{\"f\":[6,7]},\"redField\":false}",
+ json);
+ assertEquals(original, gson.<Foo<String, Integer>>fromJson(json, type));
+ }
+
+ public void testBasicTypeVariables() throws Exception {
+ Gson gson = new Gson();
+ Blue blue1 = new Blue(true);
+ String json = gson.toJson(blue1);
+
+ Blue blue2 = gson.fromJson(json, Blue.class);
+ assertEquals(blue1, blue2);
+ }
+
+ public static class Blue extends Red<Boolean> {
+ public Blue() {
+ super(false);
+ }
+
+ public Blue(boolean value) {
+ super(value);
+ }
+
+ // Technically, we should implement hashcode too
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Blue)) {
+ return false;
+ }
+ Blue blue = (Blue) o;
+ return redField.equals(blue.redField);
+ }
+ }
+
+ public static class Red<S> {
+ protected S redField;
+
+ public Red() {}
+
+ public Red(S redField) {
+ this.redField = redField;
+ }
+ }
+
+ public static class Foo<S, T> extends Red<Boolean> {
+ private S someSField;
+ private T someTField;
+ public final Map<S, List<T>> map = new HashMap<S, List<T>>();
+
+ public Foo() {}
+
+ public Foo(S sValue, T tValue, Boolean redField) {
+ super(redField);
+ this.someSField = sValue;
+ this.someTField = tValue;
+ }
+
+ // Technically, we should implement hashcode too
+ @Override
+ @SuppressWarnings("unchecked")
+ public boolean equals(Object o) {
+ if (!(o instanceof Foo<?, ?>)) {
+ return false;
+ }
+ Foo<S, T> realFoo = (Foo<S, T>) o;
+ return redField.equals(realFoo.redField)
+ && someTField.equals(realFoo.someTField)
+ && someSField.equals(realFoo.someSField)
+ && map.equals(realFoo.map);
+ }
+ }
+
+ public static class Bar extends Foo<String, Integer> {
+ public Bar() {
+ this("", 0, false);
+ }
+
+ public Bar(String s, Integer i, boolean b) {
+ super(s, i, b);
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/UncategorizedTest.java b/gson/src/test/java/com/google/gson/functional/UncategorizedTest.java
new file mode 100644
index 00000000..62c7fa09
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/UncategorizedTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+import com.google.gson.common.TestTypes.ClassOverridingEquals;
+
+import com.google.gson.reflect.TypeToken;
+import java.util.Arrays;
+import java.util.List;
+import junit.framework.TestCase;
+
+import java.lang.reflect.Type;
+
+/**
+ * Functional tests that do not fall neatly into any of the existing classification.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class UncategorizedTest extends TestCase {
+
+ private Gson gson = null;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testInvalidJsonDeserializationFails() throws Exception {
+ try {
+ gson.fromJson("adfasdf1112,,,\":", BagOfPrimitives.class);
+ fail("Bad JSON should throw a ParseException");
+ } catch (JsonParseException expected) { }
+
+ try {
+ gson.fromJson("{adfasdf1112,,,\":}", BagOfPrimitives.class);
+ fail("Bad JSON should throw a ParseException");
+ } catch (JsonParseException expected) { }
+ }
+
+ public void testObjectEqualButNotSameSerialization() throws Exception {
+ ClassOverridingEquals objA = new ClassOverridingEquals();
+ ClassOverridingEquals objB = new ClassOverridingEquals();
+ objB.ref = objA;
+ String json = gson.toJson(objB);
+ assertEquals(objB.getExpectedJson(), json);
+ }
+
+ public void testStaticFieldsAreNotSerialized() {
+ BagOfPrimitives target = new BagOfPrimitives();
+ assertFalse(gson.toJson(target).contains("DEFAULT_VALUE"));
+ }
+
+ public void testGsonInstanceReusableForSerializationAndDeserialization() {
+ BagOfPrimitives bag = new BagOfPrimitives();
+ String json = gson.toJson(bag);
+ BagOfPrimitives deserialized = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals(bag, deserialized);
+ }
+
+ /**
+ * This test ensures that a custom deserializer is able to return a derived class instance for a
+ * base class object. For a motivation for this test, see Issue 37 and
+ * http://groups.google.com/group/google-gson/browse_thread/thread/677d56e9976d7761
+ */
+ public void testReturningDerivedClassesDuringDeserialization() {
+ Gson gson = new GsonBuilder().registerTypeAdapter(Base.class, new BaseTypeAdapter()).create();
+ String json = "{\"opType\":\"OP1\"}";
+ Base base = gson.fromJson(json, Base.class);
+ assertTrue(base instanceof Derived1);
+ assertEquals(OperationType.OP1, base.opType);
+
+ json = "{\"opType\":\"OP2\"}";
+ base = gson.fromJson(json, Base.class);
+ assertTrue(base instanceof Derived2);
+ assertEquals(OperationType.OP2, base.opType);
+ }
+
+ /**
+ * Test that trailing whitespace is ignored.
+ * http://code.google.com/p/google-gson/issues/detail?id=302
+ */
+ public void testTrailingWhitespace() throws Exception {
+ List<Integer> integers = gson.fromJson("[1,2,3] \n\n ",
+ new TypeToken<List<Integer>>() {}.getType());
+ assertEquals(Arrays.asList(1, 2, 3), integers);
+ }
+
+ private enum OperationType { OP1, OP2 }
+ private static class Base {
+ OperationType opType;
+ }
+ private static class Derived1 extends Base {
+ Derived1() { opType = OperationType.OP1; }
+ }
+ private static class Derived2 extends Base {
+ Derived2() { opType = OperationType.OP2; }
+ }
+ private static class BaseTypeAdapter implements JsonDeserializer<Base> {
+ public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ String opTypeStr = json.getAsJsonObject().get("opType").getAsString();
+ OperationType opType = OperationType.valueOf(opTypeStr);
+ switch (opType) {
+ case OP1:
+ return new Derived1();
+ case OP2:
+ return new Derived2();
+ }
+ throw new JsonParseException("unknown type: " + json);
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/functional/VersioningTest.java b/gson/src/test/java/com/google/gson/functional/VersioningTest.java
new file mode 100644
index 00000000..bc526de0
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/functional/VersioningTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.GsonBuilder;
+import com.google.gson.annotations.Since;
+import com.google.gson.annotations.Until;
+import com.google.gson.common.TestTypes.BagOfPrimitives;
+
+import junit.framework.TestCase;
+
+/**
+ * Functional tests for versioning support in Gson.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class VersioningTest extends TestCase {
+ private static final int A = 0;
+ private static final int B = 1;
+ private static final int C = 2;
+ private static final int D = 3;
+
+ private GsonBuilder builder;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ builder = new GsonBuilder();
+ }
+
+ public void testVersionedUntilSerialization() {
+ Version1 target = new Version1();
+ Gson gson = builder.setVersion(1.29).create();
+ String json = gson.toJson(target);
+ assertTrue(json.contains("\"a\":" + A));
+
+ gson = builder.setVersion(1.3).create();
+ json = gson.toJson(target);
+ assertFalse(json.contains("\"a\":" + A));
+ }
+
+ public void testVersionedUntilDeserialization() {
+ Gson gson = builder.setVersion(1.3).create();
+ String json = "{\"a\":3,\"b\":4,\"c\":5}";
+ Version1 version1 = gson.fromJson(json, Version1.class);
+ assertEquals(A, version1.a);
+ }
+
+ public void testVersionedClassesSerialization() {
+ Gson gson = builder.setVersion(1.0).create();
+ String json1 = gson.toJson(new Version1());
+ String json2 = gson.toJson(new Version1_1());
+ assertEquals(json1, json2);
+ }
+
+ public void testVersionedClassesDeserialization() {
+ Gson gson = builder.setVersion(1.0).create();
+ String json = "{\"a\":3,\"b\":4,\"c\":5}";
+ Version1 version1 = gson.fromJson(json, Version1.class);
+ assertEquals(3, version1.a);
+ assertEquals(4, version1.b);
+ Version1_1 version1_1 = gson.fromJson(json, Version1_1.class);
+ assertEquals(3, version1_1.a);
+ assertEquals(4, version1_1.b);
+ assertEquals(C, version1_1.c);
+ }
+
+ public void testIgnoreLaterVersionClassSerialization() {
+ Gson gson = builder.setVersion(1.0).create();
+ assertEquals("null", gson.toJson(new Version1_2()));
+ }
+
+ public void testIgnoreLaterVersionClassDeserialization() {
+ Gson gson = builder.setVersion(1.0).create();
+ String json = "{\"a\":3,\"b\":4,\"c\":5,\"d\":6}";
+ Version1_2 version1_2 = gson.fromJson(json, Version1_2.class);
+ // Since the class is versioned to be after 1.0, we expect null
+ // This is the new behavior in Gson 2.0
+ assertNull(version1_2);
+ }
+
+ public void testVersionedGsonWithUnversionedClassesSerialization() {
+ Gson gson = builder.setVersion(1.0).create();
+ BagOfPrimitives target = new BagOfPrimitives(10, 20, false, "stringValue");
+ assertEquals(target.getExpectedJson(), gson.toJson(target));
+ }
+
+ public void testVersionedGsonWithUnversionedClassesDeserialization() {
+ Gson gson = builder.setVersion(1.0).create();
+ String json = "{\"longValue\":10,\"intValue\":20,\"booleanValue\":false}";
+
+ BagOfPrimitives expected = new BagOfPrimitives();
+ expected.longValue = 10;
+ expected.intValue = 20;
+ expected.booleanValue = false;
+ BagOfPrimitives actual = gson.fromJson(json, BagOfPrimitives.class);
+ assertEquals(expected, actual);
+ }
+
+ public void testVersionedGsonMixingSinceAndUntilSerialization() {
+ Gson gson = builder.setVersion(1.0).create();
+ SinceUntilMixing target = new SinceUntilMixing();
+ String json = gson.toJson(target);
+ assertFalse(json.contains("\"b\":" + B));
+
+ gson = builder.setVersion(1.2).create();
+ json = gson.toJson(target);
+ assertTrue(json.contains("\"b\":" + B));
+
+ gson = builder.setVersion(1.3).create();
+ json = gson.toJson(target);
+ assertFalse(json.contains("\"b\":" + B));
+ }
+
+ public void testVersionedGsonMixingSinceAndUntilDeserialization() {
+ String json = "{\"a\":5,\"b\":6}";
+ Gson gson = builder.setVersion(1.0).create();
+ SinceUntilMixing result = gson.fromJson(json, SinceUntilMixing.class);
+ assertEquals(5, result.a);
+ assertEquals(B, result.b);
+
+ gson = builder.setVersion(1.2).create();
+ result = gson.fromJson(json, SinceUntilMixing.class);
+ assertEquals(5, result.a);
+ assertEquals(6, result.b);
+
+ gson = builder.setVersion(1.3).create();
+ result = gson.fromJson(json, SinceUntilMixing.class);
+ assertEquals(5, result.a);
+ assertEquals(B, result.b);
+ }
+
+ private static class Version1 {
+ @Until(1.3) int a = A;
+ @Since(1.0) int b = B;
+ }
+
+ private static class Version1_1 extends Version1 {
+ @Since(1.1) int c = C;
+ }
+
+ @Since(1.2)
+ private static class Version1_2 extends Version1_1 {
+ @SuppressWarnings("unused")
+ int d = D;
+ }
+
+ private static class SinceUntilMixing {
+ int a = A;
+
+ @Since(1.1)
+ @Until(1.3)
+ int b = B;
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java b/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java
new file mode 100644
index 00000000..c80700bd
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 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.internal;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+public final class GsonTypesTest extends TestCase {
+
+ public void testNewParameterizedTypeWithoutOwner() throws Exception {
+ // List<A>. List is a top-level class
+ Type type = $Gson$Types.newParameterizedTypeWithOwner(null, List.class, A.class);
+ assertEquals(A.class, getFirstTypeArgument(type));
+
+ // A<B>. A is a static inner class.
+ type = $Gson$Types.newParameterizedTypeWithOwner(null, A.class, B.class);
+ assertEquals(B.class, getFirstTypeArgument(type));
+
+ final class D {
+ }
+ try {
+ // D<A> is not allowed since D is not a static inner class
+ $Gson$Types.newParameterizedTypeWithOwner(null, D.class, A.class);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ // A<D> is allowed.
+ type = $Gson$Types.newParameterizedTypeWithOwner(null, A.class, D.class);
+ assertEquals(D.class, getFirstTypeArgument(type));
+ }
+
+ public void testGetFirstTypeArgument() throws Exception {
+ assertNull(getFirstTypeArgument(A.class));
+
+ Type type = $Gson$Types.newParameterizedTypeWithOwner(null, A.class, B.class, C.class);
+ assertEquals(B.class, getFirstTypeArgument(type));
+ }
+
+ private static final class A {
+ }
+ private static final class B {
+ }
+ private static final class C {
+ }
+
+ /**
+ * Given a parameterized type A&lt;B,C&gt;, returns B. If the specified type is not
+ * a generic type, returns null.
+ */
+ public static Type getFirstTypeArgument(Type type) throws Exception {
+ if (!(type instanceof ParameterizedType)) return null;
+ ParameterizedType ptype = (ParameterizedType) type;
+ Type[] actualTypeArguments = ptype.getActualTypeArguments();
+ if (actualTypeArguments.length == 0) return null;
+ return $Gson$Types.canonicalize(actualTypeArguments[0]);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/internal/LazilyParsedNumberTest.java b/gson/src/test/java/com/google/gson/internal/LazilyParsedNumberTest.java
new file mode 100644
index 00000000..f108fa0d
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/internal/LazilyParsedNumberTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 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.internal;
+
+import junit.framework.TestCase;
+
+public class LazilyParsedNumberTest extends TestCase {
+ public void testHashCode() {
+ LazilyParsedNumber n1 = new LazilyParsedNumber("1");
+ LazilyParsedNumber n1Another = new LazilyParsedNumber("1");
+ assertEquals(n1.hashCode(), n1Another.hashCode());
+ }
+
+ public void testEquals() {
+ LazilyParsedNumber n1 = new LazilyParsedNumber("1");
+ LazilyParsedNumber n1Another = new LazilyParsedNumber("1");
+ assertTrue(n1.equals(n1Another));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/internal/LinkedHashTreeMapTest.java b/gson/src/test/java/com/google/gson/internal/LinkedHashTreeMapTest.java
new file mode 100644
index 00000000..2aeeeb76
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/internal/LinkedHashTreeMapTest.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2012 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.internal;
+
+import com.google.gson.common.MoreAsserts;
+import com.google.gson.internal.LinkedHashTreeMap.AvlBuilder;
+import com.google.gson.internal.LinkedHashTreeMap.AvlIterator;
+import com.google.gson.internal.LinkedHashTreeMap.Node;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import junit.framework.TestCase;
+
+public final class LinkedHashTreeMapTest extends TestCase {
+ public void testIterationOrder() {
+ LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>();
+ map.put("a", "android");
+ map.put("c", "cola");
+ map.put("b", "bbq");
+ assertIterationOrder(map.keySet(), "a", "c", "b");
+ assertIterationOrder(map.values(), "android", "cola", "bbq");
+ }
+
+ public void testRemoveRootDoesNotDoubleUnlink() {
+ LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>();
+ map.put("a", "android");
+ map.put("c", "cola");
+ map.put("b", "bbq");
+ Iterator<Map.Entry<String,String>> it = map.entrySet().iterator();
+ it.next();
+ it.next();
+ it.next();
+ it.remove();
+ assertIterationOrder(map.keySet(), "a", "c");
+ }
+
+ public void testPutNullKeyFails() {
+ LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>();
+ try {
+ map.put(null, "android");
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testPutNonComparableKeyFails() {
+ LinkedHashTreeMap<Object, String> map = new LinkedHashTreeMap<Object, String>();
+ try {
+ map.put(new Object(), "android");
+ fail();
+ } catch (ClassCastException expected) {}
+ }
+
+ public void testContainsNonComparableKeyReturnsFalse() {
+ LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>();
+ map.put("a", "android");
+ assertFalse(map.containsKey(new Object()));
+ }
+
+ public void testContainsNullKeyIsAlwaysFalse() {
+ LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>();
+ map.put("a", "android");
+ assertFalse(map.containsKey(null));
+ }
+
+ public void testPutOverrides() throws Exception {
+ LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>();
+ assertNull(map.put("d", "donut"));
+ assertNull(map.put("e", "eclair"));
+ assertNull(map.put("f", "froyo"));
+ assertEquals(3, map.size());
+
+ assertEquals("donut", map.get("d"));
+ assertEquals("donut", map.put("d", "done"));
+ assertEquals(3, map.size());
+ }
+
+ public void testEmptyStringValues() {
+ LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>();
+ map.put("a", "");
+ assertTrue(map.containsKey("a"));
+ assertEquals("", map.get("a"));
+ }
+
+ // NOTE that this does not happen every time, but given the below predictable random,
+ // this test will consistently fail (assuming the initial size is 16 and rehashing
+ // size remains at 3/4)
+ public void testForceDoublingAndRehash() throws Exception {
+ Random random = new Random(1367593214724L);
+ LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>();
+ String[] keys = new String[1000];
+ for (int i = 0; i < keys.length; i++) {
+ keys[i] = Integer.toString(Math.abs(random.nextInt()), 36) + "-" + i;
+ map.put(keys[i], "" + i);
+ }
+
+ for (int i = 0; i < keys.length; i++) {
+ String key = keys[i];
+ assertTrue(map.containsKey(key));
+ assertEquals("" + i, map.get(key));
+ }
+ }
+
+ public void testClear() {
+ LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<String, String>();
+ map.put("a", "android");
+ map.put("c", "cola");
+ map.put("b", "bbq");
+ map.clear();
+ assertIterationOrder(map.keySet());
+ assertEquals(0, map.size());
+ }
+
+ public void testEqualsAndHashCode() throws Exception {
+ LinkedHashTreeMap<String, Integer> map1 = new LinkedHashTreeMap<String, Integer>();
+ map1.put("A", 1);
+ map1.put("B", 2);
+ map1.put("C", 3);
+ map1.put("D", 4);
+
+ LinkedHashTreeMap<String, Integer> map2 = new LinkedHashTreeMap<String, Integer>();
+ map2.put("C", 3);
+ map2.put("B", 2);
+ map2.put("D", 4);
+ map2.put("A", 1);
+
+ MoreAsserts.assertEqualsAndHashCode(map1, map2);
+ }
+
+ public void testAvlWalker() {
+ assertAvlWalker(node(node("a"), "b", node("c")),
+ "a", "b", "c");
+ assertAvlWalker(node(node(node("a"), "b", node("c")), "d", node(node("e"), "f", node("g"))),
+ "a", "b", "c", "d", "e", "f", "g");
+ assertAvlWalker(node(node(null, "a", node("b")), "c", node(node("d"), "e", null)),
+ "a", "b", "c", "d", "e");
+ assertAvlWalker(node(null, "a", node(null, "b", node(null, "c", node("d")))),
+ "a", "b", "c", "d");
+ assertAvlWalker(node(node(node(node("a"), "b", null), "c", null), "d", null),
+ "a", "b", "c", "d");
+ }
+
+ private void assertAvlWalker(Node<String, String> root, String... values) {
+ AvlIterator<String, String> iterator = new AvlIterator<String, String>();
+ iterator.reset(root);
+ for (String value : values) {
+ assertEquals(value, iterator.next().getKey());
+ }
+ assertNull(iterator.next());
+ }
+
+ public void testAvlBuilder() {
+ assertAvlBuilder(1, "a");
+ assertAvlBuilder(2, "(. a b)");
+ assertAvlBuilder(3, "(a b c)");
+ assertAvlBuilder(4, "(a b (. c d))");
+ assertAvlBuilder(5, "(a b (c d e))");
+ assertAvlBuilder(6, "((. a b) c (d e f))");
+ assertAvlBuilder(7, "((a b c) d (e f g))");
+ assertAvlBuilder(8, "((a b c) d (e f (. g h)))");
+ assertAvlBuilder(9, "((a b c) d (e f (g h i)))");
+ assertAvlBuilder(10, "((a b c) d ((. e f) g (h i j)))");
+ assertAvlBuilder(11, "((a b c) d ((e f g) h (i j k)))");
+ assertAvlBuilder(12, "((a b (. c d)) e ((f g h) i (j k l)))");
+ assertAvlBuilder(13, "((a b (c d e)) f ((g h i) j (k l m)))");
+ assertAvlBuilder(14, "(((. a b) c (d e f)) g ((h i j) k (l m n)))");
+ assertAvlBuilder(15, "(((a b c) d (e f g)) h ((i j k) l (m n o)))");
+ assertAvlBuilder(16, "(((a b c) d (e f g)) h ((i j k) l (m n (. o p))))");
+ assertAvlBuilder(30, "((((. a b) c (d e f)) g ((h i j) k (l m n))) o "
+ + "(((p q r) s (t u v)) w ((x y z) A (B C D))))");
+ assertAvlBuilder(31, "((((a b c) d (e f g)) h ((i j k) l (m n o))) p "
+ + "(((q r s) t (u v w)) x ((y z A) B (C D E))))");
+ }
+
+ private void assertAvlBuilder(int size, String expected) {
+ char[] values = "abcdefghijklmnopqrstuvwxyzABCDE".toCharArray();
+ AvlBuilder<String, String> avlBuilder = new AvlBuilder<String, String>();
+ avlBuilder.reset(size);
+ for (int i = 0; i < size; i++) {
+ avlBuilder.add(node(Character.toString(values[i])));
+ }
+ assertTree(expected, avlBuilder.root());
+ }
+
+ public void testDoubleCapacity() {
+ @SuppressWarnings("unchecked") // Arrays and generics don't get along.
+ Node<String, String>[] oldTable = new Node[1];
+ oldTable[0] = node(node(node("a"), "b", node("c")), "d", node(node("e"), "f", node("g")));
+
+ Node<String, String>[] newTable = LinkedHashTreeMap.doubleCapacity(oldTable);
+ assertTree("(b d f)", newTable[0]); // Even hash codes!
+ assertTree("(a c (. e g))", newTable[1]); // Odd hash codes!
+ }
+
+ public void testDoubleCapacityAllNodesOnLeft() {
+ @SuppressWarnings("unchecked") // Arrays and generics don't get along.
+ Node<String, String>[] oldTable = new Node[1];
+ oldTable[0] = node(node("b"), "d", node("f"));
+
+ Node<String, String>[] newTable = LinkedHashTreeMap.doubleCapacity(oldTable);
+ assertTree("(b d f)", newTable[0]); // Even hash codes!
+ assertNull(newTable[1]); // Odd hash codes!
+
+ for (Node<?, ?> node : newTable) {
+ if (node != null) {
+ assertConsistent(node);
+ }
+ }
+ }
+
+ private static final Node<String, String> head = new Node<String, String>();
+
+ private Node<String, String> node(String value) {
+ return new Node<String, String>(null, value, value.hashCode(), head, head);
+ }
+
+ private Node<String, String> node(Node<String, String> left, String value,
+ Node<String, String> right) {
+ Node<String, String> result = node(value);
+ if (left != null) {
+ result.left = left;
+ left.parent = result;
+ }
+ if (right != null) {
+ result.right = right;
+ right.parent = result;
+ }
+ return result;
+ }
+
+ private void assertTree(String expected, Node<?, ?> root) {
+ assertEquals(expected, toString(root));
+ assertConsistent(root);
+ }
+
+ private void assertConsistent(Node<?, ?> node) {
+ int leftHeight = 0;
+ if (node.left != null) {
+ assertConsistent(node.left);
+ assertSame(node, node.left.parent);
+ leftHeight = node.left.height;
+ }
+ int rightHeight = 0;
+ if (node.right != null) {
+ assertConsistent(node.right);
+ assertSame(node, node.right.parent);
+ rightHeight = node.right.height;
+ }
+ if (node.parent != null) {
+ assertTrue(node.parent.left == node || node.parent.right == node);
+ }
+ if (Math.max(leftHeight, rightHeight) + 1 != node.height) {
+ fail();
+ }
+ }
+
+ private String toString(Node<?, ?> root) {
+ if (root == null) {
+ return ".";
+ } else if (root.left == null && root.right == null) {
+ return String.valueOf(root.key);
+ } else {
+ return String.format("(%s %s %s)", toString(root.left), root.key, toString(root.right));
+ }
+ }
+
+ private <T> void assertIterationOrder(Iterable<T> actual, T... expected) {
+ ArrayList<T> actualList = new ArrayList<T>();
+ for (T t : actual) {
+ actualList.add(t);
+ }
+ assertEquals(Arrays.asList(expected), actualList);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java b/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java
new file mode 100644
index 00000000..580d25a5
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2012 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.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+import com.google.gson.common.MoreAsserts;
+
+public final class LinkedTreeMapTest extends TestCase {
+
+ public void testIterationOrder() {
+ LinkedTreeMap<String, String> map = new LinkedTreeMap<String, String>();
+ map.put("a", "android");
+ map.put("c", "cola");
+ map.put("b", "bbq");
+ assertIterationOrder(map.keySet(), "a", "c", "b");
+ assertIterationOrder(map.values(), "android", "cola", "bbq");
+ }
+
+ public void testRemoveRootDoesNotDoubleUnlink() {
+ LinkedTreeMap<String, String> map = new LinkedTreeMap<String, String>();
+ map.put("a", "android");
+ map.put("c", "cola");
+ map.put("b", "bbq");
+ Iterator<Map.Entry<String,String>> it = map.entrySet().iterator();
+ it.next();
+ it.next();
+ it.next();
+ it.remove();
+ assertIterationOrder(map.keySet(), "a", "c");
+ }
+
+ public void testPutNullKeyFails() {
+ LinkedTreeMap<String, String> map = new LinkedTreeMap<String, String>();
+ try {
+ map.put(null, "android");
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testPutNonComparableKeyFails() {
+ LinkedTreeMap<Object, String> map = new LinkedTreeMap<Object, String>();
+ try {
+ map.put(new Object(), "android");
+ fail();
+ } catch (ClassCastException expected) {}
+ }
+
+ public void testContainsNonComparableKeyReturnsFalse() {
+ LinkedTreeMap<String, String> map = new LinkedTreeMap<String, String>();
+ map.put("a", "android");
+ assertFalse(map.containsKey(new Object()));
+ }
+
+ public void testContainsNullKeyIsAlwaysFalse() {
+ LinkedTreeMap<String, String> map = new LinkedTreeMap<String, String>();
+ map.put("a", "android");
+ assertFalse(map.containsKey(null));
+ }
+
+ public void testPutOverrides() throws Exception {
+ LinkedTreeMap<String, String> map = new LinkedTreeMap<String, String>();
+ assertNull(map.put("d", "donut"));
+ assertNull(map.put("e", "eclair"));
+ assertNull(map.put("f", "froyo"));
+ assertEquals(3, map.size());
+
+ assertEquals("donut", map.get("d"));
+ assertEquals("donut", map.put("d", "done"));
+ assertEquals(3, map.size());
+ }
+
+ public void testEmptyStringValues() {
+ LinkedTreeMap<String, String> map = new LinkedTreeMap<String, String>();
+ map.put("a", "");
+ assertTrue(map.containsKey("a"));
+ assertEquals("", map.get("a"));
+ }
+
+ public void testLargeSetOfRandomKeys() throws Exception {
+ Random random = new Random(1367593214724L);
+ LinkedTreeMap<String, String> map = new LinkedTreeMap<String, String>();
+ String[] keys = new String[1000];
+ for (int i = 0; i < keys.length; i++) {
+ keys[i] = Integer.toString(Math.abs(random.nextInt()), 36) + "-" + i;
+ map.put(keys[i], "" + i);
+ }
+
+ for (int i = 0; i < keys.length; i++) {
+ String key = keys[i];
+ assertTrue(map.containsKey(key));
+ assertEquals("" + i, map.get(key));
+ }
+ }
+
+ public void testClear() {
+ LinkedTreeMap<String, String> map = new LinkedTreeMap<String, String>();
+ map.put("a", "android");
+ map.put("c", "cola");
+ map.put("b", "bbq");
+ map.clear();
+ assertIterationOrder(map.keySet());
+ assertEquals(0, map.size());
+ }
+
+ public void testEqualsAndHashCode() throws Exception {
+ LinkedTreeMap<String, Integer> map1 = new LinkedTreeMap<String, Integer>();
+ map1.put("A", 1);
+ map1.put("B", 2);
+ map1.put("C", 3);
+ map1.put("D", 4);
+
+ LinkedTreeMap<String, Integer> map2 = new LinkedTreeMap<String, Integer>();
+ map2.put("C", 3);
+ map2.put("B", 2);
+ map2.put("D", 4);
+ map2.put("A", 1);
+
+ MoreAsserts.assertEqualsAndHashCode(map1, map2);
+ }
+
+ private <T> void assertIterationOrder(Iterable<T> actual, T... expected) {
+ ArrayList<T> actualList = new ArrayList<T>();
+ for (T t : actual) {
+ actualList.add(t);
+ }
+ assertEquals(Arrays.asList(expected), actualList);
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/internal/bind/JsonElementReaderTest.java b/gson/src/test/java/com/google/gson/internal/bind/JsonElementReaderTest.java
new file mode 100644
index 00000000..10624711
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/internal/bind/JsonElementReaderTest.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2011 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.internal.bind;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.stream.JsonToken;
+import java.io.IOException;
+import junit.framework.TestCase;
+
+@SuppressWarnings("resource")
+public final class JsonElementReaderTest extends TestCase {
+
+ public void testNumbers() throws IOException {
+ JsonElement element = new JsonParser().parse("[1, 2, 3]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ assertEquals(1, reader.nextInt());
+ assertEquals(2L, reader.nextLong());
+ assertEquals(3.0, reader.nextDouble());
+ reader.endArray();
+ }
+
+ public void testLenientNansAndInfinities() throws IOException {
+ JsonElement element = new JsonParser().parse("[NaN, -Infinity, Infinity]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.setLenient(true);
+ reader.beginArray();
+ assertTrue(Double.isNaN(reader.nextDouble()));
+ assertEquals(Double.NEGATIVE_INFINITY, reader.nextDouble());
+ assertEquals(Double.POSITIVE_INFINITY, reader.nextDouble());
+ reader.endArray();
+ }
+
+ public void testStrictNansAndInfinities() throws IOException {
+ JsonElement element = new JsonParser().parse("[NaN, -Infinity, Infinity]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.setLenient(false);
+ reader.beginArray();
+ try {
+ reader.nextDouble();
+ fail();
+ } catch (NumberFormatException e) {
+ }
+ assertEquals("NaN", reader.nextString());
+ try {
+ reader.nextDouble();
+ fail();
+ } catch (NumberFormatException e) {
+ }
+ assertEquals("-Infinity", reader.nextString());
+ try {
+ reader.nextDouble();
+ fail();
+ } catch (NumberFormatException e) {
+ }
+ assertEquals("Infinity", reader.nextString());
+ reader.endArray();
+ }
+
+ public void testNumbersFromStrings() throws IOException {
+ JsonElement element = new JsonParser().parse("[\"1\", \"2\", \"3\"]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ assertEquals(1, reader.nextInt());
+ assertEquals(2L, reader.nextLong());
+ assertEquals(3.0, reader.nextDouble());
+ reader.endArray();
+ }
+
+ public void testStringsFromNumbers() throws IOException {
+ JsonElement element = new JsonParser().parse("[1]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ assertEquals("1", reader.nextString());
+ reader.endArray();
+ }
+
+ public void testBooleans() throws IOException {
+ JsonElement element = new JsonParser().parse("[true, false]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ assertEquals(false, reader.nextBoolean());
+ reader.endArray();
+ }
+
+ public void testNulls() throws IOException {
+ JsonElement element = new JsonParser().parse("[null,null]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ reader.nextNull();
+ reader.nextNull();
+ reader.endArray();
+ }
+
+ public void testStrings() throws IOException {
+ JsonElement element = new JsonParser().parse("[\"A\",\"B\"]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ assertEquals("A", reader.nextString());
+ assertEquals("B", reader.nextString());
+ reader.endArray();
+ }
+
+ public void testArray() throws IOException {
+ JsonElement element = new JsonParser().parse("[1, 2, 3]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ assertEquals(JsonToken.BEGIN_ARRAY, reader.peek());
+ reader.beginArray();
+ assertEquals(JsonToken.NUMBER, reader.peek());
+ assertEquals(1, reader.nextInt());
+ assertEquals(JsonToken.NUMBER, reader.peek());
+ assertEquals(2, reader.nextInt());
+ assertEquals(JsonToken.NUMBER, reader.peek());
+ assertEquals(3, reader.nextInt());
+ assertEquals(JsonToken.END_ARRAY, reader.peek());
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testObject() throws IOException {
+ JsonElement element = new JsonParser().parse("{\"A\": 1, \"B\": 2}");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ assertEquals(JsonToken.BEGIN_OBJECT, reader.peek());
+ reader.beginObject();
+ assertEquals(JsonToken.NAME, reader.peek());
+ assertEquals("A", reader.nextName());
+ assertEquals(JsonToken.NUMBER, reader.peek());
+ assertEquals(1, reader.nextInt());
+ assertEquals(JsonToken.NAME, reader.peek());
+ assertEquals("B", reader.nextName());
+ assertEquals(JsonToken.NUMBER, reader.peek());
+ assertEquals(2, reader.nextInt());
+ assertEquals(JsonToken.END_OBJECT, reader.peek());
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testEmptyArray() throws IOException {
+ JsonElement element = new JsonParser().parse("[]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ reader.endArray();
+ }
+
+ public void testNestedArrays() throws IOException {
+ JsonElement element = new JsonParser().parse("[[],[[]]]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ reader.beginArray();
+ reader.endArray();
+ reader.beginArray();
+ reader.beginArray();
+ reader.endArray();
+ reader.endArray();
+ reader.endArray();
+ }
+
+ public void testNestedObjects() throws IOException {
+ JsonElement element = new JsonParser().parse("{\"A\":{},\"B\":{\"C\":{}}}");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginObject();
+ assertEquals("A", reader.nextName());
+ reader.beginObject();
+ reader.endObject();
+ assertEquals("B", reader.nextName());
+ reader.beginObject();
+ assertEquals("C", reader.nextName());
+ reader.beginObject();
+ reader.endObject();
+ reader.endObject();
+ reader.endObject();
+ }
+
+ public void testEmptyObject() throws IOException {
+ JsonElement element = new JsonParser().parse("{}");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginObject();
+ reader.endObject();
+ }
+
+ public void testSkipValue() throws IOException {
+ JsonElement element = new JsonParser().parse("[\"A\",{\"B\":[[]]},\"C\",[[]],\"D\",null]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ assertEquals("A", reader.nextString());
+ reader.skipValue();
+ assertEquals("C", reader.nextString());
+ reader.skipValue();
+ assertEquals("D", reader.nextString());
+ reader.skipValue();
+ reader.endArray();
+ }
+
+ public void testWrongType() throws IOException {
+ JsonElement element = new JsonParser().parse("[[],\"A\"]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.nextString();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.nextInt();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.nextLong();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.nextDouble();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.nextName();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.beginObject();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.endArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.endObject();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ reader.beginArray();
+ reader.endArray();
+
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.nextInt();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ try {
+ reader.nextLong();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ try {
+ reader.nextDouble();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ try {
+ reader.nextName();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ assertEquals("A", reader.nextString());
+ reader.endArray();
+ }
+
+ public void testEarlyClose() throws IOException {
+ JsonElement element = new JsonParser().parse("[1, 2, 3]");
+ JsonTreeReader reader = new JsonTreeReader(element);
+ reader.beginArray();
+ reader.close();
+ try {
+ reader.peek();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java
new file mode 100644
index 00000000..e07014d3
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2011 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.internal.bind;
+
+import com.google.gson.JsonNull;
+import java.io.IOException;
+import junit.framework.TestCase;
+
+@SuppressWarnings("resource")
+public final class JsonTreeWriterTest extends TestCase {
+ public void testArray() throws IOException {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.beginArray();
+ writer.value(1);
+ writer.value(2);
+ writer.value(3);
+ writer.endArray();
+ assertEquals("[1,2,3]", writer.get().toString());
+ }
+
+ public void testNestedArray() throws IOException {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.beginArray();
+ writer.beginArray();
+ writer.endArray();
+ writer.beginArray();
+ writer.beginArray();
+ writer.endArray();
+ writer.endArray();
+ writer.endArray();
+ assertEquals("[[],[[]]]", writer.get().toString());
+ }
+
+ public void testObject() throws IOException {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.beginObject();
+ writer.name("A").value(1);
+ writer.name("B").value(2);
+ writer.endObject();
+ assertEquals("{\"A\":1,\"B\":2}", writer.get().toString());
+ }
+
+ public void testNestedObject() throws IOException {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.beginObject();
+ writer.name("A");
+ writer.beginObject();
+ writer.name("B");
+ writer.beginObject();
+ writer.endObject();
+ writer.endObject();
+ writer.name("C");
+ writer.beginObject();
+ writer.endObject();
+ writer.endObject();
+ assertEquals("{\"A\":{\"B\":{}},\"C\":{}}", writer.get().toString());
+ }
+
+ public void testWriteAfterClose() throws Exception {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.setLenient(true);
+ writer.beginArray();
+ writer.value("A");
+ writer.endArray();
+ writer.close();
+ try {
+ writer.beginArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testPrematureClose() throws Exception {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.setLenient(true);
+ writer.beginArray();
+ try {
+ writer.close();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testSerializeNullsFalse() throws IOException {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.setSerializeNulls(false);
+ writer.beginObject();
+ writer.name("A");
+ writer.nullValue();
+ writer.endObject();
+ assertEquals("{}", writer.get().toString());
+ }
+
+ public void testSerializeNullsTrue() throws IOException {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.setSerializeNulls(true);
+ writer.beginObject();
+ writer.name("A");
+ writer.nullValue();
+ writer.endObject();
+ assertEquals("{\"A\":null}", writer.get().toString());
+ }
+
+ public void testEmptyWriter() {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ assertEquals(JsonNull.INSTANCE, writer.get());
+ }
+
+ public void testLenientNansAndInfinities() throws IOException {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.setLenient(true);
+ writer.beginArray();
+ writer.value(Double.NaN);
+ writer.value(Double.NEGATIVE_INFINITY);
+ writer.value(Double.POSITIVE_INFINITY);
+ writer.endArray();
+ assertEquals("[NaN,-Infinity,Infinity]", writer.get().toString());
+ }
+
+ public void testStrictNansAndInfinities() throws IOException {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.setLenient(false);
+ writer.beginArray();
+ try {
+ writer.value(Double.NaN);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ writer.value(Double.NEGATIVE_INFINITY);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ writer.value(Double.POSITIVE_INFINITY);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testStrictBoxedNansAndInfinities() throws IOException {
+ JsonTreeWriter writer = new JsonTreeWriter();
+ writer.setLenient(false);
+ writer.beginArray();
+ try {
+ writer.value(new Double(Double.NaN));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ writer.value(new Double(Double.NEGATIVE_INFINITY));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ writer.value(new Double(Double.POSITIVE_INFINITY));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java b/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java
new file mode 100644
index 00000000..cf444eee
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java
@@ -0,0 +1,346 @@
+/*
+ * 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.metrics;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonParseException;
+import com.google.gson.annotations.Expose;
+import com.google.gson.reflect.TypeToken;
+
+import junit.framework.TestCase;
+
+import java.io.StringWriter;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tests to measure performance for Gson. All tests in this file will be disabled in code. To run
+ * them remove disabled_ prefix from the tests and run them.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class PerformanceTest extends TestCase {
+ private static final int COLLECTION_SIZE = 5000;
+
+ private static final int NUM_ITERATIONS = 100;
+
+ private Gson gson;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ gson = new Gson();
+ }
+
+ public void testDummy() {
+ // This is here to prevent Junit for complaining when we disable all tests.
+ }
+
+ public void disabled_testStringDeserialization() {
+ StringBuilder sb = new StringBuilder(8096);
+ sb.append("Error Yippie");
+
+ while (true) {
+ try {
+ String stackTrace = sb.toString();
+ sb.append(stackTrace);
+ String json = "{\"message\":\"Error message.\"," + "\"stackTrace\":\"" + stackTrace + "\"}";
+ parseLongJson(json);
+ System.out.println("Gson could handle a string of size: " + stackTrace.length());
+ } catch (JsonParseException expected) {
+ break;
+ }
+ }
+ }
+
+ private void parseLongJson(String json) throws JsonParseException {
+ ExceptionHolder target = gson.fromJson(json, ExceptionHolder.class);
+ assertTrue(target.message.contains("Error"));
+ assertTrue(target.stackTrace.contains("Yippie"));
+ }
+
+ private static class ExceptionHolder {
+ public final String message;
+ public final String stackTrace;
+
+ // For use by Gson
+ @SuppressWarnings("unused")
+ private ExceptionHolder() {
+ this("", "");
+ }
+ public ExceptionHolder(String message, String stackTrace) {
+ this.message = message;
+ this.stackTrace = stackTrace;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static class CollectionEntry {
+ final String name;
+ final String value;
+
+ // For use by Gson
+ private CollectionEntry() {
+ this(null, null);
+ }
+
+ CollectionEntry(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+ }
+
+ /**
+ * Created in response to http://code.google.com/p/google-gson/issues/detail?id=96
+ */
+ public void disabled_testLargeCollectionSerialization() {
+ int count = 1400000;
+ List<CollectionEntry> list = new ArrayList<CollectionEntry>(count);
+ for (int i = 0; i < count; ++i) {
+ list.add(new CollectionEntry("name"+i,"value"+i));
+ }
+ gson.toJson(list);
+ }
+
+ /**
+ * Created in response to http://code.google.com/p/google-gson/issues/detail?id=96
+ */
+ public void disabled_testLargeCollectionDeserialization() {
+ StringBuilder sb = new StringBuilder();
+ int count = 87000;
+ boolean first = true;
+ sb.append('[');
+ for (int i = 0; i < count; ++i) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append("{name:'name").append(i).append("',value:'value").append(i).append("'}");
+ }
+ sb.append(']');
+ String json = sb.toString();
+ Type collectionType = new TypeToken<ArrayList<CollectionEntry>>(){}.getType();
+ List<CollectionEntry> list = gson.fromJson(json, collectionType);
+ assertEquals(count, list.size());
+ }
+
+ /**
+ * Created in response to http://code.google.com/p/google-gson/issues/detail?id=96
+ */
+ // Last I tested, Gson was able to serialize upto 14MB byte array
+ public void disabled_testByteArraySerialization() {
+ for (int size = 4145152; true; size += 1036288) {
+ byte[] ba = new byte[size];
+ for (int i = 0; i < size; ++i) {
+ ba[i] = 0x05;
+ }
+ gson.toJson(ba);
+ System.out.printf("Gson could serialize a byte array of size: %d\n", size);
+ }
+ }
+
+ /**
+ * Created in response to http://code.google.com/p/google-gson/issues/detail?id=96
+ */
+ // Last I tested, Gson was able to deserialize a byte array of 11MB
+ public void disable_testByteArrayDeserialization() {
+ for (int numElements = 10639296; true; numElements += 16384) {
+ StringBuilder sb = new StringBuilder(numElements*2);
+ sb.append("[");
+ boolean first = true;
+ for (int i = 0; i < numElements; ++i) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append("5");
+ }
+ sb.append("]");
+ String json = sb.toString();
+ byte[] ba = gson.fromJson(json, byte[].class);
+ System.out.printf("Gson could deserialize a byte array of size: %d\n", ba.length);
+ }
+ }
+
+// The tests to measure serialization and deserialization performance of Gson
+// Based on the discussion at
+// http://groups.google.com/group/google-gson/browse_thread/thread/7a50b17a390dfaeb
+// Test results: 10/19/2009
+// Serialize classes avg time: 60 ms
+// Deserialized classes avg time: 70 ms
+// Serialize exposed classes avg time: 159 ms
+// Deserialized exposed classes avg time: 173 ms
+
+ public void disabled_testSerializeClasses() {
+ ClassWithList c = new ClassWithList("str");
+ for (int i = 0; i < COLLECTION_SIZE; ++i) {
+ c.list.add(new ClassWithField("element-" + i));
+ }
+ StringWriter w = new StringWriter();
+ long t1 = System.currentTimeMillis();
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ gson.toJson(c, w);
+ }
+ long t2 = System.currentTimeMillis();
+ long avg = (t2 - t1) / NUM_ITERATIONS;
+ System.out.printf("Serialize classes avg time: %d ms\n", avg);
+ }
+
+ public void disabled_testDeserializeClasses() {
+ String json = buildJsonForClassWithList();
+ ClassWithList[] target = new ClassWithList[NUM_ITERATIONS];
+ long t1 = System.currentTimeMillis();
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ target[i] = gson.fromJson(json, ClassWithList.class);
+ }
+ long t2 = System.currentTimeMillis();
+ long avg = (t2 - t1) / NUM_ITERATIONS;
+ System.out.printf("Deserialize classes avg time: %d ms\n", avg);
+ }
+
+ public void disable_testLargeObjectSerializationAndDeserialization() {
+ Map<String, Long> largeObject = new HashMap<String, Long>();
+ for (long l = 0; l < 100000; l++) {
+ largeObject.put("field" + l, l);
+ }
+
+ long t1 = System.currentTimeMillis();
+ String json = gson.toJson(largeObject);
+ long t2 = System.currentTimeMillis();
+ System.out.printf("Large object serialized in: %d ms\n", (t2 - t1));
+
+ t1 = System.currentTimeMillis();
+ gson.fromJson(json, new TypeToken<Map<String, Long>>() {}.getType());
+ t2 = System.currentTimeMillis();
+ System.out.printf("Large object deserialized in: %d ms\n", (t2 - t1));
+
+ }
+
+ public void disabled_testSerializeExposedClasses() {
+ ClassWithListOfObjects c1 = new ClassWithListOfObjects("str");
+ for (int i1 = 0; i1 < COLLECTION_SIZE; ++i1) {
+ c1.list.add(new ClassWithExposedField("element-" + i1));
+ }
+ ClassWithListOfObjects c = c1;
+ StringWriter w = new StringWriter();
+ long t1 = System.currentTimeMillis();
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ gson.toJson(c, w);
+ }
+ long t2 = System.currentTimeMillis();
+ long avg = (t2 - t1) / NUM_ITERATIONS;
+ System.out.printf("Serialize exposed classes avg time: %d ms\n", avg);
+ }
+
+ public void disabled_testDeserializeExposedClasses() {
+ String json = buildJsonForClassWithList();
+ ClassWithListOfObjects[] target = new ClassWithListOfObjects[NUM_ITERATIONS];
+ long t1 = System.currentTimeMillis();
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ target[i] = gson.fromJson(json, ClassWithListOfObjects.class);
+ }
+ long t2 = System.currentTimeMillis();
+ long avg = (t2 - t1) / NUM_ITERATIONS;
+ System.out.printf("Deserialize exposed classes avg time: %d ms\n", avg);
+ }
+
+ public void disabled_testLargeGsonMapRoundTrip() throws Exception {
+ Map<Long, Long> original = new HashMap<Long, Long>();
+ for (long i = 0; i < 1000000; i++) {
+ original.put(i, i + 1);
+ }
+
+ Gson gson = new Gson();
+ String json = gson.toJson(original);
+ Type longToLong = new TypeToken<Map<Long, Long>>(){}.getType();
+ gson.fromJson(json, longToLong);
+ }
+
+ private String buildJsonForClassWithList() {
+ StringBuilder sb = new StringBuilder("{");
+ sb.append("field:").append("'str',");
+ sb.append("list:[");
+ boolean first = true;
+ for (int i = 0; i < COLLECTION_SIZE; ++i) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append("{field:'element-" + i + "'}");
+ }
+ sb.append("]");
+ sb.append("}");
+ String json = sb.toString();
+ return json;
+ }
+
+ @SuppressWarnings("unused")
+ private static final class ClassWithList {
+ final String field;
+ final List<ClassWithField> list = new ArrayList<ClassWithField>(COLLECTION_SIZE);
+ ClassWithList() {
+ this(null);
+ }
+ ClassWithList(String field) {
+ this.field = field;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static final class ClassWithField {
+ final String field;
+ ClassWithField() {
+ this("");
+ }
+ public ClassWithField(String field) {
+ this.field = field;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static final class ClassWithListOfObjects {
+ @Expose
+ final String field;
+ @Expose
+ final List<ClassWithExposedField> list = new ArrayList<ClassWithExposedField>(COLLECTION_SIZE);
+ ClassWithListOfObjects() {
+ this(null);
+ }
+ ClassWithListOfObjects(String field) {
+ this.field = field;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static final class ClassWithExposedField {
+ @Expose
+ final String field;
+ ClassWithExposedField() {
+ this("");
+ }
+ ClassWithExposedField(String field) {
+ this.field = field;
+ }
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java b/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java
new file mode 100644
index 00000000..7dda9d47
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 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.reflect;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.RandomAccess;
+import java.util.Set;
+import junit.framework.TestCase;
+
+/**
+ * @author Jesse Wilson
+ */
+@SuppressWarnings({"deprecation"})
+public final class TypeTokenTest extends TestCase {
+
+ List<Integer> listOfInteger = null;
+ List<Number> listOfNumber = null;
+ List<String> listOfString = null;
+ List<?> listOfUnknown = null;
+ List<Set<String>> listOfSetOfString = null;
+ List<Set<?>> listOfSetOfUnknown = null;
+
+ public void testIsAssignableFromRawTypes() {
+ assertTrue(TypeToken.get(Object.class).isAssignableFrom(String.class));
+ assertFalse(TypeToken.get(String.class).isAssignableFrom(Object.class));
+ assertTrue(TypeToken.get(RandomAccess.class).isAssignableFrom(ArrayList.class));
+ assertFalse(TypeToken.get(ArrayList.class).isAssignableFrom(RandomAccess.class));
+ }
+
+ public void testIsAssignableFromWithTypeParameters() throws Exception {
+ Type a = getClass().getDeclaredField("listOfInteger").getGenericType();
+ Type b = getClass().getDeclaredField("listOfNumber").getGenericType();
+ assertTrue(TypeToken.get(a).isAssignableFrom(a));
+ assertTrue(TypeToken.get(b).isAssignableFrom(b));
+
+ // listOfInteger = listOfNumber; // doesn't compile; must be false
+ assertFalse(TypeToken.get(a).isAssignableFrom(b));
+ // listOfNumber = listOfInteger; // doesn't compile; must be false
+ assertFalse(TypeToken.get(b).isAssignableFrom(a));
+ }
+
+ public void testIsAssignableFromWithBasicWildcards() throws Exception {
+ Type a = getClass().getDeclaredField("listOfString").getGenericType();
+ Type b = getClass().getDeclaredField("listOfUnknown").getGenericType();
+ assertTrue(TypeToken.get(a).isAssignableFrom(a));
+ assertTrue(TypeToken.get(b).isAssignableFrom(b));
+
+ // listOfString = listOfUnknown // doesn't compile; must be false
+ assertFalse(TypeToken.get(a).isAssignableFrom(b));
+ listOfUnknown = listOfString; // compiles; must be true
+ // The following assertion is too difficult to support reliably, so disabling
+ // assertTrue(TypeToken.get(b).isAssignableFrom(a));
+ }
+
+ public void testIsAssignableFromWithNestedWildcards() throws Exception {
+ Type a = getClass().getDeclaredField("listOfSetOfString").getGenericType();
+ Type b = getClass().getDeclaredField("listOfSetOfUnknown").getGenericType();
+ assertTrue(TypeToken.get(a).isAssignableFrom(a));
+ assertTrue(TypeToken.get(b).isAssignableFrom(b));
+
+ // listOfSetOfString = listOfSetOfUnknown; // doesn't compile; must be false
+ assertFalse(TypeToken.get(a).isAssignableFrom(b));
+ // listOfSetOfUnknown = listOfSetOfString; // doesn't compile; must be false
+ assertFalse(TypeToken.get(b).isAssignableFrom(a));
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/stream/JsonReaderPathTest.java b/gson/src/test/java/com/google/gson/stream/JsonReaderPathTest.java
new file mode 100644
index 00000000..50661664
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/stream/JsonReaderPathTest.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2014 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.stream;
+
+import java.io.IOException;
+import java.io.StringReader;
+import junit.framework.TestCase;
+
+@SuppressWarnings("resource")
+public class JsonReaderPathTest extends TestCase {
+ public void testPath() throws IOException {
+ JsonReader reader = new JsonReader(
+ new StringReader("{\"a\":[2,true,false,null,\"b\",{\"c\":\"d\"},[3]]}"));
+ assertEquals("$", reader.getPath());
+ reader.beginObject();
+ assertEquals("$.", reader.getPath());
+ reader.nextName();
+ assertEquals("$.a", reader.getPath());
+ reader.beginArray();
+ assertEquals("$.a[0]", reader.getPath());
+ reader.nextInt();
+ assertEquals("$.a[1]", reader.getPath());
+ reader.nextBoolean();
+ assertEquals("$.a[2]", reader.getPath());
+ reader.nextBoolean();
+ assertEquals("$.a[3]", reader.getPath());
+ reader.nextNull();
+ assertEquals("$.a[4]", reader.getPath());
+ reader.nextString();
+ assertEquals("$.a[5]", reader.getPath());
+ reader.beginObject();
+ assertEquals("$.a[5].", reader.getPath());
+ reader.nextName();
+ assertEquals("$.a[5].c", reader.getPath());
+ reader.nextString();
+ assertEquals("$.a[5].c", reader.getPath());
+ reader.endObject();
+ assertEquals("$.a[6]", reader.getPath());
+ reader.beginArray();
+ assertEquals("$.a[6][0]", reader.getPath());
+ reader.nextInt();
+ assertEquals("$.a[6][1]", reader.getPath());
+ reader.endArray();
+ assertEquals("$.a[7]", reader.getPath());
+ reader.endArray();
+ assertEquals("$.a", reader.getPath());
+ reader.endObject();
+ assertEquals("$", reader.getPath());
+ }
+
+ public void testObjectPath() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{\"a\":1,\"b\":2}"));
+ assertEquals("$", reader.getPath());
+
+ reader.peek();
+ assertEquals("$", reader.getPath());
+ reader.beginObject();
+ assertEquals("$.", reader.getPath());
+
+ reader.peek();
+ assertEquals("$.", reader.getPath());
+ reader.nextName();
+ assertEquals("$.a", reader.getPath());
+
+ reader.peek();
+ assertEquals("$.a", reader.getPath());
+ reader.nextInt();
+ assertEquals("$.a", reader.getPath());
+
+ reader.peek();
+ assertEquals("$.a", reader.getPath());
+ reader.nextName();
+ assertEquals("$.b", reader.getPath());
+
+ reader.peek();
+ assertEquals("$.b", reader.getPath());
+ reader.nextInt();
+ assertEquals("$.b", reader.getPath());
+
+ reader.peek();
+ assertEquals("$.b", reader.getPath());
+ reader.endObject();
+ assertEquals("$", reader.getPath());
+
+ reader.peek();
+ assertEquals("$", reader.getPath());
+ reader.close();
+ assertEquals("$", reader.getPath());
+ }
+
+ public void testArrayPath() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[1,2]"));
+ assertEquals("$", reader.getPath());
+
+ reader.peek();
+ assertEquals("$", reader.getPath());
+ reader.beginArray();
+ assertEquals("$[0]", reader.getPath());
+
+ reader.peek();
+ assertEquals("$[0]", reader.getPath());
+ reader.nextInt();
+ assertEquals("$[1]", reader.getPath());
+
+ reader.peek();
+ assertEquals("$[1]", reader.getPath());
+ reader.nextInt();
+ assertEquals("$[2]", reader.getPath());
+
+ reader.peek();
+ assertEquals("$[2]", reader.getPath());
+ reader.endArray();
+ assertEquals("$", reader.getPath());
+
+ reader.peek();
+ assertEquals("$", reader.getPath());
+ reader.close();
+ assertEquals("$", reader.getPath());
+ }
+
+ public void testMultipleTopLevelValuesInOneDocument() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[][]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ reader.endArray();
+ assertEquals("$", reader.getPath());
+ reader.beginArray();
+ reader.endArray();
+ assertEquals("$", reader.getPath());
+ }
+
+ public void testSkipArrayElements() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[1,2,3]"));
+ reader.beginArray();
+ reader.skipValue();
+ reader.skipValue();
+ assertEquals("$[2]", reader.getPath());
+ }
+
+ public void testSkipObjectNames() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{\"a\":1}"));
+ reader.beginObject();
+ reader.skipValue();
+ assertEquals("$.null", reader.getPath());
+ }
+
+ public void testSkipObjectValues() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{\"a\":1,\"b\":2}"));
+ reader.beginObject();
+ reader.nextName();
+ reader.skipValue();
+ assertEquals("$.null", reader.getPath());
+ reader.nextName();
+ assertEquals("$.b", reader.getPath());
+ }
+
+ public void testSkipNestedStructures() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[[1,2,3],4]"));
+ reader.beginArray();
+ reader.skipValue();
+ assertEquals("$[1]", reader.getPath());
+ }
+
+ public void testArrayOfObjects() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[{},{},{}]"));
+ reader.beginArray();
+ assertEquals("$[0]", reader.getPath());
+ reader.beginObject();
+ assertEquals("$[0].", reader.getPath());
+ reader.endObject();
+ assertEquals("$[1]", reader.getPath());
+ reader.beginObject();
+ assertEquals("$[1].", reader.getPath());
+ reader.endObject();
+ assertEquals("$[2]", reader.getPath());
+ reader.beginObject();
+ assertEquals("$[2].", reader.getPath());
+ reader.endObject();
+ assertEquals("$[3]", reader.getPath());
+ reader.endArray();
+ assertEquals("$", reader.getPath());
+ }
+
+ public void testArrayOfArrays() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[[],[],[]]"));
+ reader.beginArray();
+ assertEquals("$[0]", reader.getPath());
+ reader.beginArray();
+ assertEquals("$[0][0]", reader.getPath());
+ reader.endArray();
+ assertEquals("$[1]", reader.getPath());
+ reader.beginArray();
+ assertEquals("$[1][0]", reader.getPath());
+ reader.endArray();
+ assertEquals("$[2]", reader.getPath());
+ reader.beginArray();
+ assertEquals("$[2][0]", reader.getPath());
+ reader.endArray();
+ assertEquals("$[3]", reader.getPath());
+ reader.endArray();
+ assertEquals("$", reader.getPath());
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java b/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java
new file mode 100644
index 00000000..72c9aa4c
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java
@@ -0,0 +1,1775 @@
+/*
+ * Copyright (C) 2010 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.stream;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Arrays;
+import junit.framework.TestCase;
+
+import static com.google.gson.stream.JsonToken.BEGIN_ARRAY;
+import static com.google.gson.stream.JsonToken.BEGIN_OBJECT;
+import static com.google.gson.stream.JsonToken.BOOLEAN;
+import static com.google.gson.stream.JsonToken.END_ARRAY;
+import static com.google.gson.stream.JsonToken.END_OBJECT;
+import static com.google.gson.stream.JsonToken.NAME;
+import static com.google.gson.stream.JsonToken.NULL;
+import static com.google.gson.stream.JsonToken.NUMBER;
+import static com.google.gson.stream.JsonToken.STRING;
+
+@SuppressWarnings("resource")
+public final class JsonReaderTest extends TestCase {
+ public void testReadArray() throws IOException {
+ JsonReader reader = new JsonReader(reader("[true, true]"));
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ assertEquals(true, reader.nextBoolean());
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testReadEmptyArray() throws IOException {
+ JsonReader reader = new JsonReader(reader("[]"));
+ reader.beginArray();
+ assertFalse(reader.hasNext());
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testReadObject() throws IOException {
+ JsonReader reader = new JsonReader(reader(
+ "{\"a\": \"android\", \"b\": \"banana\"}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals("android", reader.nextString());
+ assertEquals("b", reader.nextName());
+ assertEquals("banana", reader.nextString());
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testReadEmptyObject() throws IOException {
+ JsonReader reader = new JsonReader(reader("{}"));
+ reader.beginObject();
+ assertFalse(reader.hasNext());
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testSkipArray() throws IOException {
+ JsonReader reader = new JsonReader(reader(
+ "{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ reader.skipValue();
+ assertEquals("b", reader.nextName());
+ assertEquals(123, reader.nextInt());
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testSkipArrayAfterPeek() throws Exception {
+ JsonReader reader = new JsonReader(reader(
+ "{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals(BEGIN_ARRAY, reader.peek());
+ reader.skipValue();
+ assertEquals("b", reader.nextName());
+ assertEquals(123, reader.nextInt());
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testSkipTopLevelObject() throws Exception {
+ JsonReader reader = new JsonReader(reader(
+ "{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}"));
+ reader.skipValue();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testSkipObject() throws IOException {
+ JsonReader reader = new JsonReader(reader(
+ "{\"a\": { \"c\": [], \"d\": [true, true, {}] }, \"b\": \"banana\"}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ reader.skipValue();
+ assertEquals("b", reader.nextName());
+ reader.skipValue();
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testSkipObjectAfterPeek() throws Exception {
+ String json = "{" + " \"one\": { \"num\": 1 }"
+ + ", \"two\": { \"num\": 2 }" + ", \"three\": { \"num\": 3 }" + "}";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginObject();
+ assertEquals("one", reader.nextName());
+ assertEquals(BEGIN_OBJECT, reader.peek());
+ reader.skipValue();
+ assertEquals("two", reader.nextName());
+ assertEquals(BEGIN_OBJECT, reader.peek());
+ reader.skipValue();
+ assertEquals("three", reader.nextName());
+ reader.skipValue();
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testSkipInteger() throws IOException {
+ JsonReader reader = new JsonReader(reader(
+ "{\"a\":123456789,\"b\":-123456789}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ reader.skipValue();
+ assertEquals("b", reader.nextName());
+ reader.skipValue();
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testSkipDouble() throws IOException {
+ JsonReader reader = new JsonReader(reader(
+ "{\"a\":-123.456e-789,\"b\":123456789.0}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ reader.skipValue();
+ assertEquals("b", reader.nextName());
+ reader.skipValue();
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testHelloWorld() throws IOException {
+ String json = "{\n" +
+ " \"hello\": true,\n" +
+ " \"foo\": [\"world\"]\n" +
+ "}";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginObject();
+ assertEquals("hello", reader.nextName());
+ assertEquals(true, reader.nextBoolean());
+ assertEquals("foo", reader.nextName());
+ reader.beginArray();
+ assertEquals("world", reader.nextString());
+ reader.endArray();
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testNulls() {
+ try {
+ new JsonReader(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testEmptyString() {
+ try {
+ new JsonReader(reader("")).beginArray();
+ fail();
+ } catch (IOException expected) {
+ }
+ try {
+ new JsonReader(reader("")).beginObject();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testNoTopLevelObject() {
+ try {
+ new JsonReader(reader("true")).nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testCharacterUnescaping() throws IOException {
+ String json = "[\"a\","
+ + "\"a\\\"\","
+ + "\"\\\"\","
+ + "\":\","
+ + "\",\","
+ + "\"\\b\","
+ + "\"\\f\","
+ + "\"\\n\","
+ + "\"\\r\","
+ + "\"\\t\","
+ + "\" \","
+ + "\"\\\\\","
+ + "\"{\","
+ + "\"}\","
+ + "\"[\","
+ + "\"]\","
+ + "\"\\u0000\","
+ + "\"\\u0019\","
+ + "\"\\u20AC\""
+ + "]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ assertEquals("a", reader.nextString());
+ assertEquals("a\"", reader.nextString());
+ assertEquals("\"", reader.nextString());
+ assertEquals(":", reader.nextString());
+ assertEquals(",", reader.nextString());
+ assertEquals("\b", reader.nextString());
+ assertEquals("\f", reader.nextString());
+ assertEquals("\n", reader.nextString());
+ assertEquals("\r", reader.nextString());
+ assertEquals("\t", reader.nextString());
+ assertEquals(" ", reader.nextString());
+ assertEquals("\\", reader.nextString());
+ assertEquals("{", reader.nextString());
+ assertEquals("}", reader.nextString());
+ assertEquals("[", reader.nextString());
+ assertEquals("]", reader.nextString());
+ assertEquals("\0", reader.nextString());
+ assertEquals("\u0019", reader.nextString());
+ assertEquals("\u20AC", reader.nextString());
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testUnescapingInvalidCharacters() throws IOException {
+ String json = "[\"\\u000g\"]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ try {
+ reader.nextString();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ }
+
+ public void testUnescapingTruncatedCharacters() throws IOException {
+ String json = "[\"\\u000";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ try {
+ reader.nextString();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testUnescapingTruncatedSequence() throws IOException {
+ String json = "[\"\\";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ try {
+ reader.nextString();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testIntegersWithFractionalPartSpecified() throws IOException {
+ JsonReader reader = new JsonReader(reader("[1.0,1.0,1.0]"));
+ reader.beginArray();
+ assertEquals(1.0, reader.nextDouble());
+ assertEquals(1, reader.nextInt());
+ assertEquals(1L, reader.nextLong());
+ }
+
+ public void testDoubles() throws IOException {
+ String json = "[-0.0,"
+ + "1.0,"
+ + "1.7976931348623157E308,"
+ + "4.9E-324,"
+ + "0.0,"
+ + "-0.5,"
+ + "2.2250738585072014E-308,"
+ + "3.141592653589793,"
+ + "2.718281828459045]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ assertEquals(-0.0, reader.nextDouble());
+ assertEquals(1.0, reader.nextDouble());
+ assertEquals(1.7976931348623157E308, reader.nextDouble());
+ assertEquals(4.9E-324, reader.nextDouble());
+ assertEquals(0.0, reader.nextDouble());
+ assertEquals(-0.5, reader.nextDouble());
+ assertEquals(2.2250738585072014E-308, reader.nextDouble());
+ assertEquals(3.141592653589793, reader.nextDouble());
+ assertEquals(2.718281828459045, reader.nextDouble());
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testStrictNonFiniteDoubles() throws IOException {
+ String json = "[NaN]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ try {
+ reader.nextDouble();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ public void testStrictQuotedNonFiniteDoubles() throws IOException {
+ String json = "[\"NaN\"]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ try {
+ reader.nextDouble();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ public void testLenientNonFiniteDoubles() throws IOException {
+ String json = "[NaN, -Infinity, Infinity]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertTrue(Double.isNaN(reader.nextDouble()));
+ assertEquals(Double.NEGATIVE_INFINITY, reader.nextDouble());
+ assertEquals(Double.POSITIVE_INFINITY, reader.nextDouble());
+ reader.endArray();
+ }
+
+ public void testLenientQuotedNonFiniteDoubles() throws IOException {
+ String json = "[\"NaN\", \"-Infinity\", \"Infinity\"]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertTrue(Double.isNaN(reader.nextDouble()));
+ assertEquals(Double.NEGATIVE_INFINITY, reader.nextDouble());
+ assertEquals(Double.POSITIVE_INFINITY, reader.nextDouble());
+ reader.endArray();
+ }
+
+ public void testStrictNonFiniteDoublesWithSkipValue() throws IOException {
+ String json = "[NaN]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ public void testLongs() throws IOException {
+ String json = "[0,0,0,"
+ + "1,1,1,"
+ + "-1,-1,-1,"
+ + "-9223372036854775808,"
+ + "9223372036854775807]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ assertEquals(0L, reader.nextLong());
+ assertEquals(0, reader.nextInt());
+ assertEquals(0.0, reader.nextDouble());
+ assertEquals(1L, reader.nextLong());
+ assertEquals(1, reader.nextInt());
+ assertEquals(1.0, reader.nextDouble());
+ assertEquals(-1L, reader.nextLong());
+ assertEquals(-1, reader.nextInt());
+ assertEquals(-1.0, reader.nextDouble());
+ try {
+ reader.nextInt();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ assertEquals(Long.MIN_VALUE, reader.nextLong());
+ try {
+ reader.nextInt();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ assertEquals(Long.MAX_VALUE, reader.nextLong());
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void disabled_testNumberWithOctalPrefix() throws IOException {
+ String json = "[01]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ try {
+ reader.peek();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ try {
+ reader.nextInt();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ try {
+ reader.nextLong();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ try {
+ reader.nextDouble();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ assertEquals("01", reader.nextString());
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testBooleans() throws IOException {
+ JsonReader reader = new JsonReader(reader("[true,false]"));
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ assertEquals(false, reader.nextBoolean());
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testPeekingUnquotedStringsPrefixedWithBooleans() throws IOException {
+ JsonReader reader = new JsonReader(reader("[truey]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(STRING, reader.peek());
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ assertEquals("truey", reader.nextString());
+ reader.endArray();
+ }
+
+ public void testMalformedNumbers() throws IOException {
+ assertNotANumber("-");
+ assertNotANumber(".");
+
+ // exponent lacks digit
+ assertNotANumber("e");
+ assertNotANumber("0e");
+ assertNotANumber(".e");
+ assertNotANumber("0.e");
+ assertNotANumber("-.0e");
+
+ // no integer
+ assertNotANumber("e1");
+ assertNotANumber(".e1");
+ assertNotANumber("-e1");
+
+ // trailing characters
+ assertNotANumber("1x");
+ assertNotANumber("1.1x");
+ assertNotANumber("1e1x");
+ assertNotANumber("1ex");
+ assertNotANumber("1.1ex");
+ assertNotANumber("1.1e1x");
+
+ // fraction has no digit
+ assertNotANumber("0.");
+ assertNotANumber("-0.");
+ assertNotANumber("0.e1");
+ assertNotANumber("-0.e1");
+
+ // no leading digit
+ assertNotANumber(".0");
+ assertNotANumber("-.0");
+ assertNotANumber(".0e1");
+ assertNotANumber("-.0e1");
+ }
+
+ private void assertNotANumber(String s) throws IOException {
+ JsonReader reader = new JsonReader(reader("[" + s + "]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(JsonToken.STRING, reader.peek());
+ assertEquals(s, reader.nextString());
+ reader.endArray();
+ }
+
+ public void testPeekingUnquotedStringsPrefixedWithIntegers() throws IOException {
+ JsonReader reader = new JsonReader(reader("[12.34e5x]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(STRING, reader.peek());
+ try {
+ reader.nextInt();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ assertEquals("12.34e5x", reader.nextString());
+ }
+
+ public void testPeekLongMinValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("[-9223372036854775808]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(NUMBER, reader.peek());
+ assertEquals(-9223372036854775808L, reader.nextLong());
+ }
+
+ public void testPeekLongMaxValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("[9223372036854775807]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(NUMBER, reader.peek());
+ assertEquals(9223372036854775807L, reader.nextLong());
+ }
+
+ public void testLongLargerThanMaxLongThatWrapsAround() throws IOException {
+ JsonReader reader = new JsonReader(reader("[22233720368547758070]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(NUMBER, reader.peek());
+ try {
+ reader.nextLong();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ }
+
+ public void testLongLargerThanMinLongThatWrapsAround() throws IOException {
+ JsonReader reader = new JsonReader(reader("[-22233720368547758070]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(NUMBER, reader.peek());
+ try {
+ reader.nextLong();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ }
+
+ /**
+ * This test fails because there's no double for 9223372036854775808, and our
+ * long parsing uses Double.parseDouble() for fractional values.
+ */
+ public void disabled_testPeekLargerThanLongMaxValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("[9223372036854775808]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(NUMBER, reader.peek());
+ try {
+ reader.nextLong();
+ fail();
+ } catch (NumberFormatException e) {
+ }
+ }
+
+ /**
+ * This test fails because there's no double for -9223372036854775809, and our
+ * long parsing uses Double.parseDouble() for fractional values.
+ */
+ public void disabled_testPeekLargerThanLongMinValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("[-9223372036854775809]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(NUMBER, reader.peek());
+ try {
+ reader.nextLong();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ assertEquals(-9223372036854775809d, reader.nextDouble());
+ }
+
+ /**
+ * This test fails because there's no double for 9223372036854775806, and
+ * our long parsing uses Double.parseDouble() for fractional values.
+ */
+ public void disabled_testHighPrecisionLong() throws IOException {
+ String json = "[9223372036854775806.000]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ assertEquals(9223372036854775806L, reader.nextLong());
+ reader.endArray();
+ }
+
+ public void testPeekMuchLargerThanLongMinValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("[-92233720368547758080]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(NUMBER, reader.peek());
+ try {
+ reader.nextLong();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ assertEquals(-92233720368547758080d, reader.nextDouble());
+ }
+
+ public void testQuotedNumberWithEscape() throws IOException {
+ JsonReader reader = new JsonReader(reader("[\"12\u00334\"]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(STRING, reader.peek());
+ assertEquals(1234, reader.nextInt());
+ }
+
+ public void testMixedCaseLiterals() throws IOException {
+ JsonReader reader = new JsonReader(reader("[True,TruE,False,FALSE,NULL,nulL]"));
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ assertEquals(true, reader.nextBoolean());
+ assertEquals(false, reader.nextBoolean());
+ assertEquals(false, reader.nextBoolean());
+ reader.nextNull();
+ reader.nextNull();
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testMissingValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\":}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ try {
+ reader.nextString();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testPrematureEndOfInput() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\":true,"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals(true, reader.nextBoolean());
+ try {
+ reader.nextName();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testPrematurelyClosed() throws IOException {
+ try {
+ JsonReader reader = new JsonReader(reader("{\"a\":[]}"));
+ reader.beginObject();
+ reader.close();
+ reader.nextName();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+
+ try {
+ JsonReader reader = new JsonReader(reader("{\"a\":[]}"));
+ reader.close();
+ reader.beginObject();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+
+ try {
+ JsonReader reader = new JsonReader(reader("{\"a\":true}"));
+ reader.beginObject();
+ reader.nextName();
+ reader.peek();
+ reader.close();
+ reader.nextBoolean();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testNextFailuresDoNotAdvance() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\":true}"));
+ reader.beginObject();
+ try {
+ reader.nextString();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ assertEquals("a", reader.nextName());
+ try {
+ reader.nextName();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.beginArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.endArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.beginObject();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.endObject();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ assertEquals(true, reader.nextBoolean());
+ try {
+ reader.nextString();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.nextName();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.beginArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ reader.endArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ reader.close();
+ }
+
+ public void testIntegerMismatchFailuresDoNotAdvance() throws IOException {
+ JsonReader reader = new JsonReader(reader("[1.5]"));
+ reader.beginArray();
+ try {
+ reader.nextInt();
+ fail();
+ } catch (NumberFormatException expected) {
+ }
+ assertEquals(1.5d, reader.nextDouble());
+ reader.endArray();
+ }
+
+ public void testStringNullIsNotNull() throws IOException {
+ JsonReader reader = new JsonReader(reader("[\"null\"]"));
+ reader.beginArray();
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testNullLiteralIsNotAString() throws IOException {
+ JsonReader reader = new JsonReader(reader("[null]"));
+ reader.beginArray();
+ try {
+ reader.nextString();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testStrictNameValueSeparator() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\"=true}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("{\"a\"=>true}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientNameValueSeparator() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\"=true}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals(true, reader.nextBoolean());
+
+ reader = new JsonReader(reader("{\"a\"=>true}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals(true, reader.nextBoolean());
+ }
+
+ public void testStrictNameValueSeparatorWithSkipValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\"=true}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("{\"a\"=>true}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testCommentsInStringValue() throws Exception {
+ JsonReader reader = new JsonReader(reader("[\"// comment\"]"));
+ reader.beginArray();
+ assertEquals("// comment", reader.nextString());
+ reader.endArray();
+
+ reader = new JsonReader(reader("{\"a\":\"#someComment\"}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals("#someComment", reader.nextString());
+ reader.endObject();
+
+ reader = new JsonReader(reader("{\"#//a\":\"#some //Comment\"}"));
+ reader.beginObject();
+ assertEquals("#//a", reader.nextName());
+ assertEquals("#some //Comment", reader.nextString());
+ reader.endObject();
+ }
+
+ public void testStrictComments() throws IOException {
+ JsonReader reader = new JsonReader(reader("[// comment \n true]"));
+ reader.beginArray();
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("[# comment \n true]"));
+ reader.beginArray();
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("[/* comment */ true]"));
+ reader.beginArray();
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientComments() throws IOException {
+ JsonReader reader = new JsonReader(reader("[// comment \n true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+
+ reader = new JsonReader(reader("[# comment \n true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+
+ reader = new JsonReader(reader("[/* comment */ true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ }
+
+ public void testStrictCommentsWithSkipValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("[// comment \n true]"));
+ reader.beginArray();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("[# comment \n true]"));
+ reader.beginArray();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("[/* comment */ true]"));
+ reader.beginArray();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testStrictUnquotedNames() throws IOException {
+ JsonReader reader = new JsonReader(reader("{a:true}"));
+ reader.beginObject();
+ try {
+ reader.nextName();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientUnquotedNames() throws IOException {
+ JsonReader reader = new JsonReader(reader("{a:true}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ }
+
+ public void testStrictUnquotedNamesWithSkipValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("{a:true}"));
+ reader.beginObject();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testStrictSingleQuotedNames() throws IOException {
+ JsonReader reader = new JsonReader(reader("{'a':true}"));
+ reader.beginObject();
+ try {
+ reader.nextName();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientSingleQuotedNames() throws IOException {
+ JsonReader reader = new JsonReader(reader("{'a':true}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ }
+
+ public void testStrictSingleQuotedNamesWithSkipValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("{'a':true}"));
+ reader.beginObject();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testStrictUnquotedStrings() throws IOException {
+ JsonReader reader = new JsonReader(reader("[a]"));
+ reader.beginArray();
+ try {
+ reader.nextString();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ public void testStrictUnquotedStringsWithSkipValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("[a]"));
+ reader.beginArray();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ public void testLenientUnquotedStrings() throws IOException {
+ JsonReader reader = new JsonReader(reader("[a]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals("a", reader.nextString());
+ }
+
+ public void testStrictSingleQuotedStrings() throws IOException {
+ JsonReader reader = new JsonReader(reader("['a']"));
+ reader.beginArray();
+ try {
+ reader.nextString();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientSingleQuotedStrings() throws IOException {
+ JsonReader reader = new JsonReader(reader("['a']"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals("a", reader.nextString());
+ }
+
+ public void testStrictSingleQuotedStringsWithSkipValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("['a']"));
+ reader.beginArray();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testStrictSemicolonDelimitedArray() throws IOException {
+ JsonReader reader = new JsonReader(reader("[true;true]"));
+ reader.beginArray();
+ try {
+ reader.nextBoolean();
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientSemicolonDelimitedArray() throws IOException {
+ JsonReader reader = new JsonReader(reader("[true;true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ assertEquals(true, reader.nextBoolean());
+ }
+
+ public void testStrictSemicolonDelimitedArrayWithSkipValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("[true;true]"));
+ reader.beginArray();
+ try {
+ reader.skipValue();
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testStrictSemicolonDelimitedNameValuePair() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\":true;\"b\":true}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ try {
+ reader.nextBoolean();
+ reader.nextName();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientSemicolonDelimitedNameValuePair() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\":true;\"b\":true}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals(true, reader.nextBoolean());
+ assertEquals("b", reader.nextName());
+ }
+
+ public void testStrictSemicolonDelimitedNameValuePairWithSkipValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\":true;\"b\":true}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ try {
+ reader.skipValue();
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testStrictUnnecessaryArraySeparators() throws IOException {
+ JsonReader reader = new JsonReader(reader("[true,,true]"));
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("[,true]"));
+ reader.beginArray();
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("[true,]"));
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("[,]"));
+ reader.beginArray();
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientUnnecessaryArraySeparators() throws IOException {
+ JsonReader reader = new JsonReader(reader("[true,,true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ reader.nextNull();
+ assertEquals(true, reader.nextBoolean());
+ reader.endArray();
+
+ reader = new JsonReader(reader("[,true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ reader.nextNull();
+ assertEquals(true, reader.nextBoolean());
+ reader.endArray();
+
+ reader = new JsonReader(reader("[true,]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ reader.nextNull();
+ reader.endArray();
+
+ reader = new JsonReader(reader("[,]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ reader.nextNull();
+ reader.nextNull();
+ reader.endArray();
+ }
+
+ public void testStrictUnnecessaryArraySeparatorsWithSkipValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("[true,,true]"));
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("[,true]"));
+ reader.beginArray();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("[true,]"));
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(reader("[,]"));
+ reader.beginArray();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testStrictMultipleTopLevelValues() throws IOException {
+ JsonReader reader = new JsonReader(reader("[] []"));
+ reader.beginArray();
+ reader.endArray();
+ try {
+ reader.peek();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientMultipleTopLevelValues() throws IOException {
+ JsonReader reader = new JsonReader(reader("[] true {}"));
+ reader.setLenient(true);
+ reader.beginArray();
+ reader.endArray();
+ assertEquals(true, reader.nextBoolean());
+ reader.beginObject();
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testStrictMultipleTopLevelValuesWithSkipValue() throws IOException {
+ JsonReader reader = new JsonReader(reader("[] []"));
+ reader.beginArray();
+ reader.endArray();
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testStrictTopLevelString() {
+ JsonReader reader = new JsonReader(reader("\"a\""));
+ try {
+ reader.nextString();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientTopLevelString() throws IOException {
+ JsonReader reader = new JsonReader(reader("\"a\""));
+ reader.setLenient(true);
+ assertEquals("a", reader.nextString());
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testStrictTopLevelValueType() {
+ JsonReader reader = new JsonReader(reader("true"));
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientTopLevelValueType() throws IOException {
+ JsonReader reader = new JsonReader(reader("true"));
+ reader.setLenient(true);
+ assertEquals(true, reader.nextBoolean());
+ }
+
+ public void testStrictTopLevelValueTypeWithSkipValue() {
+ JsonReader reader = new JsonReader(reader("true"));
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testStrictNonExecutePrefix() {
+ JsonReader reader = new JsonReader(reader(")]}'\n []"));
+ try {
+ reader.beginArray();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testStrictNonExecutePrefixWithSkipValue() {
+ JsonReader reader = new JsonReader(reader(")]}'\n []"));
+ try {
+ reader.skipValue();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientNonExecutePrefix() throws IOException {
+ JsonReader reader = new JsonReader(reader(")]}'\n []"));
+ reader.setLenient(true);
+ reader.beginArray();
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testLenientNonExecutePrefixWithLeadingWhitespace() throws IOException {
+ JsonReader reader = new JsonReader(reader("\r\n \t)]}'\n []"));
+ reader.setLenient(true);
+ reader.beginArray();
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testLenientPartialNonExecutePrefix() {
+ JsonReader reader = new JsonReader(reader(")]}' []"));
+ reader.setLenient(true);
+ try {
+ assertEquals(")", reader.nextString());
+ reader.nextString();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testBomIgnoredAsFirstCharacterOfDocument() throws IOException {
+ JsonReader reader = new JsonReader(reader("\ufeff[]"));
+ reader.beginArray();
+ reader.endArray();
+ }
+
+ public void testBomForbiddenAsOtherCharacterInDocument() throws IOException {
+ JsonReader reader = new JsonReader(reader("[\ufeff]"));
+ reader.beginArray();
+ try {
+ reader.endArray();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testFailWithPosition() throws IOException {
+ testFailWithPosition("Expected value at line 6 column 5 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]",
+ "[\n\n" + spaces + "\n\n\n\"a\",}]");
+ }
+
+ public void testFailWithPositionOverSlashSlashEndOfLineComment() throws IOException {
+ testFailWithPosition("Expected value at line 5 column 6 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]",
+ "\n# foo\n\n#bar\r\n[\"a\",}");
+ }
+
+ public void testFailWithPositionOverCStyleComment() throws IOException {
+ testFailWithPosition("Expected value at line 6 column 12 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]",
+ "[\"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,}");
+ }
+
+ public void testFailWithEscapedNewlineCharacter() throws IOException {
+ testFailWithPosition("Expected value at line 5 column 3 path $[1]", "[\n\n\"\\\n\n\",}");
+ }
+
+ public void testFailWithPositionIsOffsetByBom() throws IOException {
+ testFailWithPosition("Expected value at line 1 column 6 path $[1]",
+ "\ufeff[\"a\",}]");
+ }
+
+ private void testFailWithPosition(String message, String json) throws IOException {
+ // Validate that it works reading the string normally.
+ JsonReader reader1 = new JsonReader(reader(json));
+ reader1.setLenient(true);
+ reader1.beginArray();
+ reader1.nextString();
+ try {
+ reader1.peek();
+ fail();
+ } catch (IOException expected) {
+ assertEquals(message, expected.getMessage());
+ }
+
+ // Also validate that it works when skipping.
+ JsonReader reader2 = new JsonReader(reader(json));
+ reader2.setLenient(true);
+ reader2.beginArray();
+ reader2.skipValue();
+ try {
+ reader2.peek();
+ fail();
+ } catch (IOException expected) {
+ assertEquals(message, expected.getMessage());
+ }
+ }
+
+ public void testFailWithPositionDeepPath() throws IOException {
+ JsonReader reader = new JsonReader(reader("[1,{\"a\":[2,3,}"));
+ reader.beginArray();
+ reader.nextInt();
+ reader.beginObject();
+ reader.nextName();
+ reader.beginArray();
+ reader.nextInt();
+ reader.nextInt();
+ try {
+ reader.peek();
+ fail();
+ } catch (IOException expected) {
+ assertEquals("Expected value at line 1 column 14 path $[1].a[2]", expected.getMessage());
+ }
+ }
+
+ public void testStrictVeryLongNumber() throws IOException {
+ JsonReader reader = new JsonReader(reader("[0." + repeat('9', 8192) + "]"));
+ reader.beginArray();
+ try {
+ assertEquals(1d, reader.nextDouble());
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ public void testLenientVeryLongNumber() throws IOException {
+ JsonReader reader = new JsonReader(reader("[0." + repeat('9', 8192) + "]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(JsonToken.STRING, reader.peek());
+ assertEquals(1d, reader.nextDouble());
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testVeryLongUnquotedLiteral() throws IOException {
+ String literal = "a" + repeat('b', 8192) + "c";
+ JsonReader reader = new JsonReader(reader("[" + literal + "]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(literal, reader.nextString());
+ reader.endArray();
+ }
+
+ public void testDeeplyNestedArrays() throws IOException {
+ // this is nested 40 levels deep; Gson is tuned for nesting is 30 levels deep or fewer
+ JsonReader reader = new JsonReader(reader(
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"));
+ for (int i = 0; i < 40; i++) {
+ reader.beginArray();
+ }
+ assertEquals("$[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]"
+ + "[0][0][0][0][0][0][0][0][0][0][0][0][0][0]", reader.getPath());
+ for (int i = 0; i < 40; i++) {
+ reader.endArray();
+ }
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testDeeplyNestedObjects() throws IOException {
+ // Build a JSON document structured like {"a":{"a":{"a":{"a":true}}}}, but 40 levels deep
+ String array = "{\"a\":%s}";
+ String json = "true";
+ for (int i = 0; i < 40; i++) {
+ json = String.format(array, json);
+ }
+
+ JsonReader reader = new JsonReader(reader(json));
+ for (int i = 0; i < 40; i++) {
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ }
+ assertEquals("$.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a"
+ + ".a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", reader.getPath());
+ assertEquals(true, reader.nextBoolean());
+ for (int i = 0; i < 40; i++) {
+ reader.endObject();
+ }
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ // http://code.google.com/p/google-gson/issues/detail?id=409
+ public void testStringEndingInSlash() throws IOException {
+ JsonReader reader = new JsonReader(reader("/"));
+ reader.setLenient(true);
+ try {
+ reader.peek();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ public void testDocumentWithCommentEndingInSlash() throws IOException {
+ JsonReader reader = new JsonReader(reader("/* foo *//"));
+ reader.setLenient(true);
+ try {
+ reader.peek();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ public void testStringWithLeadingSlash() throws IOException {
+ JsonReader reader = new JsonReader(reader("/x"));
+ reader.setLenient(true);
+ try {
+ reader.peek();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ public void testUnterminatedObject() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\":\"android\"x"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals("android", reader.nextString());
+ try {
+ reader.peek();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ public void testVeryLongQuotedString() throws IOException {
+ char[] stringChars = new char[1024 * 16];
+ Arrays.fill(stringChars, 'x');
+ String string = new String(stringChars);
+ String json = "[\"" + string + "\"]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.beginArray();
+ assertEquals(string, reader.nextString());
+ reader.endArray();
+ }
+
+ public void testVeryLongUnquotedString() throws IOException {
+ char[] stringChars = new char[1024 * 16];
+ Arrays.fill(stringChars, 'x');
+ String string = new String(stringChars);
+ String json = "[" + string + "]";
+ JsonReader reader = new JsonReader(reader(json));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(string, reader.nextString());
+ reader.endArray();
+ }
+
+ public void testVeryLongUnterminatedString() throws IOException {
+ char[] stringChars = new char[1024 * 16];
+ Arrays.fill(stringChars, 'x');
+ String string = new String(stringChars);
+ String json = "[" + string;
+ JsonReader reader = new JsonReader(reader(json));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(string, reader.nextString());
+ try {
+ reader.peek();
+ fail();
+ } catch (EOFException expected) {
+ }
+ }
+
+ public void testSkipVeryLongUnquotedString() throws IOException {
+ JsonReader reader = new JsonReader(reader("[" + repeat('x', 8192) + "]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ reader.skipValue();
+ reader.endArray();
+ }
+
+ public void testSkipTopLevelUnquotedString() throws IOException {
+ JsonReader reader = new JsonReader(reader(repeat('x', 8192)));
+ reader.setLenient(true);
+ reader.skipValue();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testSkipVeryLongQuotedString() throws IOException {
+ JsonReader reader = new JsonReader(reader("[\"" + repeat('x', 8192) + "\"]"));
+ reader.beginArray();
+ reader.skipValue();
+ reader.endArray();
+ }
+
+ public void testSkipTopLevelQuotedString() throws IOException {
+ JsonReader reader = new JsonReader(reader("\"" + repeat('x', 8192) + "\""));
+ reader.setLenient(true);
+ reader.skipValue();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testStringAsNumberWithTruncatedExponent() throws IOException {
+ JsonReader reader = new JsonReader(reader("[123e]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(STRING, reader.peek());
+ }
+
+ public void testStringAsNumberWithDigitAndNonDigitExponent() throws IOException {
+ JsonReader reader = new JsonReader(reader("[123e4b]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(STRING, reader.peek());
+ }
+
+ public void testStringAsNumberWithNonDigitExponent() throws IOException {
+ JsonReader reader = new JsonReader(reader("[123eb]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(STRING, reader.peek());
+ }
+
+ public void testEmptyStringName() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"\":true}"));
+ reader.setLenient(true);
+ assertEquals(BEGIN_OBJECT, reader.peek());
+ reader.beginObject();
+ assertEquals(NAME, reader.peek());
+ assertEquals("", reader.nextName());
+ assertEquals(JsonToken.BOOLEAN, reader.peek());
+ assertEquals(true, reader.nextBoolean());
+ assertEquals(JsonToken.END_OBJECT, reader.peek());
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
+ public void testStrictExtraCommasInMaps() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\":\"b\",}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals("b", reader.nextString());
+ try {
+ reader.peek();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientExtraCommasInMaps() throws IOException {
+ JsonReader reader = new JsonReader(reader("{\"a\":\"b\",}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals("b", reader.nextString());
+ try {
+ reader.peek();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ private String repeat(char c, int count) {
+ char[] array = new char[count];
+ Arrays.fill(array, c);
+ return new String(array);
+ }
+
+ public void testMalformedDocuments() throws IOException {
+ assertDocument("{]", BEGIN_OBJECT, IOException.class);
+ assertDocument("{,", BEGIN_OBJECT, IOException.class);
+ assertDocument("{{", BEGIN_OBJECT, IOException.class);
+ assertDocument("{[", BEGIN_OBJECT, IOException.class);
+ assertDocument("{:", BEGIN_OBJECT, IOException.class);
+ assertDocument("{\"name\",", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{\"name\",", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{\"name\":}", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{\"name\"::", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{\"name\":,", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{\"name\"=}", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{\"name\"=>}", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{\"name\"=>\"string\":", BEGIN_OBJECT, NAME, STRING, IOException.class);
+ assertDocument("{\"name\"=>\"string\"=", BEGIN_OBJECT, NAME, STRING, IOException.class);
+ assertDocument("{\"name\"=>\"string\"=>", BEGIN_OBJECT, NAME, STRING, IOException.class);
+ 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_OBJECT, IOException.class);
+ assertDocument("{\"name\"", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{\"name\",", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{'name'", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{'name',", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("{name", BEGIN_OBJECT, NAME, IOException.class);
+ assertDocument("[", BEGIN_ARRAY, IOException.class);
+ assertDocument("[string", BEGIN_ARRAY, STRING, IOException.class);
+ assertDocument("[\"string\"", BEGIN_ARRAY, STRING, IOException.class);
+ assertDocument("['string'", BEGIN_ARRAY, STRING, IOException.class);
+ assertDocument("[123", BEGIN_ARRAY, NUMBER, IOException.class);
+ assertDocument("[123,", BEGIN_ARRAY, NUMBER, IOException.class);
+ assertDocument("{\"name\":123", BEGIN_OBJECT, NAME, NUMBER, IOException.class);
+ assertDocument("{\"name\":123,", BEGIN_OBJECT, NAME, NUMBER, IOException.class);
+ assertDocument("{\"name\":\"string\"", BEGIN_OBJECT, NAME, STRING, IOException.class);
+ assertDocument("{\"name\":\"string\",", BEGIN_OBJECT, NAME, STRING, IOException.class);
+ assertDocument("{\"name\":'string'", BEGIN_OBJECT, NAME, STRING, IOException.class);
+ assertDocument("{\"name\":'string',", BEGIN_OBJECT, NAME, STRING, IOException.class);
+ assertDocument("{\"name\":false", BEGIN_OBJECT, NAME, BOOLEAN, IOException.class);
+ assertDocument("{\"name\":false,,", BEGIN_OBJECT, NAME, BOOLEAN, IOException.class);
+ }
+
+ /**
+ * This test behave slightly differently in Gson 2.2 and earlier. It fails
+ * during peek rather than during nextString().
+ */
+ public void testUnterminatedStringFailure() throws IOException {
+ JsonReader reader = new JsonReader(reader("[\"string"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(JsonToken.STRING, reader.peek());
+ try {
+ reader.nextString();
+ fail();
+ } catch (MalformedJsonException expected) {
+ }
+ }
+
+ private void assertDocument(String document, Object... expectations) throws IOException {
+ JsonReader reader = new JsonReader(reader(document));
+ reader.setLenient(true);
+ for (Object expectation : expectations) {
+ if (expectation == BEGIN_OBJECT) {
+ reader.beginObject();
+ } else if (expectation == BEGIN_ARRAY) {
+ reader.beginArray();
+ } else if (expectation == END_OBJECT) {
+ reader.endObject();
+ } else if (expectation == END_ARRAY) {
+ reader.endArray();
+ } else if (expectation == NAME) {
+ assertEquals("name", reader.nextName());
+ } else if (expectation == BOOLEAN) {
+ assertEquals(false, reader.nextBoolean());
+ } else if (expectation == STRING) {
+ assertEquals("string", reader.nextString());
+ } else if (expectation == NUMBER) {
+ assertEquals(123, reader.nextInt());
+ } else if (expectation == NULL) {
+ reader.nextNull();
+ } else if (expectation == IOException.class) {
+ try {
+ reader.peek();
+ fail();
+ } catch (IOException expected) {
+ }
+ } else {
+ throw new AssertionError();
+ }
+ }
+ }
+
+ /**
+ * Returns a reader that returns one character at a time.
+ */
+ private Reader reader(final String s) {
+ /* if (true) */ return new StringReader(s);
+ /* return new Reader() {
+ int position = 0;
+ @Override public int read(char[] buffer, int offset, int count) throws IOException {
+ if (position == s.length()) {
+ return -1;
+ } else if (count > 0) {
+ buffer[offset] = s.charAt(position++);
+ return 1;
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+ @Override public void close() throws IOException {
+ }
+ }; */
+ }
+}
diff --git a/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java b/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java
new file mode 100644
index 00000000..4cfd55a7
--- /dev/null
+++ b/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2010 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.stream;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import junit.framework.TestCase;
+
+@SuppressWarnings("resource")
+public final class JsonWriterTest extends TestCase {
+
+ public void testWrongTopLevelType() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ try {
+ jsonWriter.value("a");
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testTwoNames() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginObject();
+ jsonWriter.name("a");
+ try {
+ jsonWriter.name("a");
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testNameWithoutValue() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginObject();
+ jsonWriter.name("a");
+ try {
+ jsonWriter.endObject();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testValueWithoutName() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginObject();
+ try {
+ jsonWriter.value(true);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testMultipleTopLevelValues() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray().endArray();
+ try {
+ jsonWriter.beginArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testBadNestingObject() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.beginObject();
+ try {
+ jsonWriter.endArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testBadNestingArray() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.beginArray();
+ try {
+ jsonWriter.endObject();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testNullName() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginObject();
+ try {
+ jsonWriter.name(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testNullStringValue() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginObject();
+ jsonWriter.name("a");
+ jsonWriter.value((String) null);
+ jsonWriter.endObject();
+ assertEquals("{\"a\":null}", stringWriter.toString());
+ }
+
+ public void testJsonValue() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginObject();
+ jsonWriter.name("a");
+ jsonWriter.jsonValue("{\"b\":true}");
+ jsonWriter.name("c");
+ jsonWriter.value(1);
+ jsonWriter.endObject();
+ assertEquals("{\"a\":{\"b\":true},\"c\":1}", stringWriter.toString());
+ }
+
+ public void testNonFiniteDoubles() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ try {
+ jsonWriter.value(Double.NaN);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ jsonWriter.value(Double.NEGATIVE_INFINITY);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ jsonWriter.value(Double.POSITIVE_INFINITY);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testNonFiniteBoxedDoubles() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ try {
+ jsonWriter.value(new Double(Double.NaN));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ jsonWriter.value(new Double(Double.NEGATIVE_INFINITY));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ jsonWriter.value(new Double(Double.POSITIVE_INFINITY));
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testDoubles() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.value(-0.0);
+ jsonWriter.value(1.0);
+ jsonWriter.value(Double.MAX_VALUE);
+ jsonWriter.value(Double.MIN_VALUE);
+ jsonWriter.value(0.0);
+ jsonWriter.value(-0.5);
+ jsonWriter.value(2.2250738585072014E-308);
+ jsonWriter.value(Math.PI);
+ jsonWriter.value(Math.E);
+ jsonWriter.endArray();
+ jsonWriter.close();
+ assertEquals("[-0.0,"
+ + "1.0,"
+ + "1.7976931348623157E308,"
+ + "4.9E-324,"
+ + "0.0,"
+ + "-0.5,"
+ + "2.2250738585072014E-308,"
+ + "3.141592653589793,"
+ + "2.718281828459045]", stringWriter.toString());
+ }
+
+ public void testLongs() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.value(0);
+ jsonWriter.value(1);
+ jsonWriter.value(-1);
+ jsonWriter.value(Long.MIN_VALUE);
+ jsonWriter.value(Long.MAX_VALUE);
+ jsonWriter.endArray();
+ jsonWriter.close();
+ assertEquals("[0,"
+ + "1,"
+ + "-1,"
+ + "-9223372036854775808,"
+ + "9223372036854775807]", stringWriter.toString());
+ }
+
+ public void testNumbers() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.value(new BigInteger("0"));
+ jsonWriter.value(new BigInteger("9223372036854775808"));
+ jsonWriter.value(new BigInteger("-9223372036854775809"));
+ jsonWriter.value(new BigDecimal("3.141592653589793238462643383"));
+ jsonWriter.endArray();
+ jsonWriter.close();
+ assertEquals("[0,"
+ + "9223372036854775808,"
+ + "-9223372036854775809,"
+ + "3.141592653589793238462643383]", stringWriter.toString());
+ }
+
+ public void testBooleans() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.value(true);
+ jsonWriter.value(false);
+ jsonWriter.endArray();
+ assertEquals("[true,false]", stringWriter.toString());
+ }
+
+ public void testNulls() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.nullValue();
+ jsonWriter.endArray();
+ assertEquals("[null]", stringWriter.toString());
+ }
+
+ public void testStrings() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.value("a");
+ jsonWriter.value("a\"");
+ jsonWriter.value("\"");
+ jsonWriter.value(":");
+ jsonWriter.value(",");
+ jsonWriter.value("\b");
+ jsonWriter.value("\f");
+ jsonWriter.value("\n");
+ jsonWriter.value("\r");
+ jsonWriter.value("\t");
+ jsonWriter.value(" ");
+ jsonWriter.value("\\");
+ jsonWriter.value("{");
+ jsonWriter.value("}");
+ jsonWriter.value("[");
+ jsonWriter.value("]");
+ jsonWriter.value("\0");
+ jsonWriter.value("\u0019");
+ jsonWriter.endArray();
+ assertEquals("[\"a\","
+ + "\"a\\\"\","
+ + "\"\\\"\","
+ + "\":\","
+ + "\",\","
+ + "\"\\b\","
+ + "\"\\f\","
+ + "\"\\n\","
+ + "\"\\r\","
+ + "\"\\t\","
+ + "\" \","
+ + "\"\\\\\","
+ + "\"{\","
+ + "\"}\","
+ + "\"[\","
+ + "\"]\","
+ + "\"\\u0000\","
+ + "\"\\u0019\"]", stringWriter.toString());
+ }
+
+ public void testUnicodeLineBreaksEscaped() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.value("\u2028 \u2029");
+ jsonWriter.endArray();
+ assertEquals("[\"\\u2028 \\u2029\"]", stringWriter.toString());
+ }
+
+ public void testEmptyArray() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.endArray();
+ assertEquals("[]", stringWriter.toString());
+ }
+
+ public void testEmptyObject() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginObject();
+ jsonWriter.endObject();
+ assertEquals("{}", stringWriter.toString());
+ }
+
+ public void testObjectsInArrays() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginArray();
+ jsonWriter.beginObject();
+ jsonWriter.name("a").value(5);
+ jsonWriter.name("b").value(false);
+ jsonWriter.endObject();
+ jsonWriter.beginObject();
+ jsonWriter.name("c").value(6);
+ jsonWriter.name("d").value(true);
+ jsonWriter.endObject();
+ jsonWriter.endArray();
+ assertEquals("[{\"a\":5,\"b\":false},"
+ + "{\"c\":6,\"d\":true}]", stringWriter.toString());
+ }
+
+ public void testArraysInObjects() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginObject();
+ jsonWriter.name("a");
+ jsonWriter.beginArray();
+ jsonWriter.value(5);
+ jsonWriter.value(false);
+ jsonWriter.endArray();
+ jsonWriter.name("b");
+ jsonWriter.beginArray();
+ jsonWriter.value(6);
+ jsonWriter.value(true);
+ jsonWriter.endArray();
+ jsonWriter.endObject();
+ assertEquals("{\"a\":[5,false],"
+ + "\"b\":[6,true]}", stringWriter.toString());
+ }
+
+ public void testDeepNestingArrays() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ for (int i = 0; i < 20; i++) {
+ jsonWriter.beginArray();
+ }
+ for (int i = 0; i < 20; i++) {
+ jsonWriter.endArray();
+ }
+ assertEquals("[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]", stringWriter.toString());
+ }
+
+ public void testDeepNestingObjects() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginObject();
+ for (int i = 0; i < 20; i++) {
+ jsonWriter.name("a");
+ jsonWriter.beginObject();
+ }
+ for (int i = 0; i < 20; i++) {
+ jsonWriter.endObject();
+ }
+ jsonWriter.endObject();
+ assertEquals("{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":"
+ + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{"
+ + "}}}}}}}}}}}}}}}}}}}}}", stringWriter.toString());
+ }
+
+ public void testRepeatedName() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.beginObject();
+ jsonWriter.name("a").value(true);
+ jsonWriter.name("a").value(false);
+ jsonWriter.endObject();
+ // JsonWriter doesn't attempt to detect duplicate names
+ assertEquals("{\"a\":true,\"a\":false}", stringWriter.toString());
+ }
+
+ public void testPrettyPrintObject() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.setIndent(" ");
+
+ jsonWriter.beginObject();
+ jsonWriter.name("a").value(true);
+ jsonWriter.name("b").value(false);
+ jsonWriter.name("c").value(5.0);
+ jsonWriter.name("e").nullValue();
+ jsonWriter.name("f").beginArray();
+ jsonWriter.value(6.0);
+ jsonWriter.value(7.0);
+ jsonWriter.endArray();
+ jsonWriter.name("g").beginObject();
+ jsonWriter.name("h").value(8.0);
+ jsonWriter.name("i").value(9.0);
+ jsonWriter.endObject();
+ jsonWriter.endObject();
+
+ String expected = "{\n"
+ + " \"a\": true,\n"
+ + " \"b\": false,\n"
+ + " \"c\": 5.0,\n"
+ + " \"e\": null,\n"
+ + " \"f\": [\n"
+ + " 6.0,\n"
+ + " 7.0\n"
+ + " ],\n"
+ + " \"g\": {\n"
+ + " \"h\": 8.0,\n"
+ + " \"i\": 9.0\n"
+ + " }\n"
+ + "}";
+ assertEquals(expected, stringWriter.toString());
+ }
+
+ public void testPrettyPrintArray() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.setIndent(" ");
+
+ jsonWriter.beginArray();
+ jsonWriter.value(true);
+ jsonWriter.value(false);
+ jsonWriter.value(5.0);
+ jsonWriter.nullValue();
+ jsonWriter.beginObject();
+ jsonWriter.name("a").value(6.0);
+ jsonWriter.name("b").value(7.0);
+ jsonWriter.endObject();
+ jsonWriter.beginArray();
+ jsonWriter.value(8.0);
+ jsonWriter.value(9.0);
+ jsonWriter.endArray();
+ jsonWriter.endArray();
+
+ String expected = "[\n"
+ + " true,\n"
+ + " false,\n"
+ + " 5.0,\n"
+ + " null,\n"
+ + " {\n"
+ + " \"a\": 6.0,\n"
+ + " \"b\": 7.0\n"
+ + " },\n"
+ + " [\n"
+ + " 8.0,\n"
+ + " 9.0\n"
+ + " ]\n"
+ + "]";
+ assertEquals(expected, stringWriter.toString());
+ }
+
+ public void testLenientWriterPermitsMultipleTopLevelValues() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter writer = new JsonWriter(stringWriter);
+ writer.setLenient(true);
+ writer.beginArray();
+ writer.endArray();
+ writer.beginArray();
+ writer.endArray();
+ writer.close();
+ assertEquals("[][]", stringWriter.toString());
+ }
+
+ public void testStrictWriterDoesNotPermitMultipleTopLevelValues() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter writer = new JsonWriter(stringWriter);
+ writer.beginArray();
+ writer.endArray();
+ try {
+ writer.beginArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testClosedWriterThrowsOnStructure() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter writer = new JsonWriter(stringWriter);
+ writer.beginArray();
+ writer.endArray();
+ writer.close();
+ try {
+ writer.beginArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ writer.endArray();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ writer.beginObject();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ writer.endObject();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testClosedWriterThrowsOnName() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter writer = new JsonWriter(stringWriter);
+ writer.beginArray();
+ writer.endArray();
+ writer.close();
+ try {
+ writer.name("a");
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testClosedWriterThrowsOnValue() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter writer = new JsonWriter(stringWriter);
+ writer.beginArray();
+ writer.endArray();
+ writer.close();
+ try {
+ writer.value("a");
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testClosedWriterThrowsOnFlush() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter writer = new JsonWriter(stringWriter);
+ writer.beginArray();
+ writer.endArray();
+ writer.close();
+ try {
+ writer.flush();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testWriterCloseIsIdempotent() throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter writer = new JsonWriter(stringWriter);
+ writer.beginArray();
+ writer.endArray();
+ writer.close();
+ writer.close();
+ }
+}