Вопрос-ответ

Gson TypeToken with dynamic ArrayList item type

Gson TypeToken с динамическим типом элемента ArrayList

У меня есть этот код:

Type typeOfObjectsList = new TypeToken<ArrayList<myClass>>() {}.getType();
List<myClass> objectsList = new Gson().fromJson(json, typeOfObjectsList);

Он преобразует строку JSON в List набор объектов.
Но теперь я хочу иметь это ArrayList с динамическим типом (не только myClass), определенным во время выполнения.

Тип элемента ArrayList будет определен с помощью отражения.

Я пробовал это:

    private <T> Type setModelAndGetCorrespondingList2(Class<T> type) {
Type typeOfObjectsListNew = new TypeToken<ArrayList<T>>() {}.getType();
return typeOfObjectsListNew;
}

Но это не работает. Это исключение:

java.sql.SQLException: Fail to convert to internal representation: {....my json....}
Переведено автоматически
Ответ 1

Начиная с Gson 2.8.0, вы можете использовать TypeToken#getParameterized(Type rawType, Type... typeArguments) для создания TypeToken, тогда getType() должно получиться.

Например:

TypeToken.getParameterized(ArrayList.class, myClass).getType()
Ответ 2

Предлагаемый вами синтаксис неверен. Следующее

new TypeToken<ArrayList<Class.forName(MyClass)>>

недопустимо, потому что вы пытаетесь передать вызов метода, в котором ожидается имя типа.

Следующее

new TypeToken<ArrayList<T>>() 

это невозможно из-за того, как работают дженерики (стирание типов) и отражение. Весь TypeToken хак работает, потому что Class#getGenericSuperclass() выполняет следующее


Возвращает тип, представляющий прямой суперкласс сущности (класса, интерфейса, примитивного типа или void), представленной этим классом.


Если суперкласс является параметризованным типом, возвращаемый объект типа должен точно отражать фактические параметры типа, используемые в исходном коде.


Другими словами, если он увидит ArrayList<T>, это ParameterizedType он вернет, и вы не сможете извлечь значение времени компиляции, которое имела бы переменная типа T.

Type и ParameterizedType являются обоими интерфейсами. Вы можете предоставить экземпляр своей собственной реализации (определить класс, который реализует любой интерфейс и переопределяет его методы) или использовать один из полезных заводских методов, которые TypeToken предоставляются в его последних версиях. Например,

private Type setModelAndGetCorrespondingList2(Class<?> typeArgument) {
return TypeToken.getParameterized(ArrayList.class, typeArgument).getType();
}
Ответ 3

Вариант 1 - реализовать java.lang.reflect.ParameterizedType самостоятельно и передать его в Gson.

private static class ListParameterizedType implements ParameterizedType {

private Type type;

private ListParameterizedType(Type type) {
this.type = type;
}

@Override
public Type[] getActualTypeArguments() {
return new Type[] {type};
}

@Override
public Type getRawType() {
return ArrayList.class;
}

@Override
public Type getOwnerType() {
return null;
}

// implement equals method too! (as per javadoc)
}

Тогда просто:

Type type = new ListParameterizedType(clazz);
List<T> list = gson.fromJson(json, type);

Обратите внимание, что в соответствии с javadoc также должен быть реализован метод equals .

Вариант 2 - (не делайте этого) повторно используйте gson внутри...

Это тоже будет работать, по крайней мере, с Gson 2.2.4.

Type type = com.google.gson.internal.$Gson$Types.newParameterizedTypeWithOwner(null, ArrayList.class, clazz);
Ответ 4

С kotlin вы можете использовать приведенные ниже функции для преобразования (из / в) любого (JSONArray / JSONObject) всего в одной строке без необходимости отправлять TypeToken :-

Преобразуйте любой класс или массив в строку JSON

inline fun <reified T : Any> T?.json() = this?.let { Gson().toJson(this, T::class.java) }

Пример использования :


 val list = listOf("1","2","3")
val jsonArrayAsString = list.json()
//output : ["1","2","3"]

val model= Foo(name = "example",email = "t@t.com")
val jsonObjectAsString = model.json()
//output : {"name" : "example", "email" : "t@t.com"}

Преобразуйте строку JSON в любой класс или массив

inline fun <reified T : Any> String?.fromJson(): T? = this?.let {
val type = object : TypeToken<T>() {}.type
Gson().fromJson(this, type)
}

Пример использования :


 val jsonArrayAsString = "[\"1\",\"2\",\"3\"]"
val list = jsonArrayAsString.fromJson<List<String>>()

val jsonObjectAsString = "{ "name" : "example", "email" : "t@t.com"}"
val model : Foo? = jsonObjectAsString.fromJson()
//or
val model = jsonObjectAsString.fromJson<Foo>()
java reflection arraylist