implemented support for deserialization to deserialize to more specific type if an instance creator returns a sub-class.

This commit is contained in:
Inderjeet Singh 2009-10-09 18:16:16 +00:00
parent fffb204cee
commit 338758a0d3
6 changed files with 111 additions and 28 deletions

View File

@ -79,7 +79,8 @@ 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, true));
ObjectNavigator on =
navigatorFactory.create(new ObjectTypePair(json.getAsObject(), typeOfT, true));
on.accept(visitor);
Object target = visitor.getTarget();
return (T) target;

View File

@ -66,16 +66,17 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
@SuppressWarnings("unchecked")
public final boolean visitUsingCustomHandler(ObjectTypePair objTypePair) {
Type objType = objTypePair.getType();
JsonDeserializer deserializer = deserializers.getHandlerFor(objType);
if (deserializer != null) {
Pair<JsonDeserializer<?>, ObjectTypePair> pair = objTypePair.getMatchingHandler(deserializers);
if (pair == null) {
return false;
}
if (!json.isJsonNull()) {
JsonDeserializer deserializer = pair.getFirst();
Type objType = pair.getSecond().getType();
target = (T) deserializer.deserialize(json, objType, context);
}
return true;
}
return false;
}
final Object visitChildAsObject(Type childType, JsonElement jsonChild) {
JsonDeserializationVisitor<?> childVisitor =
@ -92,7 +93,7 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
}
private Object visitChild(Type type, JsonDeserializationVisitor<?> childVisitor) {
ObjectNavigator on = factory.create(new ObjectTypePair(null, type, true));
ObjectNavigator on = factory.create(new ObjectTypePair(null, type, false));
on.accept(childVisitor);
// the underlying object may have changed during the construction phase
// This happens primarily because of custom deserializers

View File

@ -149,7 +149,6 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
return childVisitor.getJsonElement();
}
@SuppressWarnings("unchecked")
public boolean visitUsingCustomHandler(ObjectTypePair objTypePair) {
try {
Object obj = objTypePair.getObject();
@ -176,7 +175,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
*/
@SuppressWarnings("unchecked")
private JsonElement findAndInvokeCustomSerializer(ObjectTypePair objTypePair) {
Pair<JsonSerializer, ObjectTypePair> pair = objTypePair.getMatchingSerializer(serializers);
Pair<JsonSerializer<?>,ObjectTypePair> pair = objTypePair.getMatchingHandler(serializers);
if (pair == null) {
return null;
}
@ -192,7 +191,6 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
}
}
@SuppressWarnings("unchecked")
public boolean visitFieldUsingCustomHandler(Field f, Type declaredTypeOfField, Object parent) {
try {
Preconditions.checkState(root.isJsonObject());

View File

@ -102,14 +102,14 @@ final class ObjectNavigator {
if (objectToVisit == null) {
return;
}
objTypePair.setObject(objectToVisit);
visitor.start(objTypePair);
try {
if (objTypeInfo.isArray()) {
visitor.visitArray(objectToVisit, objTypePair.getType());
} else if (objTypeInfo.getActualType() == Object.class
&& isPrimitiveOrString(objectToVisit)) {
// TODO(Joel): this is only used for deserialization of "primitves"
// TODO(Joel): this is only used for deserialization of "primitives"
// we should rethink this!!!
visitor.visitPrimitive(objectToVisit);
objectToVisit = visitor.getTarget();

View File

@ -23,7 +23,7 @@ import java.lang.reflect.Type;
* @author Inderjeet Singh
*/
final class ObjectTypePair {
private final Object obj;
private Object obj;
private final Type type;
private final boolean preserveType;
@ -37,26 +37,28 @@ final class ObjectTypePair {
return obj;
}
void setObject(Object obj) {
this.obj = obj;
}
Type getType() {
return type;
}
@SuppressWarnings("unchecked")
Pair<JsonSerializer, ObjectTypePair> getMatchingSerializer(
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers) {
Preconditions.checkNotNull(obj);
JsonSerializer serializer = null;
if (!preserveType) {
// First try looking up the serializer for the actual type
<HANDLER> Pair<HANDLER, ObjectTypePair> getMatchingHandler(
ParameterizedTypeHandlerMap<HANDLER> handlers) {
HANDLER handler = null;
if (!preserveType && obj != null) {
// First try looking up the handler for the actual type
ObjectTypePair moreSpecificType = toMoreSpecificType();
serializer = serializers.getHandlerFor(moreSpecificType.type);
if (serializer != null) {
return new Pair<JsonSerializer, ObjectTypePair>(serializer, moreSpecificType);
handler = handlers.getHandlerFor(moreSpecificType.type);
if (handler != null) {
return new Pair<HANDLER, ObjectTypePair>(handler, moreSpecificType);
}
}
// Try the specified type
serializer = serializers.getHandlerFor(type);
return serializer == null ? null : new Pair<JsonSerializer, ObjectTypePair>(serializer, this);
handler = handlers.getHandlerFor(type);
return handler == null ? null : new Pair<HANDLER, ObjectTypePair>(handler, this);
}
ObjectTypePair toMoreSpecificType() {
@ -122,4 +124,8 @@ final class ObjectTypePair {
}
return preserveType == other.preserveType;
}
public boolean isPreserveType() {
return preserveType;
}
}

View File

@ -0,0 +1,77 @@
/*
* 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 java.lang.reflect.Type;
import junit.framework.TestCase;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.common.TestTypes.Base;
import com.google.gson.common.TestTypes.ClassWithBaseField;
import com.google.gson.common.TestTypes.Sub;
/**
* Functional Test exercising custom serialization only. When test applies to both
* serialization and deserialization then add it to CustomTypeAdapterTest.
*
* @author Inderjeet Singh
*/
public class InstanceCreatorTest extends TestCase {
public void testInstanceCreatorReturnsBaseType() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new InstanceCreator<Base>() {
public Base createInstance(Type type) {
return new Base();
}
})
.create();
String json = "{baseName:'BaseRevised',subName:'Sub'}";
Base base = gson.fromJson(json, Base.class);
assertEquals("BaseRevised", base.baseName);
}
public void testInstanceCreatorReturnsSubTypeForTopLevelObject() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new InstanceCreator<Base>() {
public Base createInstance(Type type) {
return new Sub();
}
})
.create();
String json = "{baseName:'Base',subName:'SubRevised'}";
Base base = gson.fromJson(json, Base.class);
assertFalse("SubRevised".equals(((Sub)base).subName));
}
public void testInstanceCreatorReturnsSubTypeForField() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new InstanceCreator<Base>() {
public Base createInstance(Type type) {
return new Sub();
}
})
.create();
String json = "{base:{baseName:'Base',subName:'SubRevised'}}";
ClassWithBaseField target = gson.fromJson(json, ClassWithBaseField.class);
assertTrue(target.base instanceof Sub);
assertEquals("SubRevised", ((Sub)target.base).subName);
}
}