From c2cb4d4bc081d678f4a8fc3bdcd7a8e86c36f761 Mon Sep 17 00:00:00 2001 From: Ganesh Pagade Date: Tue, 5 Mar 2019 00:01:05 +0530 Subject: [PATCH 1/6] gson advanced list examples --- .../org/baeldung/gson/entities/Animal.java | 5 + .../java/org/baeldung/gson/entities/Cow.java | 19 ++ .../java/org/baeldung/gson/entities/Dog.java | 18 ++ .../org/baeldung/gson/entities/MyClass.java | 37 +++ .../serialization/AnimalDeserializer.java | 35 +++ .../gson/advance/GsonAdvanceUnitTest.java | 110 ++++++++ .../advance/RuntimeTypeAdapterFactory.java | 265 ++++++++++++++++++ 7 files changed, 489 insertions(+) create mode 100644 gson/src/main/java/org/baeldung/gson/entities/Animal.java create mode 100644 gson/src/main/java/org/baeldung/gson/entities/Cow.java create mode 100644 gson/src/main/java/org/baeldung/gson/entities/Dog.java create mode 100644 gson/src/main/java/org/baeldung/gson/entities/MyClass.java create mode 100644 gson/src/main/java/org/baeldung/gson/serialization/AnimalDeserializer.java create mode 100644 gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java create mode 100644 gson/src/test/java/org/baeldung/gson/advance/RuntimeTypeAdapterFactory.java diff --git a/gson/src/main/java/org/baeldung/gson/entities/Animal.java b/gson/src/main/java/org/baeldung/gson/entities/Animal.java new file mode 100644 index 0000000000..2eec5f8704 --- /dev/null +++ b/gson/src/main/java/org/baeldung/gson/entities/Animal.java @@ -0,0 +1,5 @@ +package org.baeldung.gson.entities; + +public abstract class Animal { + public String type = "Animal"; +} diff --git a/gson/src/main/java/org/baeldung/gson/entities/Cow.java b/gson/src/main/java/org/baeldung/gson/entities/Cow.java new file mode 100644 index 0000000000..020bcd5860 --- /dev/null +++ b/gson/src/main/java/org/baeldung/gson/entities/Cow.java @@ -0,0 +1,19 @@ +package org.baeldung.gson.entities; + +public class Cow extends Animal { + private String breed; + + public Cow() { + breed = "Jersey"; + type = "Cow"; + } + + public String getBreed() { + return breed; + } + + public void setBreed(String breed) { + this.breed = breed; + } +} + diff --git a/gson/src/main/java/org/baeldung/gson/entities/Dog.java b/gson/src/main/java/org/baeldung/gson/entities/Dog.java new file mode 100644 index 0000000000..042d73adcf --- /dev/null +++ b/gson/src/main/java/org/baeldung/gson/entities/Dog.java @@ -0,0 +1,18 @@ +package org.baeldung.gson.entities; + +public class Dog extends Animal { + private String petName; + + public Dog() { + petName = "Milo"; + type = "Dog"; + } + + public String getPetName() { + return petName; + } + + public void setPetName(String petName) { + this.petName = petName; + } +} diff --git a/gson/src/main/java/org/baeldung/gson/entities/MyClass.java b/gson/src/main/java/org/baeldung/gson/entities/MyClass.java new file mode 100644 index 0000000000..86e14985da --- /dev/null +++ b/gson/src/main/java/org/baeldung/gson/entities/MyClass.java @@ -0,0 +1,37 @@ +package org.baeldung.gson.entities; + +public class MyClass { + private int id; + private String[] strings; + + public MyClass() { + id = 1; + strings = new String[] { "a", "b" }; + } + + + // @Override + // public String toString() { + // return "{" + + // "id=" + id + + // ", name='" + name + '\'' + + // ", strings=" + Arrays.toString(strings) + + // '}'; + // } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String[] getStrings() { + return strings; + } + + public void setStrings(String[] strings) { + this.strings = strings; + } +} diff --git a/gson/src/main/java/org/baeldung/gson/serialization/AnimalDeserializer.java b/gson/src/main/java/org/baeldung/gson/serialization/AnimalDeserializer.java new file mode 100644 index 0000000000..9dcef0e10b --- /dev/null +++ b/gson/src/main/java/org/baeldung/gson/serialization/AnimalDeserializer.java @@ -0,0 +1,35 @@ +package org.baeldung.gson.serialization; + +import com.google.gson.Gson; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import org.baeldung.gson.entities.Animal; + +public class AnimalDeserializer implements JsonDeserializer { + private String animalTypeElementName; + private Gson gson; + private Map> animalTypeRegistry; + + public AnimalDeserializer(String animalTypeElementName) { + this.animalTypeElementName = animalTypeElementName; + this.gson = new Gson(); + this.animalTypeRegistry = new HashMap<>(); + } + + public void registerBarnType(String animalTypeName, Class animalType) { + animalTypeRegistry.put(animalTypeName, animalType); + } + + public Animal deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { + JsonObject animalObject = json.getAsJsonObject(); + JsonElement animalTypeElement = animalObject.get(animalTypeElementName); + + Class animalType = animalTypeRegistry.get(animalTypeElement.getAsString()); + return gson.fromJson(animalObject, animalType); + } +} \ No newline at end of file diff --git a/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java b/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java new file mode 100644 index 0000000000..7032dde1ce --- /dev/null +++ b/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java @@ -0,0 +1,110 @@ +package org.baeldung.gson.advance; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import org.baeldung.gson.entities.Animal; +import org.baeldung.gson.entities.Cow; +import org.baeldung.gson.entities.Dog; +import org.baeldung.gson.entities.MyClass; +import org.baeldung.gson.serialization.AnimalDeserializer; +import org.junit.Test; + +public class GsonAdvanceUnitTest { + + @Test public void givenListOfMyClass_whenSerializing_thenCorrect() { + List list = new ArrayList<>(); + list.add(new MyClass()); + list.add(new MyClass()); + + Gson gson = new Gson(); + String jsonString = gson.toJson(list); + String expectedString = "[{\"id\":1,\"strings\":[\"a\",\"b\"]},{\"id\":1,\"strings\":[\"a\",\"b\"]}]"; + + assertEquals(expectedString, jsonString); + } + + @Test(expected = ClassCastException.class) + public void givenJsonString_whenIncorrectDeserializing_thenThrowClassCastException() { + String inputString = "[{\"id\":1,\"strings\":[\"a\",\"b\"]},{\"id\":1,\"strings\":[\"a\",\"b\"]}]"; + + Gson gson = new Gson(); + List list = gson.fromJson(inputString, ArrayList.class); + + assertEquals(2, list.size()); + int id = list.get(0).getId(); + } + + @Test public void givenJsonString_whenDeserializing_thenReturnListOfMyClass() { + String inputString = "[{\"id\":1,\"strings\":[\"a\",\"b\"]},{\"id\":1,\"strings\":[\"a\",\"b\"]}]"; + Type listOfMyClassObject = new TypeToken>() {}.getType(); + + Gson gson = new Gson(); + List list = gson.fromJson(inputString, listOfMyClassObject); + + assertEquals(2, list.size()); + assertEquals(1, list.get(0).getId()); + } + + @Test public void givenPolymorphicList_whenSerializeWithTypeAdapter_thenCorrect() { + String expectedString = "[{\"petName\":\"Milo\",\"type\":\"Dog\"},{\"breed\":\"Jersey\",\"type\":\"Cow\"}]"; + + List inList = new ArrayList<>(); + inList.add(new Dog()); + inList.add(new Cow()); + + String jsonString = new Gson().toJson(inList); + + assertEquals(expectedString, jsonString); + } + + @Test public void givenPolymorphicList_whenDeserializeWithTypeAdapter_thenCorrect() { + String inputString = "[{\"petName\":\"Milo\",\"type\":\"Dog\"},{\"breed\":\"Jersey\",\"type\":\"Cow\"}]"; + + AnimalDeserializer deserializer = new AnimalDeserializer("type"); + deserializer.registerBarnType("Dog", Dog.class); + deserializer.registerBarnType("Cow", Cow.class); + Gson gson = new GsonBuilder() + .registerTypeAdapter(Animal.class, deserializer) + .create(); + + List outList = gson.fromJson(inputString, new TypeToken>(){}.getType()); + + assertEquals(2, outList.size()); + assertTrue(outList.get(0) instanceof Dog); + } + + @Test public void givenPolymorphicList_whenSerializeWithRuntimeTypeAdapter_thenCorrect() { + String expectedString = "[{\"petName\":\"Milo\",\"type\":\"Dog\"},{\"breed\":\"Jersey\",\"type\":\"Cow\"}]"; + + List inList = new ArrayList<>(); + inList.add(new Dog()); + inList.add(new Cow()); + String jsonString = new Gson().toJson(inList); + + assertEquals(expectedString, jsonString); + } + + @Test public void givenPolymorphicList_whenDeserializeWithRuntimeTypeAdapter_thenCorrect() { + String inputString = "[{\"petName\":\"Milo\",\"type\":\"Dog\"},{\"breed\":\"Jersey\",\"type\":\"Cow\"}]"; + + Type listOfAnimals = new TypeToken>() {}.getType(); + + RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory.of(Animal.class) + .registerSubtype(Dog.class) + .registerSubtype(Cow.class); + + Gson gson = new GsonBuilder().registerTypeAdapterFactory(adapter).create(); + + List outList = gson.fromJson(inputString, listOfAnimals); + + assertEquals(2, outList.size()); + assertTrue(outList.get(0) instanceof Dog); + } +} \ No newline at end of file diff --git a/gson/src/test/java/org/baeldung/gson/advance/RuntimeTypeAdapterFactory.java b/gson/src/test/java/org/baeldung/gson/advance/RuntimeTypeAdapterFactory.java new file mode 100644 index 0000000000..739dd889c7 --- /dev/null +++ b/gson/src/test/java/org/baeldung/gson/advance/RuntimeTypeAdapterFactory.java @@ -0,0 +1,265 @@ +package org.baeldung.gson.advance; + +/* + * Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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. + */ + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.internal.Streams; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +/** + * Adapts values whose runtime type may differ from their declaration type. This + * is necessary when a field's type is not the same type that GSON should create + * when deserializing that field. For example, consider these types: + *
   {@code
+ *   abstract class Shape {
+ *     int x;
+ *     int y;
+ *   }
+ *   class Circle extends Shape {
+ *     int radius;
+ *   }
+ *   class Rectangle extends Shape {
+ *     int width;
+ *     int height;
+ *   }
+ *   class Diamond extends Shape {
+ *     int width;
+ *     int height;
+ *   }
+ *   class Drawing {
+ *     Shape bottomShape;
+ *     Shape topShape;
+ *   }
+ * }
+ *

Without additional type information, the serialized JSON is ambiguous. Is + * the bottom shape in this drawing a rectangle or a diamond?

   {@code
+ *   {
+ *     "bottomShape": {
+ *       "width": 10,
+ *       "height": 5,
+ *       "x": 0,
+ *       "y": 0
+ *     },
+ *     "topShape": {
+ *       "radius": 2,
+ *       "x": 4,
+ *       "y": 1
+ *     }
+ *   }}
+ * This class addresses this problem by adding type information to the + * serialized JSON and honoring that type information when the JSON is + * deserialized:
   {@code
+ *   {
+ *     "bottomShape": {
+ *       "type": "Diamond",
+ *       "width": 10,
+ *       "height": 5,
+ *       "x": 0,
+ *       "y": 0
+ *     },
+ *     "topShape": {
+ *       "type": "Circle",
+ *       "radius": 2,
+ *       "x": 4,
+ *       "y": 1
+ *     }
+ *   }}
+ * Both the type field name ({@code "type"}) and the type labels ({@code + * "Rectangle"}) are configurable. + * + *

Registering Types

+ * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field + * name to the {@link #of} factory method. If you don't supply an explicit type + * field name, {@code "type"} will be used.
   {@code
+ *   RuntimeTypeAdapterFactory shapeAdapterFactory
+ *       = RuntimeTypeAdapterFactory.of(Shape.class, "type");
+ * }
+ * Next register all of your subtypes. Every subtype must be explicitly + * registered. This protects your application from injection attacks. If you + * don't supply an explicit type label, the type's simple name will be used. + *
   {@code
+ *   shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
+ *   shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
+ *   shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
+ * }
+ * Finally, register the type adapter factory in your application's GSON builder: + *
   {@code
+ *   Gson gson = new GsonBuilder()
+ *       .registerTypeAdapterFactory(shapeAdapterFactory)
+ *       .create();
+ * }
+ * Like {@code GsonBuilder}, this API supports chaining:
   {@code
+ *   RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
+ *       .registerSubtype(Rectangle.class)
+ *       .registerSubtype(Circle.class)
+ *       .registerSubtype(Diamond.class);
+ * }
+ */ +public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { + private final Class baseType; + private final String typeFieldName; + private final Map> labelToSubtype = new LinkedHashMap>(); + private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); + private final boolean maintainType; + + private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName, boolean maintainType) { + if (typeFieldName == null || baseType == null) { + throw new NullPointerException(); + } + this.baseType = baseType; + this.typeFieldName = typeFieldName; + this.maintainType = maintainType; + } + + /** + * Creates a new runtime type adapter using for {@code baseType} using {@code + * typeFieldName} as the type field name. Type field names are case sensitive. + * {@code maintainType} flag decide if the type will be stored in pojo or not. + */ + public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName, boolean maintainType) { + return new RuntimeTypeAdapterFactory(baseType, typeFieldName, maintainType); + } + + /** + * Creates a new runtime type adapter using for {@code baseType} using {@code + * typeFieldName} as the type field name. Type field names are case sensitive. + */ + public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) { + return new RuntimeTypeAdapterFactory(baseType, typeFieldName, false); + } + + /** + * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as + * the type field name. + */ + public static RuntimeTypeAdapterFactory of(Class baseType) { + return new RuntimeTypeAdapterFactory(baseType, "type", false); + } + + /** + * Registers {@code type} identified by {@code label}. Labels are case + * sensitive. + * + * @throws IllegalArgumentException if either {@code type} or {@code label} + * have already been registered on this type adapter. + */ + public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { + if (type == null || label == null) { + throw new NullPointerException(); + } + if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { + throw new IllegalArgumentException("types and labels must be unique"); + } + labelToSubtype.put(label, type); + subtypeToLabel.put(type, label); + return this; + } + + /** + * Registers {@code type} identified by its {@link Class#getSimpleName simple + * name}. Labels are case sensitive. + * + * @throws IllegalArgumentException if either {@code type} or its simple name + * have already been registered on this type adapter. + */ + public RuntimeTypeAdapterFactory registerSubtype(Class type) { + return registerSubtype(type, type.getSimpleName()); + } + + public TypeAdapter create(Gson gson, TypeToken type) { + if (type.getRawType() != baseType) { + return null; + } + + final Map> labelToDelegate + = new LinkedHashMap>(); + final Map, TypeAdapter> subtypeToDelegate + = new LinkedHashMap, TypeAdapter>(); + for (Map.Entry> entry : labelToSubtype.entrySet()) { + TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); + labelToDelegate.put(entry.getKey(), delegate); + subtypeToDelegate.put(entry.getValue(), delegate); + } + + return new TypeAdapter() { + @Override public R read(JsonReader in) throws IOException { + JsonElement jsonElement = Streams.parse(in); + JsonElement labelJsonElement; + if (maintainType) { + labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName); + } else { + labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); + } + + if (labelJsonElement == null) { + throw new JsonParseException("cannot deserialize " + baseType + + " because it does not define a field named " + typeFieldName); + } + String label = labelJsonElement.getAsString(); + @SuppressWarnings("unchecked") // registration requires that subtype extends T + TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); + if (delegate == null) { + throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + + label + "; did you forget to register a subtype?"); + } + return delegate.fromJsonTree(jsonElement); + } + + @Override public void write(JsonWriter out, R value) throws IOException { + Class srcType = value.getClass(); + String label = subtypeToLabel.get(srcType); + @SuppressWarnings("unchecked") // registration requires that subtype extends T + TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType); + if (delegate == null) { + throw new JsonParseException("cannot serialize " + srcType.getName() + + "; did you forget to register a subtype?"); + } + JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); + + if (maintainType) { + Streams.write(jsonObject, out); + return; + } + + JsonObject clone = new JsonObject(); + + if (jsonObject.has(typeFieldName)) { + throw new JsonParseException("cannot serialize " + srcType.getName() + + " because it already defines a field named " + typeFieldName); + } + clone.add(typeFieldName, new JsonPrimitive(label)); + + for (Map.Entry e : jsonObject.entrySet()) { + clone.add(e.getKey(), e.getValue()); + } + Streams.write(clone, out); + } + }.nullSafe(); + } +} From 9c62f1ea0c0be4a6a352a76496448ba9e422d149 Mon Sep 17 00:00:00 2001 From: Ganesh Pagade Date: Tue, 5 Mar 2019 22:27:46 +0530 Subject: [PATCH 2/6] minor change --- .../java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java b/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java index 7032dde1ce..020118aa2b 100644 --- a/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java +++ b/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java @@ -37,8 +37,7 @@ public class GsonAdvanceUnitTest { Gson gson = new Gson(); List list = gson.fromJson(inputString, ArrayList.class); - assertEquals(2, list.size()); - int id = list.get(0).getId(); + assertEquals(1, list.get(0).getId()); } @Test public void givenJsonString_whenDeserializing_thenReturnListOfMyClass() { From b1834b6d96b566faed1f520552cbb14e650052d6 Mon Sep 17 00:00:00 2001 From: Ganesh Pagade Date: Tue, 5 Mar 2019 22:29:31 +0530 Subject: [PATCH 3/6] minor change --- .../main/java/org/baeldung/gson/entities/MyClass.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/gson/src/main/java/org/baeldung/gson/entities/MyClass.java b/gson/src/main/java/org/baeldung/gson/entities/MyClass.java index 86e14985da..827ca10cb0 100644 --- a/gson/src/main/java/org/baeldung/gson/entities/MyClass.java +++ b/gson/src/main/java/org/baeldung/gson/entities/MyClass.java @@ -9,16 +9,6 @@ public class MyClass { strings = new String[] { "a", "b" }; } - - // @Override - // public String toString() { - // return "{" + - // "id=" + id + - // ", name='" + name + '\'' + - // ", strings=" + Arrays.toString(strings) + - // '}'; - // } - public int getId() { return id; } From 02fd91ad35810cfe06d8b2e28a740957a8437ad5 Mon Sep 17 00:00:00 2001 From: Ganesh Pagade Date: Sat, 9 Mar 2019 14:59:08 +0530 Subject: [PATCH 4/6] review comments --- .../org/baeldung/gson/entities/MyClass.java | 36 +++++++++++++---- .../gson/advance/GsonAdvanceUnitTest.java | 40 +++++++++++-------- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/gson/src/main/java/org/baeldung/gson/entities/MyClass.java b/gson/src/main/java/org/baeldung/gson/entities/MyClass.java index 827ca10cb0..997f4c3f26 100644 --- a/gson/src/main/java/org/baeldung/gson/entities/MyClass.java +++ b/gson/src/main/java/org/baeldung/gson/entities/MyClass.java @@ -1,14 +1,18 @@ package org.baeldung.gson.entities; +import java.util.Objects; + public class MyClass { private int id; - private String[] strings; + private String name; - public MyClass() { - id = 1; - strings = new String[] { "a", "b" }; + public MyClass(int id, String name) { + this.id = id; + this.name = name; } + public MyClass() { } + public int getId() { return id; } @@ -17,11 +21,27 @@ public class MyClass { this.id = id; } - public String[] getStrings() { - return strings; + public String getName() { + return name; } - public void setStrings(String[] strings) { - this.strings = strings; + public void setName(String name) { + this.name = name; + } + + @Override public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MyClass myClass = (MyClass) o; + return id == myClass.id && Objects.equals(name, myClass.name); + } + + @Override public int hashCode() { + + return Objects.hash(id, name); } } diff --git a/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java b/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java index 020118aa2b..3bf2afbe4a 100644 --- a/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java +++ b/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java @@ -8,6 +8,7 @@ import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.baeldung.gson.entities.Animal; import org.baeldung.gson.entities.Cow; @@ -18,40 +19,42 @@ import org.junit.Test; public class GsonAdvanceUnitTest { - @Test public void givenListOfMyClass_whenSerializing_thenCorrect() { - List list = new ArrayList<>(); - list.add(new MyClass()); - list.add(new MyClass()); + @Test + public void givenListOfMyClass_whenSerializing_thenCorrect() { + List list = Arrays.asList(new MyClass(1, "name1"), new MyClass(2, "name2")); Gson gson = new Gson(); String jsonString = gson.toJson(list); - String expectedString = "[{\"id\":1,\"strings\":[\"a\",\"b\"]},{\"id\":1,\"strings\":[\"a\",\"b\"]}]"; + String expectedString = "[{\"id\":1,\"name\":\"name1\"},{\"id\":2,\"name\":\"name2\"}]"; assertEquals(expectedString, jsonString); } @Test(expected = ClassCastException.class) public void givenJsonString_whenIncorrectDeserializing_thenThrowClassCastException() { - String inputString = "[{\"id\":1,\"strings\":[\"a\",\"b\"]},{\"id\":1,\"strings\":[\"a\",\"b\"]}]"; + String inputString = "[{\"id\":1,\"name\":\"name1\"},{\"id\":2,\"name\":\"name2\"}]"; Gson gson = new Gson(); - List list = gson.fromJson(inputString, ArrayList.class); + List outputList = gson.fromJson(inputString, ArrayList.class); - assertEquals(1, list.get(0).getId()); + assertEquals(1, outputList.get(0).getId()); } - @Test public void givenJsonString_whenDeserializing_thenReturnListOfMyClass() { - String inputString = "[{\"id\":1,\"strings\":[\"a\",\"b\"]},{\"id\":1,\"strings\":[\"a\",\"b\"]}]"; + @Test + public void givenJsonString_whenDeserializing_thenReturnListOfMyClass() { + String inputString = "[{\"id\":1,\"name\":\"name1\"},{\"id\":2,\"name\":\"name2\"}]"; + List inputList = Arrays.asList(new MyClass(1, "name1"), new MyClass(2, "name2")); + Type listOfMyClassObject = new TypeToken>() {}.getType(); Gson gson = new Gson(); - List list = gson.fromJson(inputString, listOfMyClassObject); + List outputList = gson.fromJson(inputString, listOfMyClassObject); - assertEquals(2, list.size()); - assertEquals(1, list.get(0).getId()); + assertEquals(inputList, outputList); } - @Test public void givenPolymorphicList_whenSerializeWithTypeAdapter_thenCorrect() { + @Test + public void givenPolymorphicList_whenSerializeWithTypeAdapter_thenCorrect() { String expectedString = "[{\"petName\":\"Milo\",\"type\":\"Dog\"},{\"breed\":\"Jersey\",\"type\":\"Cow\"}]"; List inList = new ArrayList<>(); @@ -63,7 +66,8 @@ public class GsonAdvanceUnitTest { assertEquals(expectedString, jsonString); } - @Test public void givenPolymorphicList_whenDeserializeWithTypeAdapter_thenCorrect() { + @Test + public void givenPolymorphicList_whenDeserializeWithTypeAdapter_thenCorrect() { String inputString = "[{\"petName\":\"Milo\",\"type\":\"Dog\"},{\"breed\":\"Jersey\",\"type\":\"Cow\"}]"; AnimalDeserializer deserializer = new AnimalDeserializer("type"); @@ -79,7 +83,8 @@ public class GsonAdvanceUnitTest { assertTrue(outList.get(0) instanceof Dog); } - @Test public void givenPolymorphicList_whenSerializeWithRuntimeTypeAdapter_thenCorrect() { + @Test + public void givenPolymorphicList_whenSerializeWithRuntimeTypeAdapter_thenCorrect() { String expectedString = "[{\"petName\":\"Milo\",\"type\":\"Dog\"},{\"breed\":\"Jersey\",\"type\":\"Cow\"}]"; List inList = new ArrayList<>(); @@ -90,7 +95,8 @@ public class GsonAdvanceUnitTest { assertEquals(expectedString, jsonString); } - @Test public void givenPolymorphicList_whenDeserializeWithRuntimeTypeAdapter_thenCorrect() { + @Test + public void givenPolymorphicList_whenDeserializeWithRuntimeTypeAdapter_thenCorrect() { String inputString = "[{\"petName\":\"Milo\",\"type\":\"Dog\"},{\"breed\":\"Jersey\",\"type\":\"Cow\"}]"; Type listOfAnimals = new TypeToken>() {}.getType(); From 213349420b947a8d749eb4d2a51337bc5b80a870 Mon Sep 17 00:00:00 2001 From: Ganesh Pagade Date: Sat, 9 Mar 2019 15:06:07 +0530 Subject: [PATCH 5/6] review comments --- gson/src/main/java/org/baeldung/gson/entities/MyClass.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gson/src/main/java/org/baeldung/gson/entities/MyClass.java b/gson/src/main/java/org/baeldung/gson/entities/MyClass.java index 997f4c3f26..4e717e72c3 100644 --- a/gson/src/main/java/org/baeldung/gson/entities/MyClass.java +++ b/gson/src/main/java/org/baeldung/gson/entities/MyClass.java @@ -29,7 +29,8 @@ public class MyClass { this.name = name; } - @Override public boolean equals(Object o) { + @Override + public boolean equals(Object o) { if (this == o) { return true; } @@ -40,7 +41,8 @@ public class MyClass { return id == myClass.id && Objects.equals(name, myClass.name); } - @Override public int hashCode() { + @Override + public int hashCode() { return Objects.hash(id, name); } From ef94015a3696aa9dc428de00a7a15654a2267687 Mon Sep 17 00:00:00 2001 From: Ganesh Pagade Date: Sun, 10 Mar 2019 11:24:33 +0530 Subject: [PATCH 6/6] review comments --- .../java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java b/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java index 3bf2afbe4a..5b787f1956 100644 --- a/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java +++ b/gson/src/test/java/org/baeldung/gson/advance/GsonAdvanceUnitTest.java @@ -81,6 +81,7 @@ public class GsonAdvanceUnitTest { assertEquals(2, outList.size()); assertTrue(outList.get(0) instanceof Dog); + assertTrue(outList.get(1) instanceof Cow); } @Test @@ -101,7 +102,7 @@ public class GsonAdvanceUnitTest { Type listOfAnimals = new TypeToken>() {}.getType(); - RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory.of(Animal.class) + RuntimeTypeAdapterFactory adapter = RuntimeTypeAdapterFactory.of(Animal.class, "type") .registerSubtype(Dog.class) .registerSubtype(Cow.class); @@ -111,5 +112,6 @@ public class GsonAdvanceUnitTest { assertEquals(2, outList.size()); assertTrue(outList.get(0) instanceof Dog); + assertTrue(outList.get(1) instanceof Cow); } } \ No newline at end of file