diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java b/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java index f709393d..fb357480 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java @@ -79,7 +79,8 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont JsonDeserializationContext context) throws JsonParseException { JsonObjectDeserializationVisitor visitor = new JsonObjectDeserializationVisitor( 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; diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java index 88132108..65378cac 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java @@ -66,15 +66,16 @@ abstract class JsonDeserializationVisitor implements ObjectNavigator.Visitor @SuppressWarnings("unchecked") public final boolean visitUsingCustomHandler(ObjectTypePair objTypePair) { - Type objType = objTypePair.getType(); - JsonDeserializer deserializer = deserializers.getHandlerFor(objType); - if (deserializer != null) { - if (!json.isJsonNull()) { - target = (T) deserializer.deserialize(json, objType, context); - } - return true; + Pair, 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 false; + return true; } final Object visitChildAsObject(Type childType, JsonElement jsonChild) { @@ -92,7 +93,7 @@ abstract class JsonDeserializationVisitor 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 diff --git a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java index 9887c487..53e89192 100644 --- a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java @@ -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 pair = objTypePair.getMatchingSerializer(serializers); + Pair,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()); diff --git a/gson/src/main/java/com/google/gson/ObjectNavigator.java b/gson/src/main/java/com/google/gson/ObjectNavigator.java index c481c7b0..6a39badc 100644 --- a/gson/src/main/java/com/google/gson/ObjectNavigator.java +++ b/gson/src/main/java/com/google/gson/ObjectNavigator.java @@ -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(); diff --git a/gson/src/main/java/com/google/gson/ObjectTypePair.java b/gson/src/main/java/com/google/gson/ObjectTypePair.java index 69542fd4..d679e1a3 100644 --- a/gson/src/main/java/com/google/gson/ObjectTypePair.java +++ b/gson/src/main/java/com/google/gson/ObjectTypePair.java @@ -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 getMatchingSerializer( - ParameterizedTypeHandlerMap> serializers) { - Preconditions.checkNotNull(obj); - JsonSerializer serializer = null; - if (!preserveType) { - // First try looking up the serializer for the actual type + Pair getMatchingHandler( + ParameterizedTypeHandlerMap 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(serializer, moreSpecificType); + handler = handlers.getHandlerFor(moreSpecificType.type); + if (handler != null) { + return new Pair(handler, moreSpecificType); } } // Try the specified type - serializer = serializers.getHandlerFor(type); - return serializer == null ? null : new Pair(serializer, this); + handler = handlers.getHandlerFor(type); + return handler == null ? null : new Pair(handler, this); } ObjectTypePair toMoreSpecificType() { @@ -122,4 +124,8 @@ final class ObjectTypePair { } return preserveType == other.preserveType; } + + public boolean isPreserveType() { + return preserveType; + } } diff --git a/gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java b/gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java new file mode 100644 index 00000000..4a179862 --- /dev/null +++ b/gson/src/test/java/com/google/gson/functional/InstanceCreatorTest.java @@ -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() { + 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() { + 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() { + 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); + } +}