diff --git a/json-modules/gson-2/pom.xml b/json-modules/gson-2/pom.xml index c3935d5721..1184af14ec 100644 --- a/json-modules/gson-2/pom.xml +++ b/json-modules/gson-2/pom.xml @@ -18,6 +18,11 @@ gson ${gson.version} + + com.google.guava + guava + ${guava.version} + diff --git a/json-modules/gson-2/src/main/java/com/baeldung/gson/jsontolist/type/ParameterizedTypeImpl.java b/json-modules/gson-2/src/main/java/com/baeldung/gson/jsontolist/type/ParameterizedTypeImpl.java new file mode 100644 index 0000000000..cb22bd8012 --- /dev/null +++ b/json-modules/gson-2/src/main/java/com/baeldung/gson/jsontolist/type/ParameterizedTypeImpl.java @@ -0,0 +1,34 @@ +package com.baeldung.gson.jsontolist.type; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +public class ParameterizedTypeImpl implements ParameterizedType { + + private final Class rawType; + private final Type[] actualTypeArguments; + + private ParameterizedTypeImpl(Class rawType, Type[] actualTypeArguments) { + this.rawType = rawType; + this.actualTypeArguments = actualTypeArguments; + } + + public static ParameterizedType make(Class rawType, Type ... actualTypeArguments) { + return new ParameterizedTypeImpl(rawType, actualTypeArguments); + } + + @Override + public Type[] getActualTypeArguments() { + return actualTypeArguments; + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return null; + } +} diff --git a/json-modules/gson-2/src/main/java/com/baeldung/gson/jsontolist/type/School.java b/json-modules/gson-2/src/main/java/com/baeldung/gson/jsontolist/type/School.java new file mode 100644 index 0000000000..fc1d72fa29 --- /dev/null +++ b/json-modules/gson-2/src/main/java/com/baeldung/gson/jsontolist/type/School.java @@ -0,0 +1,23 @@ +package com.baeldung.gson.jsontolist.type; + +public class School { + + private String name; + private String city; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } +} diff --git a/json-modules/gson-2/src/main/java/com/baeldung/gson/jsontolist/type/Student.java b/json-modules/gson-2/src/main/java/com/baeldung/gson/jsontolist/type/Student.java new file mode 100644 index 0000000000..06fb1e3d44 --- /dev/null +++ b/json-modules/gson-2/src/main/java/com/baeldung/gson/jsontolist/type/Student.java @@ -0,0 +1,23 @@ +package com.baeldung.gson.jsontolist.type; + +public class Student { + + private String name; + private String grade; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getGrade() { + return grade; + } + + public void setGrade(String grade) { + this.grade = grade; + } +} diff --git a/json-modules/gson-2/src/test/java/com/baeldung/gson/jsontolist/type/JsonArrayStringToListUnitTest.java b/json-modules/gson-2/src/test/java/com/baeldung/gson/jsontolist/type/JsonArrayStringToListUnitTest.java new file mode 100644 index 0000000000..fe7ffed573 --- /dev/null +++ b/json-modules/gson-2/src/test/java/com/baeldung/gson/jsontolist/type/JsonArrayStringToListUnitTest.java @@ -0,0 +1,129 @@ +package com.baeldung.gson.jsontolist.type; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.reflect.TypeParameter; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.reflect.TypeToken; + +public class JsonArrayStringToListUnitTest { + + Logger LOGGER = LoggerFactory.getLogger(JsonArrayStringToListUnitTest.class); + final String jsonArrayOfStudents = + "[" + + "{\"name\":\"John\", \"grade\":\"1\"}, " + + "{\"name\":\"Tom\", \"grade\":\"2\"}, " + + "{\"name\":\"Ram\", \"grade\":\"3\"}, " + + "{\"name\":\"Sara\", \"grade\":\"1\"}" + + "]"; + final String jsonArrayOfSchools = + "[" + + "{\"name\":\"St. John\", \"city\":\"Chicago City\"}, " + + "{\"name\":\"St. Tom\", \"city\":\"New York City\"}, " + + "{\"name\":\"St. Ram\", \"city\":\"Mumbai\"}, " + + "{\"name\":\"St. Sara\", \"city\":\"Budapest\"}" + + "]"; + + @Test + void givenJsonArray_whenListElementTypeDynamic_thenConvertToJavaListUsingTypeToken() { + Gson gson = new Gson(); + TypeToken> typeTokenForListOfStudents = new TypeToken>(){}; + TypeToken> typeTokenForListOfSchools = new TypeToken>(){}; + List studentsLst = gson.fromJson(jsonArrayOfStudents, typeTokenForListOfStudents.getType()); + List schoolLst = gson.fromJson(jsonArrayOfSchools, typeTokenForListOfSchools.getType()); + assertAll( + () -> studentsLst.forEach(e -> assertTrue(e instanceof Student)), + () -> schoolLst.forEach(e -> assertTrue(e instanceof School)) + ); + } + + @Test + void givenJsonArray_whenListElementTypeDynamic_thenConvertToJavaListUsingTypeTokenFails() { + Gson gson = new Gson(); + List studentsLst = gson.fromJson(jsonArrayOfStudents, new ListWithDynamicTypeElement().getType()); + assertFalse(studentsLst.get(0) instanceof Student); + assertThrows(ClassCastException.class, () -> studentsLst.forEach(e -> assertTrue(e instanceof Student))); + } + + class ListWithDynamicTypeElement { + Type getType() { + TypeToken> typeToken = new TypeToken>(){}; + return typeToken.getType(); + } + } + + @Test + void givenJsonArray_whenListElementTypeDynamic_thenConvertToJavaListUsingGetParameterized() { + Gson gson = new Gson(); + List studentsLst = gson.fromJson(jsonArrayOfStudents, getGenericTypeForListFromTypeTokenUsingGetParameterized(Student.class)); + List schoolLst = gson.fromJson(jsonArrayOfSchools, getGenericTypeForListFromTypeTokenUsingGetParameterized(School.class)); + assertAll( + () -> studentsLst.forEach(e -> assertTrue(e instanceof Student)), + () -> schoolLst.forEach(e -> assertTrue(e instanceof School)) + ); + } + + @Test + void givenJsonArray_whenListElementTypeDynamic_thenConvertToJavaListUsingJsonArray() { + List studentsLst = createListFromJsonArray(jsonArrayOfStudents, Student.class); + List schoolLst = createListFromJsonArray(jsonArrayOfSchools, School.class); + assertAll( + () -> studentsLst.forEach(e -> assertTrue(e instanceof Student)), + () -> schoolLst.forEach(e -> assertTrue(e instanceof School)) + ); + } + + @Test + void givenJsonArray_whenListElementTypeDynamic_thenConvertToJavaListUsingTypeTokenFromGuava() { + Gson gson = new Gson(); + List studentsLst = gson.fromJson(jsonArrayOfStudents, getTypeForListUsingTypeTokenFromGuava(Student.class)); + List schoolLst = gson.fromJson(jsonArrayOfSchools, getTypeForListUsingTypeTokenFromGuava(School.class)); + assertAll( + () -> studentsLst.forEach(e -> assertTrue(e instanceof Student)), + () -> schoolLst.forEach(e -> assertTrue(e instanceof School)) + ); + } + + @Test + void givenJsonArray_whenListElementTypeDynamic_thenConvertToJavaListUsingParameterizedType() { + Gson gson = new Gson(); + List studentsLst = gson.fromJson(jsonArrayOfStudents, ParameterizedTypeImpl.make(List.class, Student.class)); + List schoolLst = gson.fromJson(jsonArrayOfSchools, ParameterizedTypeImpl.make(List.class, School.class)); + assertAll( + () -> studentsLst.forEach(e -> assertTrue(e instanceof Student)), + () -> schoolLst.forEach(e -> assertTrue(e instanceof School)) + ); + } + + Type getGenericTypeForListFromTypeTokenUsingGetParameterized(Class elementClass) { + return TypeToken.getParameterized(List.class, elementClass).getType(); + } + + List createListFromJsonArray(String jsonArray, Type elementType) { + Gson gson = new Gson(); + List list = new ArrayList<>(); + JsonArray array = gson.fromJson(jsonArray, JsonArray.class); + + for(JsonElement element : array) { + T item = gson.fromJson(element, elementType); + list.add(item); + } + return list; + } + + Type getTypeForListUsingTypeTokenFromGuava(Class type) { + return new com.google.common.reflect.TypeToken>() {} + .where(new TypeParameter() {}, type) + .getType(); + } + +}