Add support for wildcard type on Maps, Collections and other parameterized types.
diff --git a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java b/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java
index 685ee83..8378ce9 100644
--- a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java
+++ b/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java
@@ -70,6 +70,7 @@
rawType = ((ParameterizedType)type).getRawType();
handler = map.get(rawType);
}
+
// Check for map or collection
if (handler == null) {
if (rawType instanceof Class) {
@@ -87,13 +88,20 @@
}
private synchronized T getRawHandlerFor(Type type) {
- T handler = map.get(type);
if (type instanceof Map) {
- handler = map.get(Map.class);
+ return map.get(Map.class);
} 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) {
diff --git a/gson/src/main/java/com/google/gson/TypeUtils.java b/gson/src/main/java/com/google/gson/TypeUtils.java
index 50696bd..acda71a 100644
--- a/gson/src/main/java/com/google/gson/TypeUtils.java
+++ b/gson/src/main/java/com/google/gson/TypeUtils.java
@@ -20,6 +20,7 @@
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
/**
* Utility class containing some methods for obtaining information on types.
@@ -75,6 +76,9 @@
GenericArrayType actualType = (GenericArrayType) type;
Class<?> rawClass = toRawClass(actualType.getGenericComponentType());
return wrapWithArray(rawClass);
+ } else if (type instanceof WildcardType) {
+ WildcardType castedType = (WildcardType) type;
+ return toRawClass(castedType.getUpperBounds()[0]);
} else {
throw new IllegalArgumentException("Type \'" + type + "\' is not a Class, "
+ "ParameterizedType, or GenericArrayType. Can't extract class.");
diff --git a/gson/src/test/java/com/google/gson/functional/CollectionTest.java b/gson/src/test/java/com/google/gson/functional/CollectionTest.java
index 05ee8cc..d397b55 100644
--- a/gson/src/test/java/com/google/gson/functional/CollectionTest.java
+++ b/gson/src/test/java/com/google/gson/functional/CollectionTest.java
@@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -244,6 +245,44 @@
} catch (JsonParseException expected) {
}
}
+
+ 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")
private static int[] toIntArray(Collection collection) {
@@ -259,4 +298,21 @@
}
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;
+ }
+ }
}
diff --git a/gson/src/test/java/com/google/gson/functional/MapTest.java b/gson/src/test/java/com/google/gson/functional/MapTest.java
index 9d7bb0a..ea2501c 100755
--- a/gson/src/test/java/com/google/gson/functional/MapTest.java
+++ b/gson/src/test/java/com/google/gson/functional/MapTest.java
@@ -16,6 +16,7 @@
package com.google.gson.functional;
import java.lang.reflect.Type;
+import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -162,7 +163,7 @@
}
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) {
return new MyMap();
}
@@ -173,6 +174,25 @@
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 final long serialVersionUID = 1L;