/* * 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.ParamterizedTypeFixtures.MyParameterizedType; import com.google.gson.ParamterizedTypeFixtures.MyParameterizedTypeAdapter; import com.google.gson.ParamterizedTypeFixtures.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 src = new MyParameterizedType(10); Type typeOfSrc = new TypeToken>() {}.getType(); String json = gson.toJson(src, typeOfSrc); assertEquals(src.getExpectedJson(), json); } public void testParameterizedTypeDeserialization() throws Exception { BagOfPrimitives bag = new BagOfPrimitives(); MyParameterizedType expected = new MyParameterizedType(bag); Type expectedType = new TypeToken>() {}.getType(); BagOfPrimitives bagDefaultInstance = new BagOfPrimitives(); Gson gson = new GsonBuilder().registerTypeAdapter( expectedType, new MyParameterizedTypeInstanceCreator(bagDefaultInstance)) .create(); String json = expected.getExpectedJson(); MyParameterizedType actual = gson.fromJson(json, expectedType); assertEquals(expected, actual); } public void testTypesWithMultipleParametersSerialization() throws Exception { MultiParameters src = new MultiParameters(10, 1.0F, 2.1D, "abc", new BagOfPrimitives()); Type typeOfSrc = new TypeToken>() {}.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>() {}.getType(); String json = "{\"a\":10,\"b\":1.0,\"c\":2.1,\"d\":\"abc\"," + "\"e\":{\"longValue\":0,\"intValue\":0,\"booleanValue\":false,\"stringValue\":\"\"}}"; MultiParameters target = gson.fromJson(json, typeOfTarget); MultiParameters expected = new MultiParameters(10, 1.0F, 2.1D, "abc", new BagOfPrimitives()); assertEquals(expected, target); } public void testParameterizedTypeWithCustomSerializer() { Type ptIntegerType = new TypeToken>() {}.getType(); Type ptStringType = new TypeToken>() {}.getType(); Gson gson = new GsonBuilder() .registerTypeAdapter(ptIntegerType, new MyParameterizedTypeAdapter()) .registerTypeAdapter(ptStringType, new MyParameterizedTypeAdapter()) .create(); MyParameterizedType intTarget = new MyParameterizedType(10); String json = gson.toJson(intTarget, ptIntegerType); assertEquals(MyParameterizedTypeAdapter.getExpectedJson(intTarget), json); MyParameterizedType stringTarget = new MyParameterizedType("abc"); json = gson.toJson(stringTarget, ptStringType); assertEquals(MyParameterizedTypeAdapter.getExpectedJson(stringTarget), json); } public void testParameterizedTypesWithCustomDeserializer() { Type ptIntegerType = new TypeToken>() {}.getType(); Type ptStringType = new TypeToken>() {}.getType(); Gson gson = new GsonBuilder().registerTypeAdapter( ptIntegerType, new MyParameterizedTypeAdapter()) .registerTypeAdapter(ptStringType, new MyParameterizedTypeAdapter()) .registerTypeAdapter(ptStringType, new MyParameterizedTypeInstanceCreator("")) .registerTypeAdapter(ptIntegerType, new MyParameterizedTypeInstanceCreator(new Integer(0))) .create(); MyParameterizedType src = new MyParameterizedType(10); String json = MyParameterizedTypeAdapter.getExpectedJson(src); MyParameterizedType intTarget = gson.fromJson(json, ptIntegerType); assertEquals(10, (int) intTarget.value); MyParameterizedType srcStr = new MyParameterizedType("abc"); json = MyParameterizedTypeAdapter.getExpectedJson(srcStr); MyParameterizedType stringTarget = gson.fromJson(json, ptStringType); assertEquals("abc", stringTarget.value); } public void testParameterizedTypesWithWriterSerialization() throws Exception { Writer writer = new StringWriter(); MyParameterizedType src = new MyParameterizedType(10); Type typeOfSrc = new TypeToken>() {}.getType(); gson.toJson(src, typeOfSrc, writer); assertEquals(src.getExpectedJson(), writer.toString()); } public void testParameterizedTypeWithReaderDeserialization() throws Exception { BagOfPrimitives bag = new BagOfPrimitives(); MyParameterizedType expected = new MyParameterizedType(bag); Type expectedType = new TypeToken>() {}.getType(); BagOfPrimitives bagDefaultInstance = new BagOfPrimitives(); Gson gson = new GsonBuilder().registerTypeAdapter( expectedType, new MyParameterizedTypeInstanceCreator(bagDefaultInstance)) .create(); Reader json = new StringReader(expected.getExpectedJson()); MyParameterizedType actual = gson.fromJson(json, expectedType); assertEquals(expected, actual); } @SuppressWarnings("unchecked") public void testVariableTypeFieldsAndGenericArraysSerialization() throws Exception { Integer obj = 0; Integer[] array = { 1, 2, 3 }; List list = new ArrayList(); list.add(4); list.add(5); List[] arrayOfLists = new List[] { list, list }; Type typeOfSrc = new TypeToken>() {}.getType(); ObjectWithTypeVariables objToSerialize = new ObjectWithTypeVariables(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 list = new ArrayList(); list.add(4); list.add(5); List[] arrayOfLists = new List[] { list, list }; Type typeOfSrc = new TypeToken>() {}.getType(); ObjectWithTypeVariables objToSerialize = new ObjectWithTypeVariables(obj, array, list, arrayOfLists, list, arrayOfLists); String json = gson.toJson(objToSerialize, typeOfSrc); ObjectWithTypeVariables objAfterDeserialization = gson.fromJson(json, typeOfSrc); assertEquals(objAfterDeserialization.getExpectedJson(), json); } public void testVariableTypeDeserialization() throws Exception { Type typeOfSrc = new TypeToken>() {}.getType(); ObjectWithTypeVariables objToSerialize = new ObjectWithTypeVariables(0, null, null, null, null, null); String json = gson.toJson(objToSerialize, typeOfSrc); ObjectWithTypeVariables objAfterDeserialization = gson.fromJson(json, typeOfSrc); assertEquals(objAfterDeserialization.getExpectedJson(), json); } public void testVariableTypeArrayDeserialization() throws Exception { Integer[] array = { 1, 2, 3 }; Type typeOfSrc = new TypeToken>() {}.getType(); ObjectWithTypeVariables objToSerialize = new ObjectWithTypeVariables(null, array, null, null, null, null); String json = gson.toJson(objToSerialize, typeOfSrc); ObjectWithTypeVariables objAfterDeserialization = gson.fromJson(json, typeOfSrc); assertEquals(objAfterDeserialization.getExpectedJson(), json); } public void testParameterizedTypeWithVariableTypeDeserialization() throws Exception { List list = new ArrayList(); list.add(4); list.add(5); Type typeOfSrc = new TypeToken>() {}.getType(); ObjectWithTypeVariables objToSerialize = new ObjectWithTypeVariables(null, null, list, null, null, null); String json = gson.toJson(objToSerialize, typeOfSrc); ObjectWithTypeVariables objAfterDeserialization = gson.fromJson(json, typeOfSrc); assertEquals(objAfterDeserialization.getExpectedJson(), json); } @SuppressWarnings("unchecked") public void testParameterizedTypeGenericArraysSerialization() throws Exception { List list = new ArrayList(); list.add(1); list.add(2); List[] arrayOfLists = new List[] { list, list }; Type typeOfSrc = new TypeToken>() {}.getType(); ObjectWithTypeVariables objToSerialize = new ObjectWithTypeVariables(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 list = new ArrayList(); list.add(1); list.add(2); List[] arrayOfLists = new List[] { list, list }; Type typeOfSrc = new TypeToken>() {}.getType(); ObjectWithTypeVariables objToSerialize = new ObjectWithTypeVariables(null, null, null, arrayOfLists, null, null); String json = gson.toJson(objToSerialize, typeOfSrc); ObjectWithTypeVariables objAfterDeserialization = gson.fromJson(json, typeOfSrc); assertEquals(objAfterDeserialization.getExpectedJson(), json); } /** * An test object that has fields that are type variables. * * @param Enforce T to be a string to make writing the "toExpectedJson" method easier. */ private static class ObjectWithTypeVariables { private final T typeParameterObj; private final T[] typeParameterArray; private final List listOfTypeParameters; private final List[] arrayOfListOfTypeParameters; private final List listOfWildcardTypeParameters; private final List[] arrayOfListOfWildcardTypeParameters; // For use by Gson @SuppressWarnings("unused") private ObjectWithTypeVariables() { this(null, null, null, null, null, null); } public ObjectWithTypeVariables(T obj, T[] array, List list, List[] arrayOfList, List wildcardList, List[] 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 iterable) { boolean isFirst = true; for (T obj : iterable) { if (!isFirst) { sb.append(','); } isFirst = false; sb.append(toString(obj)); } } private void appendObjectsToBuilder(StringBuilder sb, List[] arrayOfList) { boolean isFirst = true; for (List 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 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 other = (MultiParameters) 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") private int q = 10; } private static class MyQuantity extends Quantity { @SuppressWarnings("unused") private int q2 = 20; } private interface Measurable { } private interface Field { } private interface Immutable { } public static final class Amount implements Measurable, Field>, Serializable, Immutable { private static final long serialVersionUID = -7560491093120970437L; int value = 30; } public void testDeepParameterizedTypeSerialization() { Amount amount = new Amount(); String json = gson.toJson(amount); assertTrue(json.contains("value")); assertTrue(json.contains("30")); } public void testDeepParameterizedTypeDeserialization() { String json = "{value:30}"; Type type = new TypeToken>() {}.getType(); Amount amount = gson.fromJson(json, type); assertEquals(30, amount.value); } // End: tests to reproduce issue 103 }