Added support for runtime type determination while serializing collections and its subclasses.

This currently doesn't work since Gson register a hierarchy type adapter for Collections that takes precedence over this.
This commit is contained in:
Inderjeet Singh 2011-08-04 23:55:52 +00:00
parent e04d3fa882
commit 64dc53ffc4
3 changed files with 48 additions and 22 deletions

View File

@ -18,7 +18,6 @@ package com.google.gson.internal.bind;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
@ -39,9 +38,6 @@ public final class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
public static final Factory FACTORY = new Factory() {
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (!(type instanceof ParameterizedType)) {
return null;
}
Class<? super T> rawType = typeToken.getRawType();
if (!Collection.class.isAssignableFrom(rawType)) {
@ -69,16 +65,20 @@ public final class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
}
@SuppressWarnings("unchecked") // create() doesn't define a type parameter
TypeAdapter<T> result = new CollectionTypeAdapter(elementTypeAdapter, constructor);
TypeAdapter<T> result = new CollectionTypeAdapter(context, elementType, elementTypeAdapter, constructor);
return result;
}
};
private final MiniGson context;
private final Type elementType;
private final TypeAdapter<E> elementTypeAdapter;
private final Constructor<? extends Collection<E>> constructor;
public CollectionTypeAdapter(TypeAdapter<E> elementTypeAdapter,
public CollectionTypeAdapter(MiniGson context, Type elementType, TypeAdapter<E> elementTypeAdapter,
Constructor<? extends Collection<E>> constructor) {
this.context = context;
this.elementType = elementType;
this.elementTypeAdapter = elementTypeAdapter;
this.constructor = constructor;
}
@ -107,7 +107,10 @@ public final class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
writer.beginArray();
for (E element : collection) {
elementTypeAdapter.write(writer, element);
Type runtimeType = Reflection.getRuntimeTypeIfMoreSpecific(elementType, collection, element);
TypeAdapter t = runtimeType != elementType ?
context.getAdapter(TypeToken.get(runtimeType)) : elementTypeAdapter;
t.write(writer, element);
}
writer.endArray();
}

View File

@ -16,6 +16,9 @@
package com.google.gson.common;
import java.lang.reflect.Type;
import java.util.Collection;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
@ -26,8 +29,6 @@ import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.annotations.SerializedName;
import java.lang.reflect.Type;
/**
* Types used for testing JSON serialization and deserialization
*
@ -66,6 +67,14 @@ public class TestTypes {
}
}
public static class ClassWithBaseCollectionField {
public static final String FIELD_KEY = "base";
public final Collection<Base> base;
public ClassWithBaseCollectionField(Collection<Base> base) {
this.base = base;
}
}
public static class BaseSerializer implements JsonSerializer<Base> {
public static final String NAME = BaseSerializer.class.getSimpleName();
public JsonElement serialize(Base src, Type typeOfSrc, JsonSerializationContext context) {

View File

@ -15,19 +15,7 @@
*/
package com.google.gson.functional;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.common.TestTypes.BagOfPrimitives;
import com.google.gson.common.TestTypes.Base;
import com.google.gson.common.TestTypes.ClassWithBaseArrayField;
import com.google.gson.common.TestTypes.ClassWithBaseField;
import com.google.gson.common.TestTypes.Nested;
import com.google.gson.common.TestTypes.Sub;
import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@ -36,6 +24,20 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import junit.framework.TestCase;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.common.TestTypes.BagOfPrimitives;
import com.google.gson.common.TestTypes.Base;
import com.google.gson.common.TestTypes.ClassWithBaseArrayField;
import com.google.gson.common.TestTypes.ClassWithBaseCollectionField;
import com.google.gson.common.TestTypes.ClassWithBaseField;
import com.google.gson.common.TestTypes.Nested;
import com.google.gson.common.TestTypes.Sub;
/**
* Functional tests for Json serialization and deserialization of classes with
* inheritance hierarchies.
@ -84,6 +86,18 @@ public class InheritanceTest extends TestCase {
}
}
public void testClassWithBaseCollectionFieldSerialization() {
Collection<Base> baseClasses = new ArrayList<Base>();
baseClasses.add(new Sub());
baseClasses.add(new Sub());
ClassWithBaseCollectionField sub = new ClassWithBaseCollectionField(baseClasses);
JsonObject json = gson.toJsonTree(sub).getAsJsonObject();
JsonArray bases = json.get(ClassWithBaseArrayField.FIELD_KEY).getAsJsonArray();
for (JsonElement element : bases) {
assertEquals(Sub.SUB_NAME, element.getAsJsonObject().get(Sub.SUB_FIELD_KEY).getAsString());
}
}
public void testBaseSerializedAsSub() {
Base base = new Sub();
JsonObject json = gson.toJsonTree(base).getAsJsonObject();