Ensured that a base class custom serializer is run when the type is specified explicitly during serialization. For all other situations, ensured that the actual type of the object is taken into consideration while serializing. First a custom handler corresponding to the actual type is looked up, then a custom handler for the specified type.

Created some serialization specific tests regarding custom serializers. Revised some tests to use toJsonTree for better asserts instead of string matching.
This commit is contained in:
Inderjeet Singh 2009-10-08 19:28:53 +00:00
parent 839b0c2f94
commit b634804533
13 changed files with 356 additions and 124 deletions

View File

@ -202,7 +202,7 @@ public final class Gson {
if (src == null) {
return JsonNull.createJsonNull();
}
return toJsonTree(src, src.getClass());
return toJsonTree(src, src.getClass(), false);
}
/**
@ -222,12 +222,16 @@ public final class Gson {
* @since 1.4
*/
public JsonElement toJsonTree(Object src, Type typeOfSrc) {
return toJsonTree(src, typeOfSrc, true);
}
private JsonElement toJsonTree(Object src, Type typeOfSrc, boolean preserveType) {
if (src == null) {
return JsonNull.createJsonNull();
}
JsonSerializationContext context = new JsonSerializationContextDefault(
JsonSerializationContextDefault context = new JsonSerializationContextDefault(
createDefaultObjectNavigatorFactory(serializationStrategy), serializeNulls, serializers);
return context.serialize(src, typeOfSrc);
return context.serialize(src, typeOfSrc, preserveType);
}
/**
@ -247,7 +251,7 @@ public final class Gson {
if (src == null) {
return serializeNulls ? NULL_STRING : "";
}
return toJson(src, src.getClass());
return toJson(src, src.getClass(), false);
}
/**
@ -266,8 +270,12 @@ public final class Gson {
* @return Json representation of {@code src}
*/
public String toJson(Object src, Type typeOfSrc) {
return toJson(src, typeOfSrc, true);
}
private String toJson(Object src, Type typeOfSrc, boolean preserveType) {
StringWriter writer = new StringWriter();
toJson(src, typeOfSrc, writer);
toJson(src, typeOfSrc, writer, preserveType);
return writer.toString();
}
@ -287,7 +295,7 @@ public final class Gson {
public void toJson(Object src, Appendable writer) {
try {
if (src != null) {
toJson(src, src.getClass(), writer);
toJson(src, src.getClass(), writer, false);
} else if (serializeNulls) {
writeOutNullString(writer);
}
@ -312,7 +320,11 @@ public final class Gson {
* @since 1.2
*/
public void toJson(Object src, Type typeOfSrc, Appendable writer) {
JsonElement jsonElement = toJsonTree(src, typeOfSrc);
toJson(src, typeOfSrc, writer, true);
}
private void toJson(Object src, Type typeOfSrc, Appendable writer, boolean preserveType) {
JsonElement jsonElement = toJsonTree(src, typeOfSrc, preserveType);
toJson(jsonElement, writer);
}

View File

@ -60,7 +60,7 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont
JsonDeserializationContext context) throws JsonParseException {
JsonArrayDeserializationVisitor<T> visitor = new JsonArrayDeserializationVisitor<T>(
jsonArray, arrayType, navigatorFactory, objectConstructor, deserializers, context);
ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(null, arrayType));
ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(null, arrayType, true));
on.accept(visitor);
return visitor.getTarget();
}
@ -69,7 +69,7 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont
JsonDeserializationContext context) throws JsonParseException {
JsonObjectDeserializationVisitor<T> visitor = new JsonObjectDeserializationVisitor<T>(
jsonObject, typeOfT, navigatorFactory, objectConstructor, deserializers, context);
ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(null, typeOfT));
ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(null, typeOfT, true));
on.accept(visitor);
return visitor.getTarget();
}
@ -79,7 +79,7 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont
JsonDeserializationContext context) throws JsonParseException {
JsonObjectDeserializationVisitor<T> visitor = new JsonObjectDeserializationVisitor<T>(
json, typeOfT, navigatorFactory, objectConstructor, deserializers, context);
ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(json.getAsObject(), typeOfT));
ObjectNavigator on = navigatorFactory.create(new ObjectTypePair(json.getAsObject(), typeOfT, true));
on.accept(visitor);
Object target = visitor.getTarget();
return (T) target;

View File

@ -92,10 +92,14 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
}
private Object visitChild(Type type, JsonDeserializationVisitor<?> childVisitor) {
ObjectNavigator on = factory.create(new ObjectTypePair(null, type));
ObjectNavigator on = factory.create(new ObjectTypePair(null, type, true));
on.accept(childVisitor);
// the underlying object may have changed during the construction phase
// This happens primarily because of custom deserializers
return childVisitor.getTarget();
}
public ObjectTypePair getActualTypeIfMoreSpecific(ObjectTypePair objTypePair) {
return objTypePair;
}
}

View File

@ -42,11 +42,15 @@ final class JsonSerializationContextDefault implements JsonSerializationContext
if (src == null) {
return JsonNull.createJsonNull();
}
return serialize(src, src.getClass());
return serialize(src, src.getClass(), false);
}
public JsonElement serialize(Object src, Type typeOfSrc) {
ObjectNavigator on = factory.create(new ObjectTypePair(src, typeOfSrc));
return serialize(src, typeOfSrc, true);
}
public JsonElement serialize(Object src, Type typeOfSrc, boolean preserveType) {
ObjectNavigator on = factory.create(new ObjectTypePair(src, typeOfSrc, preserveType));
JsonSerializationVisitor visitor =
new JsonSerializationVisitor(factory, serializeNulls, serializers, this, ancestors);
on.accept(visitor);

View File

@ -22,7 +22,7 @@ import java.lang.reflect.Type;
/**
* A visitor that adds JSON elements corresponding to each field of an object
*
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
@ -36,15 +36,15 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
private JsonElement root;
JsonSerializationVisitor(ObjectNavigatorFactory factory, boolean serializeNulls,
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
JsonSerializationContext context, MemoryRefStack ancestors) {
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers, JsonSerializationContext context,
MemoryRefStack ancestors) {
this.factory = factory;
this.serializeNulls = serializeNulls;
this.serializers = serializers;
this.context = context;
this.ancestors = ancestors;
}
public Object getTarget() {
return null;
}
@ -77,10 +77,10 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
for (int i = 0; i < length; ++i) {
Object child = Array.get(array, i);
Type childType = componentType;
if (child != null) {
childType = getActualTypeIfMoreSpecific(childType, child.getClass());
}
addAsArrayElement(new ObjectTypePair(child, childType));
// we should not get more specific component type yet since it is possible
// that a custom
// serializer is registered for the componentType
addAsArrayElement(new ObjectTypePair(child, childType, false));
}
}
@ -92,7 +92,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
}
} else {
Object array = getFieldValue(f, obj);
addAsChildOfObject(f, new ObjectTypePair(array, typeOfF));
addAsChildOfObject(f, new ObjectTypePair(array, typeOfF, false));
}
} catch (CircularReferenceException e) {
throw e.createDetailedException(f);
@ -107,33 +107,16 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
}
} else {
Object fieldValue = getFieldValue(f, obj);
if (fieldValue != null) {
typeOfF = getActualTypeIfMoreSpecific(typeOfF, fieldValue.getClass());
}
addAsChildOfObject(f, new ObjectTypePair(fieldValue, typeOfF));
// we should not get more specific component type yet since it is
// possible that a custom
// serializer is registered for the componentType
addAsChildOfObject(f, new ObjectTypePair(fieldValue, typeOfF, false));
}
} catch (CircularReferenceException e) {
throw e.createDetailedException(f);
}
}
// This takes care of situations where the field was declared as an Object, but the
// actual value contains something more specific. See Issue 54.
// TODO (inder): This solution will not work if the field is of a generic type, but
// the actual object is of a raw type (which is a sub-class of the generic type).
private Type getActualTypeIfMoreSpecific(Type type, Class<?> actualClass) {
if (type instanceof Class<?>) {
Class<?> typeAsClass = (Class<?>) type;
if (typeAsClass.isAssignableFrom(actualClass)) {
type = actualClass;
}
if (type == Object.class) {
type = actualClass;
}
}
return type;
}
public void visitPrimitive(Object obj) {
JsonElement json = obj == null ? JsonNull.createJsonNull() : new JsonPrimitive(obj);
assignToRoot(json);
@ -170,28 +153,35 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
public boolean visitUsingCustomHandler(ObjectTypePair objTypePair) {
try {
Object obj = objTypePair.getObject();
Type objType = objTypePair.getType();
JsonSerializer serializer = serializers.getHandlerFor(objType);
if (serializer == null && obj != null) {
serializer = serializers.getHandlerFor(obj.getClass());
}
if (serializer != null) {
if (obj == null) {
if (obj == null) {
if (serializeNulls) {
assignToRoot(JsonNull.createJsonNull());
} else {
assignToRoot(invokeCustomHandler(objTypePair, serializer));
}
return true;
}
return false;
JsonElement element = findAndInvokeCustomSerializer(objTypePair);
if (element != null) {
assignToRoot(element);
return true;
} else {
return false;
}
} catch (CircularReferenceException e) {
throw e.createDetailedException(null);
}
}
/**
* objTypePair.getObject() must not be null
*/
@SuppressWarnings("unchecked")
private JsonElement invokeCustomHandler(ObjectTypePair objTypePair, JsonSerializer serializer) {
private JsonElement findAndInvokeCustomSerializer(ObjectTypePair objTypePair) {
Pair<JsonSerializer, ObjectTypePair> pair = objTypePair.getMatchingSerializer(serializers);
if (pair == null) {
return null;
}
JsonSerializer serializer = pair.getFirst();
objTypePair = pair.getSecond();
start(objTypePair);
try {
return serializer.serialize(objTypePair.getObject(), objTypePair.getType(), context);
@ -201,7 +191,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
}
@SuppressWarnings("unchecked")
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent) {
public boolean visitFieldUsingCustomHandler(Field f, Type declaredTypeOfField, Object parent) {
try {
Preconditions.checkState(root.isJsonObject());
Object obj = f.get(parent);
@ -211,14 +201,14 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
}
return true;
}
JsonSerializer serializer = serializers.getHandlerFor(actualTypeOfField);
if (serializer != null) {
ObjectTypePair objTypePair = new ObjectTypePair(obj, actualTypeOfField);
JsonElement child = invokeCustomHandler(objTypePair, serializer);
ObjectTypePair objTypePair = new ObjectTypePair(obj, declaredTypeOfField, false);
JsonElement child = findAndInvokeCustomSerializer(objTypePair);
if (child != null) {
addChildAsElement(f, child);
return true;
} else {
return false;
}
return false;
} catch (IllegalAccessException e) {
throw new RuntimeException();
} catch (CircularReferenceException e) {
@ -246,4 +236,8 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
public JsonElement getJsonElement() {
return root;
}
public ObjectTypePair getActualTypeIfMoreSpecific(ObjectTypePair objTypePair) {
return objTypePair.toMoreSpecificType();
}
}

View File

@ -63,6 +63,8 @@ final class ObjectNavigator {
*/
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent);
public ObjectTypePair getActualTypeIfMoreSpecific(ObjectTypePair objTypePair);
/**
* Retrieve the current target
*/
@ -114,10 +116,8 @@ final class ObjectNavigator {
objectToVisit = visitor.getTarget();
} else {
visitor.startVisitingObject(objectToVisit);
// For all classes in the inheritance hierarchy (including the current class),
// visit all fields
Class<?> topLevelClass = (objTypeInfo.getRawClass() == Object.class)
? objectToVisit.getClass() : objTypeInfo.getRawClass();
ObjectTypePair currObjTypePair = visitor.getActualTypeIfMoreSpecific(objTypePair);
Class<?> topLevelClass = new TypeInfo(currObjTypePair.getType()).getRawClass();
for (Class<?> curr = topLevelClass; curr != null && !curr.equals(Object.class);
curr = curr.getSuperclass()) {
if (!curr.isSynthetic()) {
@ -145,14 +145,14 @@ final class ObjectNavigator {
continue; // skip
} else {
TypeInfo fieldTypeInfo = TypeInfoFactory.getTypeInfoForField(f, objTypePair.getType());
Type actualTypeOfField = fieldTypeInfo.getActualType();
Type declaredTypeOfField = fieldTypeInfo.getActualType();
boolean visitedWithCustomHandler =
visitor.visitFieldUsingCustomHandler(f, actualTypeOfField, obj);
visitor.visitFieldUsingCustomHandler(f, declaredTypeOfField, obj);
if (!visitedWithCustomHandler) {
if (fieldTypeInfo.isArray()) {
visitor.visitArrayField(f, actualTypeOfField, obj);
visitor.visitArrayField(f, declaredTypeOfField, obj);
} else {
visitor.visitObjectField(f, actualTypeOfField, obj);
visitor.visitObjectField(f, declaredTypeOfField, obj);
}
}
}

View File

@ -27,17 +27,67 @@ final class ObjectTypePair {
private final Object obj;
private final Type type;
private final boolean preserveType;
public ObjectTypePair(Object obj, Type type) {
ObjectTypePair(Object obj, Type type, boolean preserveType) {
this.obj = obj;
this.type = type;
this.preserveType = preserveType;
}
public Object getObject() {
Object getObject() {
return obj;
}
public Type getType() {
Type getType() {
return type;
}
@SuppressWarnings("unchecked")
Pair<JsonSerializer, ObjectTypePair> getMatchingSerializer(
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers) {
if (obj == null) {
return null;
}
JsonSerializer serializer = null;
if (!preserveType) {
// First try looking up the serializer for the actual type
ObjectTypePair moreSpecificType = toMoreSpecificType();
serializer = serializers.getHandlerFor(moreSpecificType.type);
if (serializer != null) {
return new Pair<JsonSerializer, ObjectTypePair>(serializer, moreSpecificType);
}
}
// Try the specified type
serializer = serializers.getHandlerFor(type);
return serializer == null ? null : new Pair<JsonSerializer, ObjectTypePair>(serializer, this);
}
ObjectTypePair toMoreSpecificType() {
if (preserveType || obj == null) {
return this;
}
Type actualType = getActualTypeIfMoreSpecific(type, obj.getClass());
if (actualType == type) {
return this;
}
return new ObjectTypePair(obj, actualType, preserveType);
}
// This takes care of situations where the field was declared as an Object, but the
// actual value contains something more specific. See Issue 54.
// TODO (inder): This solution will not work if the field is of a generic type, but
// the actual object is of a raw type (which is a sub-class of the generic type).
static Type getActualTypeIfMoreSpecific(Type type, Class<?> actualClass) {
if (type instanceof Class<?>) {
Class<?> typeAsClass = (Class<?>) type;
if (typeAsClass.isAssignableFrom(actualClass)) {
type = actualClass;
}
if (type == Object.class) {
type = actualClass;
}
}
return type;
}
@ -74,6 +124,6 @@ final class ObjectTypePair {
} else if (!type.equals(other.type)) {
return false;
}
return true;
return preserveType == other.preserveType;
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson;
final class Pair<FIRST, SECOND> {
private final FIRST first;
private final SECOND second;
Pair(FIRST first, SECOND second) {
this.first = first;
this.second = second;
}
public FIRST getFirst() {
return first;
}
public SECOND getSecond() {
return second;
}
}

View File

@ -43,7 +43,7 @@ public class MemoryRefStackTest extends TestCase {
}
public void testPushPeekAndPop() throws Exception {
ObjectTypePair obj = new ObjectTypePair(this, getClass());
ObjectTypePair obj = new ObjectTypePair(this, getClass(), true);
assertEquals(obj, stack.push(obj));
assertEquals(obj, stack.peek());
@ -51,7 +51,7 @@ public class MemoryRefStackTest extends TestCase {
}
public void testPopTooMany() throws Exception {
ObjectTypePair obj = new ObjectTypePair(this, getClass());
ObjectTypePair obj = new ObjectTypePair(this, getClass(), true);
stack.push(obj);
assertEquals(obj, stack.pop());
@ -64,9 +64,9 @@ public class MemoryRefStackTest extends TestCase {
MockObject objA = new MockObject();
MockObject objB = new MockObject();
assertEquals(objA, objB);
stack.push(new ObjectTypePair(objA, MockObject.class));
assertTrue(stack.contains(new ObjectTypePair(objA, MockObject.class)));
assertFalse(stack.contains(new ObjectTypePair(objB, MockObject.class)));
stack.push(new ObjectTypePair(objA, MockObject.class, true));
assertTrue(stack.contains(new ObjectTypePair(objA, MockObject.class, true)));
assertFalse(stack.contains(new ObjectTypePair(objB, MockObject.class, true)));
}
private static class MockObject {

View File

@ -16,17 +16,18 @@
package com.google.gson.common;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
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
*
@ -34,34 +35,62 @@ import com.google.gson.annotations.SerializedName;
* @author Joel Leitch
*/
public class TestTypes {
public static class Base {
public static final String BASE_NAME = Base.class.getSimpleName();
public static final String BASE_FIELD_KEY = "baseName";
public static final String SERIALIZER_KEY = "serializerName";
public String baseName = BASE_NAME;
public String serializerName;
}
public static class BaseClass {
final String baseField;
public BaseClass() {
this("baseFieldValue");
public static class Sub extends Base {
public static final String SUB_NAME = Sub.class.getSimpleName();
public static final String SUB_FIELD_KEY = "subName";
public final String subName = SUB_NAME;
}
public static class ClassWithBaseField {
public static final String FIELD_KEY = "base";
public final Base base;
@SuppressWarnings("unused")
private ClassWithBaseField() {
this(null);
}
public BaseClass(String value) {
this.baseField = value;
}
public String getExpectedJson() {
return String.format("{\"baseField\":\"%s\"}", baseField);
public ClassWithBaseField(Base base) {
this.base = base;
}
}
public static class SubClass extends BaseClass {
final String subField;
public SubClass() {
this("subFieldValue");
public static class ClassWithBaseArrayField {
public static final String FIELD_KEY = "base";
public final Base[] base;
@SuppressWarnings("unused")
private ClassWithBaseArrayField() {
this(null);
}
public SubClass(String subFieldValue) {
this.subField = subFieldValue;
}
@Override
public String getExpectedJson() {
return String.format("{\"subField\":\"%s\",\"baseField\":\"%s\"}", subField, baseField);
public ClassWithBaseArrayField(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) {
JsonObject obj = new JsonObject();
obj.addProperty(Base.SERIALIZER_KEY, NAME);
return obj;
}
}
public static class SubSerializer implements JsonSerializer<Sub> {
public static final String NAME = SubSerializer.class.getSimpleName();
public JsonElement serialize(Sub src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.addProperty(Base.SERIALIZER_KEY, NAME);
return obj;
}
}
public static class StringWrapper {
public final String someConstantStringInstanceField;

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.functional;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.common.TestTypes.Base;
import com.google.gson.common.TestTypes.BaseSerializer;
import com.google.gson.common.TestTypes.ClassWithBaseArrayField;
import com.google.gson.common.TestTypes.ClassWithBaseField;
import com.google.gson.common.TestTypes.Sub;
import com.google.gson.common.TestTypes.SubSerializer;
import junit.framework.TestCase;
/**
* Functional Test exercising custom serialization only. When test applies to both
* serialization and deserialization then add it to CustomTypeAdapterTest.
*
* @author Inderjeet Singh
*/
public class CustomSerializerTest extends TestCase {
public void testBaseClassSerializerInvokedForBaseClassFields() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new BaseSerializer())
.registerTypeAdapter(Sub.class, new SubSerializer())
.create();
ClassWithBaseField target = new ClassWithBaseField(new Base());
JsonObject json = (JsonObject) gson.toJsonTree(target);
JsonObject base = json.get("base").getAsJsonObject();
assertEquals(BaseSerializer.NAME, base.get(Base.SERIALIZER_KEY).getAsString());
}
public void testSubClassSerializerInvokedForBaseClassFieldsHoldingSubClassInstances() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new BaseSerializer())
.registerTypeAdapter(Sub.class, new SubSerializer())
.create();
ClassWithBaseField target = new ClassWithBaseField(new Sub());
JsonObject json = (JsonObject) gson.toJsonTree(target);
JsonObject base = json.get("base").getAsJsonObject();
assertEquals(SubSerializer.NAME, base.get(Base.SERIALIZER_KEY).getAsString());
}
public void testSubClassSerializerInvokedForBaseClassFieldsHoldingArrayOfSubClassInstances() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new BaseSerializer())
.registerTypeAdapter(Sub.class, new SubSerializer())
.create();
ClassWithBaseArrayField target = new ClassWithBaseArrayField(new Base[] {new Sub(), new Sub()});
JsonObject json = (JsonObject) gson.toJsonTree(target);
JsonArray array = json.get("base").getAsJsonArray();
for (JsonElement element : array) {
JsonElement serializerKey = element.getAsJsonObject().get(Base.SERIALIZER_KEY);
assertEquals(SubSerializer.NAME, serializerKey.getAsString());
}
}
public void testBaseClassSerializerInvokedForBaseClassFieldsHoldingSubClassInstances() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new BaseSerializer())
.create();
ClassWithBaseField target = new ClassWithBaseField(new Sub());
JsonObject json = (JsonObject) gson.toJsonTree(target);
JsonObject base = json.get("base").getAsJsonObject();
assertEquals(BaseSerializer.NAME, base.get(Base.SERIALIZER_KEY).getAsString());
}
}

View File

@ -473,5 +473,4 @@ public class CustomTypeAdaptersTest extends TestCase {
return new DataHolder(jsonElement.getAsString());
}
}
}

View File

@ -15,6 +15,19 @@
*/
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.Collection;
import java.util.LinkedList;
import java.util.List;
@ -23,14 +36,6 @@ 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.common.TestTypes.BagOfPrimitives;
import com.google.gson.common.TestTypes.BaseClass;
import com.google.gson.common.TestTypes.Nested;
import com.google.gson.common.TestTypes.SubClass;
/**
* Functional tests for Json serialization and deserialization of classes with
* inheritance hierarchies.
@ -62,26 +67,40 @@ public class InheritanceTest extends TestCase {
assertEquals(json, target.getExpectedJson());
}
public void testClassWithBaseFieldSerialization() throws Exception {
ClassWithBaseFields sub = new ClassWithBaseFields();
sub.field = new SubClass();
String json = gson.toJson(sub);
String expectedJson = sub.field.getExpectedJson();
assertTrue(json.contains(expectedJson));
public void testClassWithBaseFieldSerialization() {
ClassWithBaseField sub = new ClassWithBaseField(new Sub());
JsonObject json = (JsonObject) gson.toJsonTree(sub);
JsonElement base = json.getAsJsonObject().get(ClassWithBaseField.FIELD_KEY);
assertEquals(Sub.SUB_NAME, base.getAsJsonObject().get(Sub.SUB_FIELD_KEY).getAsString());
}
public void testClassWithBaseArrayFieldSerialization() throws Exception {
ClassWithBaseFields sub = new ClassWithBaseFields();
sub.array = new BaseClass[]{ new SubClass("sub1"), new SubClass("sub2")};
String json = gson.toJson(sub);
assertTrue(json.contains("sub1"));
assertTrue(json.contains("sub2"));
public void testClassWithBaseArrayFieldSerialization() {
Base[] baseClasses = new Base[]{ new Sub(), new Sub()};
ClassWithBaseArrayField sub = new ClassWithBaseArrayField(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());
}
}
private static class ClassWithBaseFields {
BaseClass field;
@SuppressWarnings("unused")
BaseClass[] array;
public void testBaseSerializedAsSub() {
Base base = new Sub();
JsonObject json = gson.toJsonTree(base).getAsJsonObject();
assertEquals(Sub.SUB_NAME, json.get(Sub.SUB_FIELD_KEY).getAsString());
}
public void testBaseSerializedAsBaseWhenSpecifiedWithExplicitType() {
Base base = new Sub();
JsonObject json = gson.toJsonTree(base, Base.class).getAsJsonObject();
assertEquals(Base.BASE_NAME, json.get(Base.BASE_FIELD_KEY).getAsString());
assertNull(json.get(Sub.SUB_FIELD_KEY));
}
public void testBaseSerializedAsSubWhenSpecifiedWithExplicitType() {
Base base = new Sub();
JsonObject json = gson.toJsonTree(base, Sub.class).getAsJsonObject();
assertEquals(Sub.SUB_NAME, json.get(Sub.SUB_FIELD_KEY).getAsString());
}
private static class SubTypeOfNested extends Nested {