diff --git a/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java index aa1e3ff61b..beacaaba82 100644 --- a/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java +++ b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java @@ -23,6 +23,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; +import java.util.EnumMap; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -199,7 +200,26 @@ private ObjectConstructor newDefaultImplementationConstructor( } if (Map.class.isAssignableFrom(rawType)) { - if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) { + // Only support creation of EnumMap, but not of custom subtypes; for them type parameters + // and constructor parameter might have completely different meaning + if (rawType == EnumMap.class) { + return new ObjectConstructor() { + @Override public T construct() { + if (type instanceof ParameterizedType) { + Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; + if (elementType instanceof Class) { + @SuppressWarnings("rawtypes") + T map = (T) new EnumMap((Class) elementType); + return map; + } else { + throw new JsonIOException("Invalid EnumMap type: " + type.toString()); + } + } else { + throw new JsonIOException("Invalid EnumMap type: " + type.toString()); + } + } + }; + } else if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) { return new ObjectConstructor() { @Override public T construct() { return (T) new ConcurrentSkipListMap(); diff --git a/gson/src/test/java/com/google/gson/functional/EnumTest.java b/gson/src/test/java/com/google/gson/functional/EnumTest.java index 8a1c6e12c6..fd21a5c271 100644 --- a/gson/src/test/java/com/google/gson/functional/EnumTest.java +++ b/gson/src/test/java/com/google/gson/functional/EnumTest.java @@ -31,7 +31,10 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; import java.util.EnumSet; +import java.util.Map; import java.util.Set; import junit.framework.TestCase; /** @@ -150,6 +153,8 @@ public void testEnumCaseMapping() { public void testEnumSet() { EnumSet foo = EnumSet.of(Roshambo.ROCK, Roshambo.PAPER); String json = gson.toJson(foo); + assertEquals("[\"ROCK\",\"PAPER\"]", json); + Type type = new TypeToken>() {}.getType(); EnumSet bar = gson.fromJson(json, type); assertTrue(bar.contains(Roshambo.ROCK)); @@ -157,6 +162,18 @@ public void testEnumSet() { assertFalse(bar.contains(Roshambo.SCISSORS)); } + public void testEnumMap() throws Exception { + EnumMap map = new EnumMap(MyEnum.class); + map.put(MyEnum.VALUE1, "test"); + String json = gson.toJson(map); + assertEquals("{\"VALUE1\":\"test\"}", json); + + Type type = new TypeToken>() {}.getType(); + EnumMap actualMap = gson.fromJson("{\"VALUE1\":\"test\"}", type); + Map expectedMap = Collections.singletonMap(MyEnum.VALUE1, "test"); + assertEquals(expectedMap, actualMap); + } + public enum Roshambo { ROCK { @Override Roshambo defeats() {