Add support for wildcard type on Maps, Collections and other parameterized types.

This commit is contained in:
Joel Leitch 2008-12-14 07:12:04 +00:00
parent 8e1c6a2b37
commit fc65667d2f
4 changed files with 93 additions and 5 deletions

View File

@ -70,6 +70,7 @@ final class ParameterizedTypeHandlerMap<T> {
rawType = ((ParameterizedType)type).getRawType(); rawType = ((ParameterizedType)type).getRawType();
handler = map.get(rawType); handler = map.get(rawType);
} }
// Check for map or collection // Check for map or collection
if (handler == null) { if (handler == null) {
if (rawType instanceof Class) { if (rawType instanceof Class) {
@ -87,13 +88,20 @@ final class ParameterizedTypeHandlerMap<T> {
} }
private synchronized T getRawHandlerFor(Type type) { private synchronized T getRawHandlerFor(Type type) {
T handler = map.get(type);
if (type instanceof Map) { if (type instanceof Map) {
handler = map.get(Map.class); return map.get(Map.class);
} else if (type instanceof Collection) { } else if (type instanceof Collection) {
handler = map.get(Collection.class); return map.get(Collection.class);
} else {
T handler = map.get(type);
if (handler == null) {
Class<?> rawClass = TypeUtils.toRawClass(type);
if (rawClass != type) {
handler = getHandlerFor(rawClass);
}
}
return handler;
} }
return handler;
} }
public synchronized boolean hasAnyHandlerFor(Type type) { public synchronized boolean hasAnyHandlerFor(Type type) {

View File

@ -20,6 +20,7 @@ import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType; import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
/** /**
* Utility class containing some methods for obtaining information on types. * Utility class containing some methods for obtaining information on types.
@ -75,6 +76,9 @@ final class TypeUtils {
GenericArrayType actualType = (GenericArrayType) type; GenericArrayType actualType = (GenericArrayType) type;
Class<?> rawClass = toRawClass(actualType.getGenericComponentType()); Class<?> rawClass = toRawClass(actualType.getGenericComponentType());
return wrapWithArray(rawClass); return wrapWithArray(rawClass);
} else if (type instanceof WildcardType) {
WildcardType castedType = (WildcardType) type;
return toRawClass(castedType.getUpperBounds()[0]);
} else { } else {
throw new IllegalArgumentException("Type \'" + type + "\' is not a Class, " throw new IllegalArgumentException("Type \'" + type + "\' is not a Class, "
+ "ParameterizedType, or GenericArrayType. Can't extract class."); + "ParameterizedType, or GenericArrayType. Can't extract class.");

View File

@ -29,6 +29,7 @@ import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -245,6 +246,44 @@ public class CollectionTest extends TestCase {
} }
} }
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));
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static int[] toIntArray(Collection collection) { private static int[] toIntArray(Collection collection) {
int[] ints = new int[collection.size()]; int[] ints = new int[collection.size()];
@ -259,4 +298,21 @@ public class CollectionTest extends TestCase {
} }
return ints; return ints;
} }
private static class ObjectWithWildcardCollection {
private final Collection<? extends BagOfPrimitives> collection;
@SuppressWarnings("unchecked")
public ObjectWithWildcardCollection() {
this(Collections.EMPTY_LIST);
}
public ObjectWithWildcardCollection(Collection<? extends BagOfPrimitives> collection) {
this.collection = collection;
}
public Collection<? extends BagOfPrimitives> getCollection() {
return collection;
}
}
} }

View File

@ -16,6 +16,7 @@
package com.google.gson.functional; package com.google.gson.functional;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -162,7 +163,7 @@ public class MapTest extends TestCase {
} }
public void disable_testMapSubclassDeserialization() { public void disable_testMapSubclassDeserialization() {
Gson gson = new GsonBuilder().registerTypeAdapter(MyMap.class, new InstanceCreator<MyMap>(){ Gson gson = new GsonBuilder().registerTypeAdapter(MyMap.class, new InstanceCreator<MyMap>() {
public MyMap createInstance(Type type) { public MyMap createInstance(Type type) {
return new MyMap(); return new MyMap();
} }
@ -173,6 +174,25 @@ public class MapTest extends TestCase {
assertEquals("2", map.get("b")); assertEquals("2", map.get("b"));
} }
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 class MyMap extends LinkedHashMap<String, String> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;