Deserialization will no longer call the default constructor or InstanceCreator if the object being deserialized has a custom deserializer.

This commit is contained in:
Joel Leitch 2008-11-25 18:42:47 +00:00
parent 1e7f3ebe7a
commit 2effd57976
5 changed files with 138 additions and 24 deletions

View File

@ -70,8 +70,7 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont
JsonDeserializationContext context) throws JsonParseException { JsonDeserializationContext context) throws JsonParseException {
JsonObjectDeserializationVisitor<T> visitor = new JsonObjectDeserializationVisitor<T>( JsonObjectDeserializationVisitor<T> visitor = new JsonObjectDeserializationVisitor<T>(
jsonObject, typeOfT, navigatorFactory, objectConstructor, deserializers, context); jsonObject, typeOfT, navigatorFactory, objectConstructor, deserializers, context);
Object target = visitor.getTarget(); ObjectNavigator on = navigatorFactory.create(null, typeOfT);
ObjectNavigator on = navigatorFactory.create(target, typeOfT);
on.accept(visitor); on.accept(visitor);
return visitor.getTarget(); return visitor.getTarget();
} }

View File

@ -52,7 +52,7 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
this.context = context; this.context = context;
} }
T getTarget() { public T getTarget() {
if (target == null) { if (target == null) {
target = constructTarget(); target = constructTarget();
} }

View File

@ -44,6 +44,10 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
this.serializers = serializers; this.serializers = serializers;
this.context = context; this.context = context;
} }
public Object getTarget() {
return null;
}
public void startVisitingObject(Object node) { public void startVisitingObject(Object node) {
assignToRoot(new JsonObject()); assignToRoot(new JsonObject());

View File

@ -59,6 +59,11 @@ final class ObjectNavigator {
* This is called to visit a field of the current object using a custom handler * This is called to visit a field of the current object using a custom handler
*/ */
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent); public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent);
/**
* Retrieve the current target
*/
Object getTarget();
} }
private final ExclusionStrategy exclusionStrategy; private final ExclusionStrategy exclusionStrategy;
@ -88,38 +93,39 @@ final class ObjectNavigator {
* If a field is null, it does not get visited. * If a field is null, it does not get visited.
*/ */
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
if (obj == null) { boolean visitedWithCustomHandler = visitor.visitUsingCustomHandler(obj, objType);
return; if (!visitedWithCustomHandler) {
} Object objectToVisit = (obj == null) ? visitor.getTarget() : obj;
TypeInfo objTypeInfo = new TypeInfo(objType); if (objectToVisit == null) {
if (exclusionStrategy.shouldSkipClass(objTypeInfo.getRawClass())) { return;
return; }
} TypeInfo objTypeInfo = new TypeInfo(objType);
if (exclusionStrategy.shouldSkipClass(objTypeInfo.getRawClass())) {
if (ancestors.contains(obj)) { return;
throw new IllegalStateException("Circular reference found: " + obj); }
}
ancestors.push(obj); if (ancestors.contains(objectToVisit)) {
throw new IllegalStateException("Circular reference found: " + objectToVisit);
try { }
boolean visitedWithCustomHandler = visitor.visitUsingCustomHandler(obj, objType); ancestors.push(objectToVisit);
if (!visitedWithCustomHandler) {
try {
if (objTypeInfo.isArray()) { if (objTypeInfo.isArray()) {
visitor.visitArray(obj, objType); visitor.visitArray(objectToVisit, objType);
} else { } else {
visitor.startVisitingObject(obj); visitor.startVisitingObject(objectToVisit);
// For all classes in the inheritance hierarchy (including the current class), // For all classes in the inheritance hierarchy (including the current class),
// visit all fields // visit all fields
for (Class<?> curr = objTypeInfo.getRawClass(); for (Class<?> curr = objTypeInfo.getRawClass();
curr != null && !curr.equals(Object.class); curr = curr.getSuperclass()) { curr != null && !curr.equals(Object.class); curr = curr.getSuperclass()) {
if (!curr.isSynthetic()) { if (!curr.isSynthetic()) {
navigateClassFields(obj, curr, visitor); navigateClassFields(objectToVisit, curr, visitor);
} }
} }
} }
} finally {
ancestors.pop();
} }
} finally {
ancestors.pop();
} }
} }

View File

@ -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<DataHolder> {
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);
}
}
}