Google Gson - десериализовать список <class>объект? (универсальный тип) - PullRequest
390 голосов
/ 05 апреля 2011

Я хочу передать объект списка через Google Gson, но я не знаю, как десериализовать универсальные типы.

То, что я пробовал после просмотра этого (ответ BalusC):

MyClass mc = new Gson().fromJson(result, new List<MyClass>(){}.getClass());

но затем я получаю ошибку в eclipse, говорящую «Тип new List () {} должен реализовывать унаследованный абстрактный метод ...», и если я использую быстрое исправление, я получаю монстра из более чем 20 заглушек метода.

Я почти уверен, что есть более простое решение, но я не могу его найти!

Edit:

Теперь у меня есть

Type listType = new TypeToken<List<MyClass>>()
                {
                }.getType();

MyClass mc = new Gson().fromJson(result, listType);

Тем не менее, я получаю следующее исключение в строке "fromJson":

java.lang.NullPointerException
at org.apache.harmony.luni.lang.reflect.ListOfTypes.length(ListOfTypes.java:47)
at org.apache.harmony.luni.lang.reflect.ImplForType.toString(ImplForType.java:83)
at java.lang.StringBuilder.append(StringBuilder.java:203)
at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:56)
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:88)
at com.google.gson.JsonDeserializationVisitor.visitUsingCustomHandler(JsonDeserializationVisitor.java:76)
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:106)
at com.google.gson.JsonDeserializationContextDefault.fromJsonArray(JsonDeserializationContextDefault.java:64)
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:49)
at com.google.gson.Gson.fromJson(Gson.java:568)
at com.google.gson.Gson.fromJson(Gson.java:515)
at com.google.gson.Gson.fromJson(Gson.java:484)
at com.google.gson.Gson.fromJson(Gson.java:434)

I do перехватывает JsonParseExceptions, и "результат" не равен нулю.

Я проверил listType с помощью отладчика и получил следующее:

  • Тип списка
    • args = ListOfTypes
      • list = null
      • resolvedTypes = Тип [1]
    • loader = PathClassLoader
    • ownerType0 = null
    • ownerTypeRes = null
    • rawType = Class (java.util.ArrayList)
    • rawTypeName = "java.util.ArrayList"

похоже, что вызов getClass не работал должным образом. Любые предложения ...?

Edit2: Я проверил Руководство пользователя Gson . В нем упоминается Исключение времени выполнения, которое должно произойти во время синтаксического анализа универсального типа в Json. Я сделал это «неправильно» (не показано выше), как в примере, но не получил это исключение вообще. Таким образом, я изменил сериализацию, как предложено в руководстве пользователя. Но не помогло.

Edit3: Решено, см. Мой ответ ниже.

Ответы [ 13 ]

869 голосов
/ 05 апреля 2011

Метод десериализации родового набора:

import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;

...

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> yourClassList = new Gson().fromJson(jsonArray, listType);

Поскольку несколько человек в комментариях упоминали об этом, вот объяснение того, как используется класс TypeToken. Конструкция new TypeToken<...>() {}.getType() захватывает тип времени компиляции (между < и >) в объект java.lang.reflect.Type времени выполнения. В отличие от объекта Class, который может представлять только необработанный (стертый) тип, объект Type может представлять любой тип языка Java, включая параметризованную реализацию универсального типа.

Сам класс TypeToken не имеет открытого конструктора, потому что вы не должны создавать его напрямую. Вместо этого вы всегда создаете анонимный подкласс (отсюда {}, который является необходимой частью этого выражения).

Из-за удаления типа класс TypeToken может захватывать только те типы, которые полностью известны во время компиляции. (То есть вы не можете сделать new TypeToken<List<T>>() {}.getType() для параметра типа T.)

Для получения дополнительной информации см. Документацию для TypeToken класса .

.
253 голосов
/ 25 июня 2013

Другой способ - использовать массив в качестве типа, например ::1001.

MyClass[] mcArray = gson.fromJson(jsonString, MyClass[].class);

Таким образом вы избегаете всех хлопот с объектом Type, и если вам действительно нужен список, вы всегда можете преобразовать массив в список следующим образом:

List<MyClass> mcList = Arrays.asList(mcArray);

ИМХО, это гораздо более читабельно.

И чтобы сделать его реальным списком (который можно изменить, см. Ограничения Arrays.asList()), просто выполните следующее:

List<MyClass> mcList = new ArrayList<>(Arrays.asList(mcArray));
27 голосов
/ 16 октября 2014

Обратитесь к этому сообщению. Тип Java в качестве аргумента для GSON

У меня есть лучшее решение для этого. Вот класс-обертка для списка, чтобы обертка могла хранить точный тип списка.

public class ListOfJson<T> implements ParameterizedType
{
  private Class<?> wrapped;

  public ListOfJson(Class<T> wrapper)
  {
    this.wrapped = wrapper;
  }

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

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

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

И тогда код может быть простым:

public static <T> List<T> toList(String json, Class<T> typeClass)
{
    return sGson.fromJson(json, new ListOfJson<T>(typeClass));
}
23 голосов
/ 27 ноября 2015

Wep, еще один способ добиться того же результата. Мы используем его для удобства чтения.

Вместо этого трудно читаемого предложения:

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> list = new Gson().fromJson(jsonArray, listType);

Создайте пустой класс, который расширяет список вашего объекта:

public class YourClassList extends ArrayList<YourClass> {}

И использовать его при разборе JSON:

List<YourClass> list = new Gson().fromJson(jsonArray, YourClassList.class);
19 голосов
/ 21 августа 2018

Поскольку Gson 2.8, мы можем создать утилиту типа

public <T> List<T> getList(String jsonArray, Class<T> clazz) {
    Type typeOfT = TypeToken.getParameterized(List.class, clazz).getType();
    return new Gson().fromJson(jsonArray, typeOfT);
}

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

String jsonArray = ...
List<User> user = getList(jsonArray, User.class);
7 голосов
/ 02 марта 2015
public static final <T> List<T> getList(final Class<T[]> clazz, final String json)
{
    final T[] jsonToObject = new Gson().fromJson(json, clazz);

    return Arrays.asList(jsonToObject);
}

Пример:

getList(MyClass[].class, "[{...}]");
6 голосов
/ 25 октября 2017

Для Kotlin просто:

import java.lang.reflect.Type
import com.google.gson.reflect.TypeToken
...
val type = object : TypeToken<ArrayList<T>>() {}.type

или, вот полезная функция:

fun <T> buildType(): Type {
    return object : TypeToken<ArrayList<T>>() {}.type
}

Затем, чтобы использовать:

val type = buildType<YourMagicObject>()
6 голосов
/ 08 апреля 2011

Поскольку он отвечает на мой первоначальный вопрос, я принял ответ doc_180, но если кто-то снова столкнется с этой проблемой, я также отвечу на 2-ю половину моего вопроса:

Описанная мною ошибка NullPointerError не имеет ничего общего с самим списком, а с его содержимым!

У класса "MyClass" не было конструктора "no args", и ни у одного из них не было своего суперкласса. После того, как я добавил простой конструктор MyClass () в MyClass и его суперкласс, все работало нормально, включая сериализацию и десериализацию List, как было предложено doc_180.

4 голосов
/ 31 марта 2016

Вот решение, которое работает с динамически определенным типом.Хитрость заключается в создании правильного типа массива с помощью Array.newInstance ().

    public static <T> List<T> fromJsonList(String json, Class<T> clazz) {
    Object [] array = (Object[])java.lang.reflect.Array.newInstance(clazz, 0);
    array = gson.fromJson(json, array.getClass());
    List<T> list = new ArrayList<T>();
    for (int i=0 ; i<array.length ; i++)
        list.add(clazz.cast(array[i]));
    return list; 
}
2 голосов
/ 14 августа 2015

Я хочу добавить еще одну возможность. Если вы не хотите использовать TypeToken и хотите преобразовать массив объектов json в ArrayList, то вы можете продолжить следующим образом:

Если ваша структура json имеет вид:

{

"results": [
    {
        "a": 100,
        "b": "value1",
        "c": true
    },
    {
        "a": 200,
        "b": "value2",
        "c": false
    },
    {
        "a": 300,
        "b": "value3",
        "c": true
    }
]

}

и ваша структура класса выглядит так:

public class ClassName implements Parcelable {

    public ArrayList<InnerClassName> results = new ArrayList<InnerClassName>();
    public static class InnerClassName {
        int a;
        String b;
        boolean c;      
    }
}

тогда вы можете разобрать это как:

Gson gson = new Gson();
final ClassName className = gson.fromJson(data, ClassName.class);
int currentTotal = className.results.size();

Теперь вы можете получить доступ к каждому элементу объекта className.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...