diff --git a/app/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java b/app/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java index 256590250..9566b1838 100644 --- a/app/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java +++ b/app/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java @@ -12,6 +12,8 @@ * 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. + * + * From 0eb681b, adapted for Gadgetbridge to support null labels. */ package com.google.gson.typeadapters; @@ -19,6 +21,7 @@ package com.google.gson.typeadapters; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.gson.Gson; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; @@ -217,7 +220,7 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { */ @CanIgnoreReturnValue public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { - if (type == null || label == null) { + if (type == null) { throw new NullPointerException(); } if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { @@ -272,14 +275,14 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); } - if (labelJsonElement == null) { + if (labelJsonElement == null && !labelToDelegate.containsKey(null)) { throw new JsonParseException( "cannot deserialize " + baseType + " because it does not define a field named " + typeFieldName); } - String label = labelJsonElement.getAsString(); + String label = labelJsonElement != null ? labelJsonElement.getAsString() : null; @SuppressWarnings("unchecked") // registration requires that subtype extends T TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); if (delegate == null) { @@ -319,7 +322,11 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { + " because it already defines a field named " + typeFieldName); } - clone.add(typeFieldName, new JsonPrimitive(label)); + if (label != null) { + clone.add(typeFieldName, new JsonPrimitive(label)); + } else { + clone.add(typeFieldName, JsonNull.INSTANCE); + } for (Map.Entry e : jsonObject.entrySet()) { clone.add(e.getKey(), e.getValue()); diff --git a/app/src/test/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactoryTest.java b/app/src/test/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactoryTest.java new file mode 100644 index 000000000..520533241 --- /dev/null +++ b/app/src/test/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactoryTest.java @@ -0,0 +1,66 @@ +package com.google.gson.typeadapters; + +import static org.junit.Assert.*; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import org.junit.Test; + +public class RuntimeTypeAdapterFactoryTest { + private static final Gson GSON = new GsonBuilder() + .registerTypeAdapterFactory(RuntimeTypeAdapterFactory + .of(Parent.class, "type") + .registerSubtype(Child1.class, null) + .registerSubtype(Child2.class, "child2") + ) + .create(); + + @Test + public void testNullLabelSerialize() { + final Child1 child1 = new Child1("hello1"); + final Child2 child2 = new Child2("hello2"); + + assertEquals("{\"field1\":\"hello1\",\"fieldParent\":\"fromChild1\"}", GSON.toJson(child1, Parent.class)); + assertEquals("{\"type\":\"child2\",\"field2\":\"hello2\",\"fieldParent\":\"fromChild2\"}", GSON.toJson(child2, Parent.class)); + } + + @Test + public void testNullLabelDeserialize() { + final Parent child1 = GSON.fromJson("{\"field1\":\"hello1\",\"fieldParent\":\"fromChild1\"}", Parent.class); + assertTrue(child1 instanceof Child1); + assertEquals("fromChild1", child1.fieldParent); + assertEquals("hello1", ((Child1) child1).field1); + + final Parent child2 = GSON.fromJson("{\"type\":\"child2\",\"field2\":\"hello2\",\"fieldParent\":\"fromChild2\"}", Parent.class); + assertTrue(child2 instanceof Child2); + assertEquals("fromChild2", child2.fieldParent); + assertEquals("hello2", ((Child2) child2).field2); + } + + private static class Parent { + private final String fieldParent; + + private Parent(final String fieldParent) { + this.fieldParent = fieldParent; + } + } + + private static class Child1 extends Parent { + private final String field1; + + private Child1(final String field1) { + super("fromChild1"); + this.field1 = field1; + } + } + + private static class Child2 extends Parent { + private final String field2; + + private Child2(final String field2) { + super("fromChild2"); + this.field2 = field2; + } + } +}