/* * 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 static com.google.common.truth.Truth.assertThat; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonObject; 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 com.google.gson.stream.JsonReader; 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; import org.junit.Before; import org.junit.Test; /** * Functional tests for the serialization and deserialization of parameterized types in Gson. * * @author Inderjeet Singh * @author Joel Leitch */ public class ParameterizedTypesTest { private Gson gson; @Before public void setUp() { gson = new Gson(); } @Test public void testParameterizedTypesSerialization() { MyParameterizedType src = new MyParameterizedType<>(10); Type typeOfSrc = new TypeToken>() {}.getType(); String json = gson.toJson(src, typeOfSrc); assertThat(json).isEqualTo(src.getExpectedJson()); } @Test public void testParameterizedTypeDeserialization() { 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); assertThat(actual).isEqualTo(expected); } @Test public void testTypesWithMultipleParametersSerialization() { 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\":\"\"}}"; assertThat(json).isEqualTo(expected); } @Test public void testTypesWithMultipleParametersDeserialization() { 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()); assertThat(target).isEqualTo(expected); } @Test 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); assertThat(json).isEqualTo(MyParameterizedTypeAdapter.getExpectedJson(intTarget)); MyParameterizedType stringTarget = new MyParameterizedType<>("abc"); json = gson.toJson(stringTarget, ptStringType); assertThat(json).isEqualTo(MyParameterizedTypeAdapter.getExpectedJson(stringTarget)); } @Test 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<>(0)) .create(); MyParameterizedType src = new MyParameterizedType<>(10); String json = MyParameterizedTypeAdapter.getExpectedJson(src); MyParameterizedType intTarget = gson.fromJson(json, ptIntegerType); assertThat(intTarget.value).isEqualTo(10); MyParameterizedType srcStr = new MyParameterizedType<>("abc"); json = MyParameterizedTypeAdapter.getExpectedJson(srcStr); MyParameterizedType stringTarget = gson.fromJson(json, ptStringType); assertThat(stringTarget.value).isEqualTo("abc"); } @Test public void testParameterizedTypesWithWriterSerialization() { Writer writer = new StringWriter(); MyParameterizedType src = new MyParameterizedType<>(10); Type typeOfSrc = new TypeToken>() {}.getType(); gson.toJson(src, typeOfSrc, writer); assertThat(writer.toString()).isEqualTo(src.getExpectedJson()); } @Test public void testParameterizedTypeWithReaderDeserialization() { 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); assertThat(actual).isEqualTo(expected); } @SuppressWarnings("varargs") @SafeVarargs private static T[] arrayOf(T... args) { return args; } @Test public void testVariableTypeFieldsAndGenericArraysSerialization() { Integer obj = 0; Integer[] array = { 1, 2, 3 }; List list = new ArrayList<>(); list.add(4); list.add(5); List[] arrayOfLists = arrayOf(list, list); Type typeOfSrc = new TypeToken>() {}.getType(); ObjectWithTypeVariables objToSerialize = new ObjectWithTypeVariables<>(obj, array, list, arrayOfLists, list, arrayOfLists); String json = gson.toJson(objToSerialize, typeOfSrc); assertThat(json).isEqualTo(objToSerialize.getExpectedJson()); } @Test public void testVariableTypeFieldsAndGenericArraysDeserialization() { Integer obj = 0; Integer[] array = { 1, 2, 3 }; List list = new ArrayList<>(); list.add(4); list.add(5); List[] arrayOfLists = arrayOf(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); assertThat(json).isEqualTo(objAfterDeserialization.getExpectedJson()); } @Test public void testVariableTypeDeserialization() { 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); assertThat(json).isEqualTo(objAfterDeserialization.getExpectedJson()); } @Test public void testVariableTypeArrayDeserialization() { 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); assertThat(json).isEqualTo(objAfterDeserialization.getExpectedJson()); } @Test public void testParameterizedTypeWithVariableTypeDeserialization() { 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); assertThat(json).isEqualTo(objAfterDeserialization.getExpectedJson()); } @Test public void testParameterizedTypeGenericArraysSerialization() { List list = new ArrayList<>(); list.add(1); list.add(2); List[] arrayOfLists = arrayOf(list, list); Type typeOfSrc = new TypeToken>() {}.getType(); ObjectWithTypeVariables objToSerialize = new ObjectWithTypeVariables<>(null, null, null, arrayOfLists, null, null); String json = gson.toJson(objToSerialize, typeOfSrc); assertThat(json).isEqualTo("{\"arrayOfListOfTypeParameters\":[[1,2],[1,2]]}"); } @Test public void testParameterizedTypeGenericArraysDeserialization() { List list = new ArrayList<>(); list.add(1); list.add(2); List[] arrayOfLists = arrayOf(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); assertThat(json).isEqualTo(objAfterDeserialization.getExpectedJson()); } /** * 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") int q = 10; } private static class MyQuantity extends Quantity { @SuppressWarnings("unused") 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; } @Test public void testDeepParameterizedTypeSerialization() { Amount amount = new Amount<>(); String json = gson.toJson(amount); assertThat(json).contains("value"); assertThat(json).contains("30"); } @Test public void testDeepParameterizedTypeDeserialization() { String json = "{value:30}"; Type type = new TypeToken>() {}.getType(); Amount amount = gson.fromJson(json, type); assertThat(amount.value).isEqualTo(30); } // End: tests to reproduce issue 103 private static void assertCorrectlyDeserialized(Object object) { @SuppressWarnings("unchecked") List list = (List) object; assertThat(list.size()).isEqualTo(1); assertThat(list.get(0).q).isEqualTo(4); } @Test public void testGsonFromJsonTypeToken() { TypeToken> typeToken = new TypeToken>() {}; Type type = typeToken.getType(); { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("q", 4); JsonArray jsonArray = new JsonArray(); jsonArray.add(jsonObject); assertCorrectlyDeserialized(gson.fromJson(jsonArray, typeToken)); assertCorrectlyDeserialized(gson.fromJson(jsonArray, type)); } String json = "[{\"q\":4}]"; { assertCorrectlyDeserialized(gson.fromJson(json, typeToken)); assertCorrectlyDeserialized(gson.fromJson(json, type)); } { assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), typeToken)); assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), type)); } { JsonReader reader = new JsonReader(new StringReader(json)); assertCorrectlyDeserialized(gson.fromJson(reader, typeToken)); reader = new JsonReader(new StringReader(json)); assertCorrectlyDeserialized(gson.fromJson(reader, type)); } } }