501 lines
18 KiB
Java
501 lines
18 KiB
Java
/*
|
|
* 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<>(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<>(bag);
|
|
Type expectedType = new TypeToken<MyParameterizedType<BagOfPrimitives>>() {}.getType();
|
|
BagOfPrimitives bagDefaultInstance = new BagOfPrimitives();
|
|
Gson gson = new GsonBuilder().registerTypeAdapter(
|
|
expectedType, new MyParameterizedTypeInstanceCreator<>(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<>(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<>(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<>(10);
|
|
String json = gson.toJson(intTarget, ptIntegerType);
|
|
assertEquals(MyParameterizedTypeAdapter.<Integer>getExpectedJson(intTarget), json);
|
|
|
|
MyParameterizedType<String> stringTarget = new MyParameterizedType<>("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<>(""))
|
|
.registerTypeAdapter(ptIntegerType, new MyParameterizedTypeInstanceCreator<>(0))
|
|
.create();
|
|
|
|
MyParameterizedType<Integer> src = new MyParameterizedType<>(10);
|
|
String json = MyParameterizedTypeAdapter.<Integer>getExpectedJson(src);
|
|
MyParameterizedType<Integer> intTarget = gson.fromJson(json, ptIntegerType);
|
|
assertEquals(10, intTarget.value.intValue());
|
|
|
|
MyParameterizedType<String> srcStr = new MyParameterizedType<>("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<>(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<>(bag);
|
|
Type expectedType = new TypeToken<MyParameterizedType<BagOfPrimitives>>() {}.getType();
|
|
BagOfPrimitives bagDefaultInstance = new BagOfPrimitives();
|
|
Gson gson = new GsonBuilder().registerTypeAdapter(
|
|
expectedType, new MyParameterizedTypeInstanceCreator<>(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<>();
|
|
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<>(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<>();
|
|
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<>(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<>(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<>(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<>();
|
|
list.add(4);
|
|
list.add(5);
|
|
|
|
Type typeOfSrc = new TypeToken<ObjectWithTypeVariables<Integer>>() {}.getType();
|
|
ObjectWithTypeVariables<Integer> objToSerialize =
|
|
new ObjectWithTypeVariables<>(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<>();
|
|
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<>(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<>();
|
|
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<>(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<>();
|
|
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
|
|
}
|