Google Gson - десериализовать список ‹class› объекта? (общий тип)

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

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

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

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

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

Редактировать:

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

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)

Я делаю перехват JsonParseExceptions, и "результат" не равен нулю.

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

  • list Type
    • args = ListOfTypes
      • list = null
      • resolvedTypes = Тип [1]
    • loader = PathClassLoader
    • ownerType0 = ноль
    • ownerTypeRes = ноль
    • rawType = Класс (java.util.ArrayList)
    • rawTypeName = "java.util.ArrayList"

поэтому кажется, что вызов "getClass" не работал должным образом. Какие-либо предложения...?

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

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


person jellyfish    schedule 05.04.2011    source источник
comment
В ответе, на который вы указали, используется TokenType. Вы пробовали такой способ?   -  person Nishant    schedule 05.04.2011
comment
получил тот же намек в качестве ответа. В следующий раз я рассмотрю этот пример подробнее. ;)   -  person jellyfish    schedule 05.04.2011
comment
Можете ли вы попробовать реализовать список в токене типа? Поскольку ваш исходный тип - это список массивов, вы должны попробовать список массивов.   -  person uncaught_exceptions    schedule 06.04.2011


Ответы (14)


Метод десериализации общей коллекции:

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 .

person uncaught_exceptions    schedule 05.04.2011
comment
В новых версиях GSON конструктор TypeToken не является общедоступным, поэтому здесь вы получаете невидимую ошибку конструктора. Что делать в этом случае? - person Pablo; 04.04.2012
comment
При использовании актуальной версии GSON (2.2.4) он снова работает. Вы можете получить доступ к конструктору здесь. - person ; 21.11.2013
comment
мой объект json начинается с объекта, а затем содержит массив объекта, который я хочу { "myObjectArray":[ {....} , {....} , {....} ] }, я создал файл модели для {....}, как мне получить этот общий код коллекции, чтобы не предполагать, что мой корневой элемент является массивом без создания нового вложенного объектный файл - person CQM; 07.02.2014
comment
какой пакет нужен для инструкции type.getType ()? Я получаю. Метод getType () не определен для типа new ArrayList ‹XXXX› () {} - person Jose Ospina; 01.03.2015
comment
Это дает мне ошибку, если я использую только proguard, иначе его работа. любое предложение - person Pratik Butani; 07.05.2015
comment
Требуется следующий импорт --- import java.lang.reflect.Type; import com.google.gson.reflect.TypeToken - person Umair Saleem; 30.06.2015
comment
Это хорошо, если YourClass зафиксирован в коде. Что, если класс появится во время выполнения? - person jasxir; 08.09.2015
comment
то вы, скорее всего, столкнетесь с исключением приведения класса (java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap), когда попытаетесь его использовать - person Jan Rabe; 09.11.2015
comment
Те {} до .getType мне кажутся не-Java. Я даже не могу сказать, что они из себя представляют. Это пустой анонимный класс что ли? - person Fermin Silva; 04.08.2016
comment
@CQM У вас есть два пути: 1 - Просто удалите первый и последний символы из строки, и все готово. 2 - Заставьте класс поместить свой ArrayList ‹Whatever› в этот класс. Теперь просто используйте Whatever.class, Что касается второго аргумента, то все готово. - person steve moretz; 27.03.2019
comment
для котлина val listType = object : TypeToken<ArrayList<YourClass>>() {}.type - person illusionJJ; 27.03.2019

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

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

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

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

ИМХО это намного читабельнее.

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

List<MyClass> mcList = new ArrayList<>(Arrays.asList(mcArray));
person DevNG    schedule 25.06.2013
comment
отлично! Как я могу использовать это с отражением? Я не знаю значение MyClass, и оно будет определяться динамически! - person Amin Sh; 27.12.2013
comment
Примечание: при этом будьте осторожны, чтобы mcList не был полноценным списком. многие вещи работать не будут. - person njzk2; 05.06.2014
comment
Как использовать его с дженериками? T[] yourClassList = gson.fromJson(message, T[].class); // нельзя выбрать из переменной типа - person Pawel Cioch; 21.02.2015
comment
@ njzk2 Что такое полноценный список? - person Mateus Viccari; 05.03.2017
comment
@MateusViccari во время этого комментария mcList в этом ответе был только результатом вызова Arrays.asList. Этот метод возвращает список, в котором большинство, если не все необязательные методы остаются нереализованными и вызывают исключения. Например, вы не можете добавить какой-либо элемент в этот список. Как следует из более позднего редактирования, Arrays.asList имеет ограничения, и превращение его в реальный ArrayList позволяет получить список, который во многих случаях более полезен. - person njzk2; 06.03.2017
comment
не забывайте перед Gson gson = new Gson (); для начинающих - person anshulkatta; 02.09.2017
comment
Если вам нужно создать тип массива во время выполнения для произвольного типа элемента, вы можете использовать Array.newInstance(clazz, 0).getClass(), как описано в ответ Дэвида Вуда. - person Daniel Pryden; 20.03.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);
person Linh    schedule 21.08.2018
comment
TypeToken#getParameterized выглядит лучше, чем взлом с анонимным подклассом - person Nikolay Kulachenko; 10.10.2018
comment
Я скопировал ваш метод как есть, и он не работает: компилятор сообщает. Метод getParameterized (Class ‹List›, Class ‹T›) не определен для типа TypeToken. Я проверил и свою версию Gson (2.8.0), и документацию, и с этой стороны все в порядке ... В итоге я использовал решение @Happier, которое отлично работает - person leguminator; 20.02.2020
comment
@leguminator вы правильно импортировали TypeToken? и вы используете java или kotlin. Я попробую еще раз протестировать - person Linh; 20.02.2020
comment
@PhanVanLinh Совершенно верно: я использую Java и импортировал com.google.gson.reflect.TypeToken и java.lang.reflect.Type. Я удвоил реализацию метода проверки: он объявлен как общедоступный статический TypeToken ‹?› GetParameterized (Type rawType, Type ... typeArguments) - person leguminator; 20.02.2020
comment
Это должно быть приемлемое решение, простое, оно использует Gson API, и вокруг него нет никаких хитростей. +1 - person 4gus71n; 29.08.2020

Обратитесь к этому сообщению. Общий тип 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));
}
person Happier    schedule 16.10.2014
comment
Что такое mEntity.rulePattern? - person Al Lelopath; 07.04.2015
comment
Это просто образец объекта для тестирования. Тебе не нужно об этом заботиться. Используйте метод toList, и все идет хорошо. - person Happier; 08.04.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);
person Roc Boronat    schedule 27.11.2015

Для Котлина просто:

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

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

fun <T> typeOfList(): Type {
    return object : TypeToken<List<T>>() {}.type
}

Затем использовать:

val type = typeOfList<YourMagicObject>()
person Chad Bingham    schedule 25.10.2017
comment
Я использовал ваш код, чтобы создать эту функцию с использованием овеществленных типов: inline fun <reified T> buildType() = object : TypeToken<T>() {}.type!! и вызвать ее с помощью типа списка: buildType<List<YourMagicObject>>() - person coffeemakr; 21.11.2017
comment
@coffeemakr Вам здесь не нужны овеществленные типы. - person Chad Bingham; 22.11.2017
comment
Ой. Но почему вы создаете маркер типа ArrayList в buildType, а также вызываете функцию с универсальным типом? Это опечатка? - Это создаст ArrayList ‹ArrayList ‹YourMagicObject›› - person coffeemakr; 23.11.2017
comment
@coffeemakr а, да. Опечатка - person Chad Bingham; 23.11.2017

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, "[{...}]");
person kayz1    schedule 02.03.2015
comment
Хороший. Но это дублирует DevNGs выше ответ, написанный двумя годами ранее: stackoverflow.com/a/17300003/1339923 (и прочтите этот ответ за предостережения к этому подходу) - person Lambart; 27.09.2017

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

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

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

person jellyfish    schedule 08.04.2011
comment
Если у вас есть список абстрактных классов, вы получите ту же ошибку. Я предполагаю, что это общее сообщение об ошибке GSON: Невозможно создать экземпляр класса. - person Drew; 30.09.2011
comment
Совет по добавлению конструктора помог мне понять, почему у меня были все нулевые значения. У меня были имена полей, такие как To и From в моей JSON-строке, но соответствующие поля в моем объекте были в нижнем регистре, поэтому они были пропущены - person Rune; 19.06.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; 
}
person David Wood    schedule 31.03.2016

Я хочу добавить еще одну возможность. Если вы не хотите использовать 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.

person Apurva Sharma    schedule 14.08.2015

См. Пример 2 для понимания Gson класса «Тип».

Пример 1. В этом deserilizeResturant мы использовали массив Employee [] и получили подробную информацию.

public static void deserializeResturant(){

       String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
       Gson gson = new Gson();
       Employee[] emp = gson.fromJson(empList, Employee[].class);
       int numberOfElementInJson = emp.length();
       System.out.println("Total JSON Elements" + numberOfElementInJson);
       for(Employee e: emp){
           System.out.println(e.getName());
           System.out.println(e.getEmpId());
       }
   }

Пример 2:

//Above deserilizeResturant used Employee[] array but what if we need to use List<Employee>
public static void deserializeResturantUsingList(){

    String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
    Gson gson = new Gson();

    // Additionally we need to se the Type then only it accepts List<Employee> which we sent here empTypeList
    Type empTypeList = new TypeToken<ArrayList<Employee>>(){}.getType();


    List<Employee> emp = gson.fromJson(empList, empTypeList);
    int numberOfElementInJson = emp.size();
    System.out.println("Total JSON Elements" + numberOfElementInJson);
    for(Employee e: emp){
        System.out.println(e.getName());
        System.out.println(e.getEmpId());
    }
}
person Ram Patro    schedule 10.09.2017

Мне понравился ответ kays1, но я не смог его реализовать. Поэтому я построил свою собственную версию, используя его концепцию.

public class JsonListHelper{
    public static final <T> List<T> getList(String json) throws Exception {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        Type typeOfList = new TypeToken<List<T>>(){}.getType();
        return gson.fromJson(json, typeOfList);
    }
}

Использование:

List<MyClass> MyList= JsonListHelper.getList(jsonArrayString);
person mike83_dev    schedule 20.10.2015
comment
Конечно, это не может сработать, поскольку вы пытаетесь использовать T во время компиляции. Это эффективно десериализует список StringMap, не так ли? - person JHH; 04.04.2017

В моем случае ответ @ uncaught_exceptions не сработал, мне пришлось использовать List.class вместо java.lang.reflect.Type:

String jsonDuplicatedItems = request.getSession().getAttribute("jsonDuplicatedItems").toString();
List<Map.Entry<Product, Integer>> entries = gson.fromJson(jsonDuplicatedItems, List.class);
person Andrei    schedule 24.03.2017

Я создал библиотеку GsonUtils для этого случая. Я добавляю это в центральный репозиторий maven.

Map<String, SimpleStructure> expected = new HashMap<>();
expected.put("foo", new SimpleStructure("peperoni"));

String json = GsonUtils.writeValue(expected);

Map<String, SimpleStructure> actual = GsonUtils.readMap(json, String.class, SimpleStructure.class);
person oleg.cherednik    schedule 11.01.2021