diff options
Diffstat (limited to 'gson/src/test/java')
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<B,C>, 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(); + } +} |