implemented support for deserialization to deserialize to more specific type if an instance creator returns a sub-class.
This commit is contained in:
parent
fffb204cee
commit
338758a0d3
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user