diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java index 65378cac..185c8e0c 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java @@ -35,6 +35,7 @@ abstract class JsonDeserializationVisitor 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 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 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, 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(jsonChild, childType, diff --git a/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java index 9c1518b2..b3a2ea94 100644 --- a/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java @@ -93,32 +93,32 @@ final class JsonObjectDeserializationVisitor 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, 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(); } diff --git a/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java b/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java index bb4e0a02..21354d07 100644 --- a/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java +++ b/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java @@ -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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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; + } } diff --git a/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java index 0a5984a0..2ae49923 100755 --- a/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java +++ b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java @@ -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 {