diff --git a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java b/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java index 685ee835..8378ce96 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 @@ final class ParameterizedTypeHandlerMap { rawType = ((ParameterizedType)type).getRawType(); handler = map.get(rawType); } + // Check for map or collection if (handler == null) { if (rawType instanceof Class) { @@ -87,13 +88,20 @@ final class ParameterizedTypeHandlerMap { } 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 50696bda..acda71a6 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.Array; 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 @@ final class TypeUtils { 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 05ee8ccc..d397b55c 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.lang.reflect.Type; 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 @@ public class CollectionTest extends TestCase { } catch (JsonParseException expected) { } } + + public void testWildcardPrimitiveCollectionSerilaization() throws Exception { + Collection target = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); + Type collectionType = new TypeToken>() { }.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>() { }.getType(); + Collection target = gson.fromJson(json, collectionType); + assertEquals(9, target.size()); + assertTrue(target.contains(1)); + assertTrue(target.contains(9)); + } + + public void testWildcardCollectionField() throws Exception { + Collection collection = new ArrayList(); + 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 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 @@ public class CollectionTest extends TestCase { } return ints; } + + private static class ObjectWithWildcardCollection { + private final Collection collection; + + @SuppressWarnings("unchecked") + public ObjectWithWildcardCollection() { + this(Collections.EMPTY_LIST); + } + + public ObjectWithWildcardCollection(Collection collection) { + this.collection = collection; + } + + public Collection 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 9d7bb0ab..ea2501cd 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 class MapTest extends TestCase { } public void disable_testMapSubclassDeserialization() { - Gson gson = new GsonBuilder().registerTypeAdapter(MyMap.class, new InstanceCreator(){ + Gson gson = new GsonBuilder().registerTypeAdapter(MyMap.class, new InstanceCreator() { public MyMap createInstance(Type type) { return new MyMap(); } @@ -173,6 +174,25 @@ public class MapTest extends TestCase { assertEquals("2", map.get("b")); } + public void testMapSerializationWithWildcardValues() { + Map> map = + new LinkedHashMap>(); + map.put("test", null); + Type typeOfMap = + new TypeToken>>() {}.getType(); + String json = gson.toJson(map, typeOfMap); + + assertEquals("{}", json); + } + + public void testMapDeserializationWithWildcardValues() { + Type typeOfMap = new TypeToken>() {}.getType(); + Map map = gson.fromJson("{\"test\":123}", typeOfMap); + assertEquals(1, map.size()); + assertEquals(new Long(123L), map.get("test")); + } + + private static class MyMap extends LinkedHashMap { private static final long serialVersionUID = 1L;