From 1da3ef98916a360f6af703da0ac0a26b3a459ddb Mon Sep 17 00:00:00 2001 From: Inderjeet Singh Date: Tue, 6 Oct 2009 01:15:28 +0000 Subject: [PATCH] Ensured that a custom handler is not visited during deserialization. Ensured that JsonTreeNavigator can handle null values as children of JsonObject. This is now possible since Gson exposes toJson(JsonElement) method to which the user can pass a JsonElement with null values. --- .../gson/JsonDeserializationVisitor.java | 4 +- .../JsonObjectDeserializationVisitor.java | 6 +- .../com/google/gson/JsonTreeNavigator.java | 6 +- .../functional/CustomTypeAdaptersTest.java | 68 +++++++++++++++++++ .../google/gson/functional/ObjectTest.java | 20 ++++-- .../gson/functional/PrintFormattingTest.java | 30 +++++++- 6 files changed, 121 insertions(+), 13 deletions(-) diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java index 23583c39..d977bd3a 100644 --- a/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonDeserializationVisitor.java @@ -69,7 +69,9 @@ abstract class JsonDeserializationVisitor implements ObjectNavigator.Visitor Type objType = objTypePair.getType(); JsonDeserializer deserializer = deserializers.getHandlerFor(objType); if (deserializer != null) { - target = (T) deserializer.deserialize(json, objType, context); + if (json != null && !json.isJsonNull()) { + target = (T) deserializer.deserialize(json, objType, context); + } return true; } return false; diff --git a/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java index 1df9da1b..9bfc65d8 100644 --- a/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java @@ -112,8 +112,10 @@ final class JsonObjectDeserializationVisitor extends JsonDeserializationVisit @SuppressWarnings("unchecked") JsonDeserializer deserializer = deserializers.getHandlerFor(actualTypeOfField); if (deserializer != null) { - Object value = deserializer.deserialize(child, actualTypeOfField, context); - f.set(parent, value); + if (child != null && !child.isJsonNull()) { + Object value = deserializer.deserialize(child, actualTypeOfField, context); + f.set(parent, value); + } return true; } return false; diff --git a/gson/src/main/java/com/google/gson/JsonTreeNavigator.java b/gson/src/main/java/com/google/gson/JsonTreeNavigator.java index 992de504..7d6ad8d1 100644 --- a/gson/src/main/java/com/google/gson/JsonTreeNavigator.java +++ b/gson/src/main/java/com/google/gson/JsonTreeNavigator.java @@ -34,7 +34,7 @@ final class JsonTreeNavigator { } public void navigate(JsonElement element) throws IOException { - if (element.isJsonNull()) { + if (element == null || element.isJsonNull()) { visitor.visitNull(); } else if (element.isJsonArray()) { JsonArray array = element.getAsJsonArray(); @@ -68,7 +68,7 @@ final class JsonTreeNavigator { */ private boolean visitChild(JsonObject parent, String childName, JsonElement child, boolean isFirst) throws IOException { - if (child.isJsonNull()) { + if (child == null || child.isJsonNull()) { if (visitNulls) { visitor.visitNullObjectMember(parent, childName, isFirst); navigate(child.getAsJsonNull()); @@ -93,7 +93,7 @@ final class JsonTreeNavigator { * Returns true if the child was visited, false if it was skipped. */ private void visitChild(JsonArray parent, JsonElement child, boolean isFirst) throws IOException { - if (child.isJsonNull()) { + if (child == null || child.isJsonNull()) { visitor.visitNullArrayMember(parent, isFirst); navigate(child); } else if (child.isJsonArray()) { diff --git a/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java index 526a42c5..88dbc5a0 100644 --- a/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java @@ -406,4 +406,72 @@ public class CustomTypeAdaptersTest extends TestCase { assertEquals("Jacob", foo.part1); assertEquals("Tomaw", foo.part2); } + + public void testEnsureCustomSerializerNotInvokedForNullValues() { + Gson gson = new GsonBuilder() + .registerTypeAdapter(DataHolder.class, new DataHolderSerializer()) + .create(); + DataHolderWrapper target = new DataHolderWrapper(new DataHolder("abc")); + String json = gson.toJson(target); + assertEquals("{\"wrappedData\":{\"myData\":\"abc\"}}", json); + } + + public void testEnsureCustomDeserializerNotInvokedForNullValues() { + Gson gson = new GsonBuilder() + .registerTypeAdapter(DataHolder.class, new DataHolderDeserializer()) + .create(); + String json = "{wrappedData:null}"; + DataHolderWrapper actual = gson.fromJson(json, DataHolderWrapper.class); + assertNull(actual.wrappedData); + } + + private static class DataHolder { + @SuppressWarnings("unused") + final String data; + + // For use by Gson + @SuppressWarnings("unused") + private DataHolder() { + this(""); + } + + public DataHolder(String data) { + this.data = data; + } + } + + private static class DataHolderWrapper { + final DataHolder wrappedData; + + // For use by Gson + @SuppressWarnings("unused") + private DataHolderWrapper() { + this(null); + } + + public DataHolderWrapper(DataHolder data) { + this.wrappedData = data; + } + } + + private static class DataHolderSerializer implements JsonSerializer { + public JsonElement serialize(DataHolder src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject obj = new JsonObject(); + obj.addProperty("myData", src.data); + return obj; + } + } + + private static class DataHolderDeserializer implements JsonDeserializer { + public DataHolder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObj = json.getAsJsonObject(); + JsonElement jsonElement = jsonObj.get("data"); + if (jsonElement == null || jsonElement.isJsonNull()) { + return new DataHolder(null); + } + return new DataHolder(jsonElement.getAsString()); + } + } + } diff --git a/gson/src/test/java/com/google/gson/functional/ObjectTest.java b/gson/src/test/java/com/google/gson/functional/ObjectTest.java index 86fe2c64..da5de99d 100644 --- a/gson/src/test/java/com/google/gson/functional/ObjectTest.java +++ b/gson/src/test/java/com/google/gson/functional/ObjectTest.java @@ -16,15 +16,10 @@ package com.google.gson.functional; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; - -import junit.framework.TestCase; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.InstanceCreator; +import com.google.gson.JsonObject; import com.google.gson.common.TestTypes.ArrayOfObjects; import com.google.gson.common.TestTypes.BagOfPrimitiveWrappers; import com.google.gson.common.TestTypes.BagOfPrimitives; @@ -35,6 +30,12 @@ import com.google.gson.common.TestTypes.ClassWithTransientFields; import com.google.gson.common.TestTypes.Nested; import com.google.gson.common.TestTypes.PrimitiveArray; +import junit.framework.TestCase; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; + /** * Functional tests for Json serialization and deserialization of regular classes. * @@ -401,4 +402,11 @@ public class ObjectTest extends TestCase { String b = ""; String c = ""; } + + public void testJsonObjectSerialization() { + Gson gson = new GsonBuilder().serializeNulls().create(); + JsonObject obj = new JsonObject(); + String json = gson.toJson(obj); + assertEquals("{}", json); + } } diff --git a/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java b/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java index 7fded58f..757037b3 100644 --- a/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java @@ -17,6 +17,8 @@ package com.google.gson.functional; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; import com.google.gson.common.TestTypes.BagOfPrimitives; import com.google.gson.common.TestTypes.ClassWithTransientFields; import com.google.gson.common.TestTypes.Nested; @@ -35,9 +37,16 @@ import java.util.List; */ public class PrintFormattingTest extends TestCase { + private Gson gson; + + @Override + protected void setUp() throws Exception { + super.setUp(); + gson = new Gson(); + } + @SuppressWarnings("unchecked") public void testCompactFormattingLeavesNoWhiteSpace() { - Gson gson = new Gson(); List list = new ArrayList(); list.add(new BagOfPrimitives()); list.add(new Nested()); @@ -48,6 +57,25 @@ public class PrintFormattingTest extends TestCase { assertContainsNoWhiteSpace(json); } + public void testJsonObjectWithNullValues() { + JsonObject obj = new JsonObject(); + obj.addProperty("field1", "value1"); + obj.addProperty("field2", (String) null); + String json = gson.toJson(obj); + assertTrue(json.contains("field1")); + assertFalse(json.contains("field2")); + } + + public void testJsonObjectWithNullValuesSerialized() { + gson = new GsonBuilder().serializeNulls().create(); + JsonObject obj = new JsonObject(); + obj.addProperty("field1", "value1"); + obj.addProperty("field2", (String) null); + String json = gson.toJson(obj); + assertTrue(json.contains("field1")); + assertTrue(json.contains("field2")); + } + private static void assertContainsNoWhiteSpace(String str) { for (char c : str.toCharArray()) { assertFalse(Character.isWhitespace(c));