Ensured that custom deserializer is invoked with actual type returned for field values.

Fixed a bug where a top-level object was constructed with default values even when the custom deserializer tried to set it to null.
This commit is contained in:
Inderjeet Singh 2009-10-09 19:41:33 +00:00
parent 338758a0d3
commit bc1e5c5c99
4 changed files with 117 additions and 19 deletions

View File

@ -35,6 +35,7 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
protected final JsonElement json;
protected final Type targetType;
protected final JsonDeserializationContext context;
protected boolean constructed;
public JsonDeserializationVisitor(JsonElement json, Type targetType,
ObjectNavigatorFactory factory, ObjectConstructor objectConstructor,
@ -47,11 +48,13 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
this.deserializers = deserializers;
this.json = json;
this.context = context;
this.constructed = false;
}
public T getTarget() {
if (target == null) {
if (!constructed) {
target = constructTarget();
constructed = true;
}
return target;
}
@ -70,14 +73,21 @@ abstract class JsonDeserializationVisitor<T> implements ObjectNavigator.Visitor
if (pair == null) {
return false;
}
if (!json.isJsonNull()) {
JsonDeserializer deserializer = pair.getFirst();
Type objType = pair.getSecond().getType();
target = (T) deserializer.deserialize(json, objType, context);
}
Object value = invokeCustomDeserializer(json, pair);
target = (T) value;
constructed = true;
return true;
}
protected Object invokeCustomDeserializer(JsonElement element,
Pair<JsonDeserializer<?>, ObjectTypePair> pair) {
if (element == null || element.isJsonNull()) {
return null;
}
Type objType = pair.getSecond().getType();
return (pair.getFirst()).deserialize(element, objType, context);
}
final Object visitChildAsObject(Type childType, JsonElement jsonChild) {
JsonDeserializationVisitor<?> childVisitor =
new JsonObjectDeserializationVisitor<Object>(jsonChild, childType,

View File

@ -93,32 +93,32 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
return namingPolicy.translateName(f);
}
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent) {
public boolean visitFieldUsingCustomHandler(Field f, Type declaredTypeOfField, Object parent) {
try {
String fName = getFieldName(f);
if (!json.isJsonObject()) {
throw new JsonParseException("Expecting object found: " + json);
}
JsonElement child = json.getAsJsonObject().get(fName);
if (child == null) {
TypeInfo typeInfo = new TypeInfo(declaredTypeOfField);
if (child == null) { // Child will be null if the field wasn't present in Json
return true;
} else if (child.isJsonNull()) {
TypeInfo typeInfo = new TypeInfo(actualTypeOfField);
if (!typeInfo.isPrimitive()) {
f.set(parent, null);
}
return true;
}
@SuppressWarnings("unchecked")
JsonDeserializer deserializer = deserializers.getHandlerFor(actualTypeOfField);
if (deserializer != null) {
if (!child.isJsonNull()) {
Object value = deserializer.deserialize(child, actualTypeOfField, context);
f.set(parent, value);
}
return true;
ObjectTypePair objTypePair = new ObjectTypePair(null, declaredTypeOfField, false);
Pair<JsonDeserializer<?>, ObjectTypePair> pair = objTypePair.getMatchingHandler(deserializers);
if (pair == null) {
return false;
}
Object value = invokeCustomDeserializer(child, pair);
if (value != null || !typeInfo.isPrimitive()) {
f.set(parent, value);
}
return false;
return true;
} catch (IllegalAccessException e) {
throw new RuntimeException();
}

View File

@ -23,6 +23,8 @@ import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.common.TestTypes.Base;
import com.google.gson.common.TestTypes.ClassWithBaseField;
import junit.framework.TestCase;
@ -144,4 +146,90 @@ public class CustomDeserializerTest extends TestCase {
@SuppressWarnings("unused")
String field2;
}
public void testCustomDeserializerReturnsNullForTopLevelObject() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return null;
}
}).create();
String json = "{baseName:'Base',subName:'SubRevised'}";
Base target = gson.fromJson(json, Base.class);
assertNull(target);
}
public void testCustomDeserializerReturnsNull() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return null;
}
}).create();
String json = "{base:{baseName:'Base',subName:'SubRevised'}}";
ClassWithBaseField target = gson.fromJson(json, ClassWithBaseField.class);
assertNull(target.base);
}
public void testCustomDeserializerReturnsNullForTopLevelPrimitives() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(long.class, new JsonDeserializer<Long>() {
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return null;
}
}).create();
String json = "10";
assertNull(gson.fromJson(json, long.class));
}
public void testCustomDeserializerReturnsNullForPrimitiveFields() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(long.class, new JsonDeserializer<Long>() {
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return null;
}
}).create();
String json = "{field:10}";
ClassWithLong target = gson.fromJson(json, ClassWithLong.class);
assertEquals(0, target.field);
}
private static class ClassWithLong {
long field;
}
public void testCustomDeserializerReturnsNullForArrayElements() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return null;
}
}).create();
String json = "[{baseName:'Base'},{baseName:'Base'}]";
Base[] target = gson.fromJson(json, Base[].class);
assertNull(target[0]);
assertNull(target[1]);
}
public void testCustomDeserializerReturnsNullForArrayElementsForArrayField() {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Base.class, new JsonDeserializer<Base>() {
public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return null;
}
}).create();
String json = "{bases:[{baseName:'Base'},{baseName:'Base'}]}";
ClassWithBaseArray target = gson.fromJson(json, ClassWithBaseArray.class);
assertNull(target.bases[0]);
assertNull(target.bases[1]);
}
private static class ClassWithBaseArray {
Base[] bases;
}
}

View File

@ -200,7 +200,7 @@ public class NullObjectAndFieldTest extends TestCase {
}).create();
String json = "{value:'value1'}";
ObjectWithField target = gson.fromJson(json, ObjectWithField.class);
assertFalse("value1".equals(target.value));
assertNull(target);
}
private static class ObjectWithField {