From 2effd579765ad470e2cd9670ad96324d86895ed1 Mon Sep 17 00:00:00 2001 From: Joel Leitch Date: Tue, 25 Nov 2008 18:42:47 +0000 Subject: [PATCH] Deserialization will no longer call the default constructor or InstanceCreator if the object being deserialized has a custom deserializer. --- .../JsonDeserializationContextDefault.java | 3 +- .../gson/JsonDeserializationVisitor.java | 2 +- .../google/gson/JsonSerializationVisitor.java | 4 + .../java/com/google/gson/ObjectNavigator.java | 48 ++++---- .../functional/CustomDeserializerTest.java | 105 ++++++++++++++++++ 5 files changed, 138 insertions(+), 24 deletions(-) create mode 100644 gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java b/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java index bda816fb..c50b63e7 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationContextDefault.java @@ -70,8 +70,7 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont JsonDeserializationContext context) throws JsonParseException { JsonObjectDeserializationVisitor visitor = new JsonObjectDeserializationVisitor( jsonObject, typeOfT, navigatorFactory, objectConstructor, deserializers, context); - Object target = visitor.getTarget(); - ObjectNavigator on = navigatorFactory.create(target, typeOfT); + ObjectNavigator on = navigatorFactory.create(null, typeOfT); on.accept(visitor); return visitor.getTarget(); } diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java index 079927f1..96db3a77 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java @@ -52,7 +52,7 @@ abstract class JsonDeserializationVisitor implements ObjectNavigator.Visitor this.context = context; } - T getTarget() { + public T getTarget() { if (target == null) { target = constructTarget(); } diff --git a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java index 1714ac5d..37a35b62 100644 --- a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java @@ -44,6 +44,10 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor { this.serializers = serializers; this.context = context; } + + public Object getTarget() { + return null; + } public void startVisitingObject(Object node) { assignToRoot(new JsonObject()); diff --git a/gson/src/main/java/com/google/gson/ObjectNavigator.java b/gson/src/main/java/com/google/gson/ObjectNavigator.java index bc394907..2f291641 100644 --- a/gson/src/main/java/com/google/gson/ObjectNavigator.java +++ b/gson/src/main/java/com/google/gson/ObjectNavigator.java @@ -59,6 +59,11 @@ final class ObjectNavigator { * This is called to visit a field of the current object using a custom handler */ public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent); + + /** + * Retrieve the current target + */ + Object getTarget(); } private final ExclusionStrategy exclusionStrategy; @@ -88,38 +93,39 @@ final class ObjectNavigator { * If a field is null, it does not get visited. */ public void accept(Visitor visitor) { - if (obj == null) { - return; - } - TypeInfo objTypeInfo = new TypeInfo(objType); - if (exclusionStrategy.shouldSkipClass(objTypeInfo.getRawClass())) { - return; - } - - if (ancestors.contains(obj)) { - throw new IllegalStateException("Circular reference found: " + obj); - } - ancestors.push(obj); - - try { - boolean visitedWithCustomHandler = visitor.visitUsingCustomHandler(obj, objType); - if (!visitedWithCustomHandler) { + boolean visitedWithCustomHandler = visitor.visitUsingCustomHandler(obj, objType); + if (!visitedWithCustomHandler) { + Object objectToVisit = (obj == null) ? visitor.getTarget() : obj; + if (objectToVisit == null) { + return; + } + TypeInfo objTypeInfo = new TypeInfo(objType); + if (exclusionStrategy.shouldSkipClass(objTypeInfo.getRawClass())) { + return; + } + + if (ancestors.contains(objectToVisit)) { + throw new IllegalStateException("Circular reference found: " + objectToVisit); + } + ancestors.push(objectToVisit); + + try { if (objTypeInfo.isArray()) { - visitor.visitArray(obj, objType); + visitor.visitArray(objectToVisit, objType); } else { - visitor.startVisitingObject(obj); + visitor.startVisitingObject(objectToVisit); // For all classes in the inheritance hierarchy (including the current class), // visit all fields for (Class curr = objTypeInfo.getRawClass(); curr != null && !curr.equals(Object.class); curr = curr.getSuperclass()) { if (!curr.isSynthetic()) { - navigateClassFields(obj, curr, visitor); + navigateClassFields(objectToVisit, curr, visitor); } } } + } finally { + ancestors.pop(); } - } finally { - ancestors.pop(); } } diff --git a/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java b/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java new file mode 100644 index 00000000..a3f337e1 --- /dev/null +++ b/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2008 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 com.google.gson.Gson; +import com.google.gson.GsonBuilder; +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 junit.framework.TestCase; + +/** + * Functional Test exercising custom deserialization only. When test applies to both + * serialization and deserialization then add it to CustomTypeAdapterTest. + * + * @author Joel Leitch + */ +public class CustomDeserializerTest extends TestCase { + private static final String DEFAULT_VALUE = "test123"; + private static final String SUFFIX = "blah"; + + private Gson gson; + + @Override + protected void setUp() throws Exception { + super.setUp(); + gson = new GsonBuilder().registerTypeAdapter(DataHolder.class, new DataHolderDeserializer()).create(); + } + + public void testDefaultConstructorNotCalledOnObject() throws Exception { + DataHolder data = new DataHolder(DEFAULT_VALUE); + String json = gson.toJson(data); + + DataHolder actual = gson.fromJson(json, DataHolder.class); + assertEquals(DEFAULT_VALUE + SUFFIX, actual.getData()); + } + + public void testDefaultConstructorNotCalledOnField() throws Exception { + DataHolderWrapper dataWrapper = new DataHolderWrapper(new DataHolder(DEFAULT_VALUE)); + String json = gson.toJson(dataWrapper); + + DataHolderWrapper actual = gson.fromJson(json, DataHolderWrapper.class); + assertEquals(DEFAULT_VALUE + SUFFIX, actual.getWrappedData().getData()); + } + + private static class DataHolder { + private final String data; + + public DataHolder() { + throw new IllegalStateException(); + } + + public DataHolder(String data) { + this.data = data; + } + + public String getData() { + return data; + } + } + + private static class DataHolderWrapper { + private final DataHolder wrappedData; + + public DataHolderWrapper() { + this(new DataHolder(DEFAULT_VALUE)); + } + + public DataHolderWrapper(DataHolder data) { + this.wrappedData = data; + } + + public DataHolder getWrappedData() { + return wrappedData; + } + } + + private static class DataHolderDeserializer implements JsonDeserializer { + public DataHolder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObj = json.getAsJsonObject(); + String dataString = jsonObj.get("data").getAsString(); + return new DataHolder(dataString + SUFFIX); + } + } +}