Registering default type hierarchy adapters first and allow users to override them.

This allows the default EnumTypeAdapter to be overridden for a specific hierachy adapter for Enum with anonymized sub-classes.
This commit is contained in:
Inderjeet Singh 2011-01-19 23:28:28 +00:00
parent fd0f526fb0
commit 2b9f81e8b5
5 changed files with 83 additions and 31 deletions

View File

@ -99,8 +99,12 @@ final class DefaultTypeAdapters {
// constants will appear as nulls. // constants will appear as nulls.
private static final ParameterizedTypeHandlerMap<JsonSerializer<?>> DEFAULT_SERIALIZERS = private static final ParameterizedTypeHandlerMap<JsonSerializer<?>> DEFAULT_SERIALIZERS =
createDefaultSerializers(); createDefaultSerializers();
static final ParameterizedTypeHandlerMap<JsonSerializer<?>> DEFAULT_HIERARCHY_SERIALIZERS =
createDefaultHierarchySerializers();
private static final ParameterizedTypeHandlerMap<JsonDeserializer<?>> DEFAULT_DESERIALIZERS = private static final ParameterizedTypeHandlerMap<JsonDeserializer<?>> DEFAULT_DESERIALIZERS =
createDefaultDeserializers(); createDefaultDeserializers();
static final ParameterizedTypeHandlerMap<JsonDeserializer<?>> DEFAULT_HIERARCHY_DESERIALIZERS =
createDefaultHierarchyDeserializers();
private static final ParameterizedTypeHandlerMap<InstanceCreator<?>> DEFAULT_INSTANCE_CREATORS = private static final ParameterizedTypeHandlerMap<InstanceCreator<?>> DEFAULT_INSTANCE_CREATORS =
createDefaultInstanceCreators(); createDefaultInstanceCreators();
@ -108,14 +112,10 @@ final class DefaultTypeAdapters {
ParameterizedTypeHandlerMap<JsonSerializer<?>> map = ParameterizedTypeHandlerMap<JsonSerializer<?>> map =
new ParameterizedTypeHandlerMap<JsonSerializer<?>>(); new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
map.registerForTypeHierarchy(Enum.class, ENUM_TYPE_ADAPTER);
map.registerForTypeHierarchy(InetAddress.class, INET_ADDRESS_ADAPTER);
map.register(URL.class, URL_TYPE_ADAPTER); map.register(URL.class, URL_TYPE_ADAPTER);
map.register(URI.class, URI_TYPE_ADAPTER); map.register(URI.class, URI_TYPE_ADAPTER);
map.register(UUID.class, UUUID_TYPE_ADAPTER); map.register(UUID.class, UUUID_TYPE_ADAPTER);
map.register(Locale.class, LOCALE_TYPE_ADAPTER); map.register(Locale.class, LOCALE_TYPE_ADAPTER);
map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER);
map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER);
map.register(Date.class, DATE_TYPE_ADAPTER); map.register(Date.class, DATE_TYPE_ADAPTER);
map.register(java.sql.Date.class, JAVA_SQL_DATE_TYPE_ADAPTER); map.register(java.sql.Date.class, JAVA_SQL_DATE_TYPE_ADAPTER);
map.register(Timestamp.class, DATE_TYPE_ADAPTER); map.register(Timestamp.class, DATE_TYPE_ADAPTER);
@ -143,17 +143,24 @@ final class DefaultTypeAdapters {
return map; return map;
} }
private static ParameterizedTypeHandlerMap<JsonSerializer<?>> createDefaultHierarchySerializers() {
ParameterizedTypeHandlerMap<JsonSerializer<?>> map =
new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
map.registerForTypeHierarchy(Enum.class, ENUM_TYPE_ADAPTER);
map.registerForTypeHierarchy(InetAddress.class, INET_ADDRESS_ADAPTER);
map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER);
map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER);
map.makeUnmodifiable();
return map;
}
private static ParameterizedTypeHandlerMap<JsonDeserializer<?>> createDefaultDeserializers() { private static ParameterizedTypeHandlerMap<JsonDeserializer<?>> createDefaultDeserializers() {
ParameterizedTypeHandlerMap<JsonDeserializer<?>> map = ParameterizedTypeHandlerMap<JsonDeserializer<?>> map =
new ParameterizedTypeHandlerMap<JsonDeserializer<?>>(); new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
map.registerForTypeHierarchy(Enum.class, wrapDeserializer(ENUM_TYPE_ADAPTER));
map.registerForTypeHierarchy(InetAddress.class, wrapDeserializer(INET_ADDRESS_ADAPTER));
map.register(URL.class, wrapDeserializer(URL_TYPE_ADAPTER)); map.register(URL.class, wrapDeserializer(URL_TYPE_ADAPTER));
map.register(URI.class, wrapDeserializer(URI_TYPE_ADAPTER)); map.register(URI.class, wrapDeserializer(URI_TYPE_ADAPTER));
map.register(UUID.class, wrapDeserializer(UUUID_TYPE_ADAPTER)); map.register(UUID.class, wrapDeserializer(UUUID_TYPE_ADAPTER));
map.register(Locale.class, wrapDeserializer(LOCALE_TYPE_ADAPTER)); map.register(Locale.class, wrapDeserializer(LOCALE_TYPE_ADAPTER));
map.registerForTypeHierarchy(Collection.class, wrapDeserializer(COLLECTION_TYPE_ADAPTER));
map.registerForTypeHierarchy(Map.class, wrapDeserializer(MAP_TYPE_ADAPTER));
map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER)); map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER));
map.register(java.sql.Date.class, wrapDeserializer(JAVA_SQL_DATE_TYPE_ADAPTER)); map.register(java.sql.Date.class, wrapDeserializer(JAVA_SQL_DATE_TYPE_ADAPTER));
map.register(Timestamp.class, wrapDeserializer(TIMESTAMP_DESERIALIZER)); map.register(Timestamp.class, wrapDeserializer(TIMESTAMP_DESERIALIZER));
@ -187,6 +194,17 @@ final class DefaultTypeAdapters {
return map; return map;
} }
private static ParameterizedTypeHandlerMap<JsonDeserializer<?>> createDefaultHierarchyDeserializers() {
ParameterizedTypeHandlerMap<JsonDeserializer<?>> map =
new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
map.registerForTypeHierarchy(Enum.class, wrapDeserializer(ENUM_TYPE_ADAPTER));
map.registerForTypeHierarchy(InetAddress.class, wrapDeserializer(INET_ADDRESS_ADAPTER));
map.registerForTypeHierarchy(Collection.class, wrapDeserializer(COLLECTION_TYPE_ADAPTER));
map.registerForTypeHierarchy(Map.class, wrapDeserializer(MAP_TYPE_ADAPTER));
map.makeUnmodifiable();
return map;
}
private static ParameterizedTypeHandlerMap<InstanceCreator<?>> createDefaultInstanceCreators() { private static ParameterizedTypeHandlerMap<InstanceCreator<?>> createDefaultInstanceCreators() {
ParameterizedTypeHandlerMap<InstanceCreator<?>> map = ParameterizedTypeHandlerMap<InstanceCreator<?>> map =
new ParameterizedTypeHandlerMap<InstanceCreator<?>>(); new ParameterizedTypeHandlerMap<InstanceCreator<?>>();
@ -211,6 +229,20 @@ final class DefaultTypeAdapters {
return getDefaultSerializers(false, LongSerializationPolicy.DEFAULT); return getDefaultSerializers(false, LongSerializationPolicy.DEFAULT);
} }
static ParameterizedTypeHandlerMap<JsonSerializer<?>> getAllDefaultSerializers() {
ParameterizedTypeHandlerMap<JsonSerializer<?>> defaultSerializers =
getDefaultSerializers(false, LongSerializationPolicy.DEFAULT);
defaultSerializers.register(DEFAULT_HIERARCHY_SERIALIZERS);
return defaultSerializers;
}
static ParameterizedTypeHandlerMap<JsonDeserializer<?>> getAllDefaultDeserializers() {
ParameterizedTypeHandlerMap<JsonDeserializer<?>> defaultDeserializers =
getDefaultDeserializers().copyOf();
defaultDeserializers.register(DEFAULT_HIERARCHY_DESERIALIZERS);
return defaultDeserializers;
}
static ParameterizedTypeHandlerMap<JsonSerializer<?>> getDefaultSerializers( static ParameterizedTypeHandlerMap<JsonSerializer<?>> getDefaultSerializers(
boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy) { boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy) {
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers = ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers =

View File

@ -152,8 +152,8 @@ public final class Gson {
public Gson() { public Gson() {
this(DEFAULT_EXCLUSION_STRATEGY, DEFAULT_EXCLUSION_STRATEGY, DEFAULT_NAMING_POLICY, this(DEFAULT_EXCLUSION_STRATEGY, DEFAULT_EXCLUSION_STRATEGY, DEFAULT_NAMING_POLICY,
new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()), new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
false, DefaultTypeAdapters.getDefaultSerializers(), false, DefaultTypeAdapters.getAllDefaultSerializers(),
DefaultTypeAdapters.getDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE, true, false); DefaultTypeAdapters.getAllDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE, true, false);
} }
Gson(ExclusionStrategy serializationStrategy, ExclusionStrategy deserializationStrategy, Gson(ExclusionStrategy serializationStrategy, ExclusionStrategy deserializationStrategy,

View File

@ -522,8 +522,12 @@ public final class GsonBuilder {
ExclusionStrategy deserializationExclusionStrategy = ExclusionStrategy deserializationExclusionStrategy =
new DisjunctionExclusionStrategy(deserializationStrategies); new DisjunctionExclusionStrategy(deserializationStrategies);
ParameterizedTypeHandlerMap<JsonSerializer<?>> customSerializers = serializers.copyOf(); ParameterizedTypeHandlerMap<JsonSerializer<?>> customSerializers =
ParameterizedTypeHandlerMap<JsonDeserializer<?>> customDeserializers = deserializers.copyOf(); DefaultTypeAdapters.DEFAULT_HIERARCHY_SERIALIZERS.copyOf();
customSerializers.register(serializers.copyOf());
ParameterizedTypeHandlerMap<JsonDeserializer<?>> customDeserializers =
DefaultTypeAdapters.DEFAULT_HIERARCHY_DESERIALIZERS.copyOf();
customDeserializers.register(deserializers.copyOf());
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers, addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers,
customDeserializers); customDeserializers);

View File

@ -105,6 +105,21 @@ final class ParameterizedTypeHandlerMap<T> {
} }
} }
public synchronized void register(ParameterizedTypeHandlerMap<T> other) {
if (!modifiable) {
throw new IllegalStateException("Attempted to modify an unmodifiable map.");
}
for (Map.Entry<Type, T> entry : other.map.entrySet()) {
register(entry.getKey(), entry.getValue());
}
// Quite important to traverse the typeHierarchyList from stack bottom first since
// we want to register the handlers in the same order to preserve priority order
for (int i = other.typeHierarchyList.size()-1; i >= 0; --i) {
Pair<Class<?>, T> entry = other.typeHierarchyList.get(i);
registerForTypeHierarchy(entry);
}
}
public synchronized void registerIfAbsent(Type typeOfT, T value) { public synchronized void registerIfAbsent(Type typeOfT, T value) {
if (!modifiable) { if (!modifiable) {
throw new IllegalStateException("Attempted to modify an unmodifiable map."); throw new IllegalStateException("Attempted to modify an unmodifiable map.");

View File

@ -16,6 +16,14 @@
package com.google.gson.functional; package com.google.gson.functional;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
import junit.framework.TestCase;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializationContext;
@ -28,14 +36,6 @@ import com.google.gson.JsonSerializer;
import com.google.gson.common.MoreAsserts; import com.google.gson.common.MoreAsserts;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import junit.framework.TestCase;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
/** /**
* Functional tests for Java 5.0 enums. * Functional tests for Java 5.0 enums.
* *
@ -120,17 +120,6 @@ public class EnumTest extends TestCase {
* Test for issue 226. * Test for issue 226.
*/ */
public void testEnumSubclass() { public void testEnumSubclass() {
assertRoshambo();
}
public void disabled_testEnumSubclassWithRegisteredTypeAdapter() {
gson = new GsonBuilder()
// .registerTypeHierarchyAdapter(Roshambo.class, new MyEnumTypeAdapter())
.create();
assertRoshambo();
}
private void assertRoshambo() {
assertFalse(Roshambo.class == Roshambo.ROCK.getClass()); assertFalse(Roshambo.class == Roshambo.ROCK.getClass());
assertEquals("\"ROCK\"", gson.toJson(Roshambo.ROCK)); assertEquals("\"ROCK\"", gson.toJson(Roshambo.ROCK));
assertEquals("[\"ROCK\",\"PAPER\",\"SCISSORS\"]", gson.toJson(EnumSet.allOf(Roshambo.class))); assertEquals("[\"ROCK\",\"PAPER\",\"SCISSORS\"]", gson.toJson(EnumSet.allOf(Roshambo.class)));
@ -139,6 +128,18 @@ public class EnumTest extends TestCase {
gson.fromJson("[\"ROCK\",\"PAPER\",\"SCISSORS\"]", new TypeToken<Set<Roshambo>>() {}.getType())); gson.fromJson("[\"ROCK\",\"PAPER\",\"SCISSORS\"]", new TypeToken<Set<Roshambo>>() {}.getType()));
} }
public void testEnumSubclassWithRegisteredTypeAdapter() {
gson = new GsonBuilder()
.registerTypeHierarchyAdapter(Roshambo.class, new MyEnumTypeAdapter())
.create();
assertFalse(Roshambo.class == Roshambo.ROCK.getClass());
assertEquals("\"123ROCK\"", gson.toJson(Roshambo.ROCK));
assertEquals("[\"123ROCK\",\"123PAPER\",\"123SCISSORS\"]", gson.toJson(EnumSet.allOf(Roshambo.class)));
assertEquals(Roshambo.ROCK, gson.fromJson("\"123ROCK\"", Roshambo.class));
assertEquals(EnumSet.allOf(Roshambo.class),
gson.fromJson("[\"123ROCK\",\"123PAPER\",\"123SCISSORS\"]", new TypeToken<Set<Roshambo>>() {}.getType()));
}
public enum Roshambo { public enum Roshambo {
ROCK { ROCK {
@Override Roshambo defeats() { @Override Roshambo defeats() {