Deserialization will no longer call the default constructor or InstanceCreator if the object being deserialized has a custom deserializer.
This commit is contained in:
parent
1e7f3ebe7a
commit
2effd57976
|
@ -70,8 +70,7 @@ final class JsonDeserializationContextDefault implements JsonDeserializationCont
|
|||
JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObjectDeserializationVisitor<T> visitor = new JsonObjectDeserializationVisitor<T>(
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
|
|||
this.context = context;
|
||||
}
|
||||
|
||||
T getTarget() {
|
||||
public T getTarget() {
|
||||
if (target == null) {
|
||||
target = constructTarget();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user