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:
parent
839b0c2f94
commit
b634804533
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -36,8 +36,8 @@ 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;
|
||||
@ -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 (serializeNulls) {
|
||||
assignToRoot(JsonNull.createJsonNull());
|
||||
} else {
|
||||
assignToRoot(invokeCustomHandler(objTypePair, serializer));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
35
gson/src/main/java/com/google/gson/Pair.java
Normal file
35
gson/src/main/java/com/google/gson/Pair.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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
|
||||
*
|
||||
@ -35,30 +36,58 @@ import com.google.gson.annotations.SerializedName;
|
||||
*/
|
||||
public class TestTypes {
|
||||
|
||||
public static class BaseClass {
|
||||
final String baseField;
|
||||
public BaseClass() {
|
||||
this("baseFieldValue");
|
||||
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 BaseClass(String value) {
|
||||
this.baseField = value;
|
||||
|
||||
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 String getExpectedJson() {
|
||||
return String.format("{\"baseField\":\"%s\"}", baseField);
|
||||
|
||||
public static class ClassWithBaseField {
|
||||
public static final String FIELD_KEY = "base";
|
||||
public final Base base;
|
||||
@SuppressWarnings("unused")
|
||||
private ClassWithBaseField() {
|
||||
this(null);
|
||||
}
|
||||
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;
|
||||
public ClassWithBaseArrayField(Base[] base) {
|
||||
this.base = base;
|
||||
}
|
||||
@Override
|
||||
public String getExpectedJson() {
|
||||
return String.format("{\"subField\":\"%s\",\"baseField\":\"%s\"}", subField, baseField);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -473,5 +473,4 @@ public class CustomTypeAdaptersTest extends TestCase {
|
||||
return new DataHolder(jsonElement.getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user