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

View File

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

View File

@ -23,6 +23,8 @@ import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import com.google.gson.common.TestTypes.Base;
import com.google.gson.common.TestTypes.ClassWithBaseField;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -144,4 +146,90 @@ public class CustomDeserializerTest extends TestCase {
@SuppressWarnings("unused") @SuppressWarnings("unused")
String field2; 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(); }).create();
String json = "{value:'value1'}"; String json = "{value:'value1'}";
ObjectWithField target = gson.fromJson(json, ObjectWithField.class); ObjectWithField target = gson.fromJson(json, ObjectWithField.class);
assertFalse("value1".equals(target.value)); assertNull(target);
} }
private static class ObjectWithField { private static class ObjectWithField {