Вопрос по java, generics, gson – Как я могу использовать Google GSON для десериализации массива JSON в коллекцию универсального типа? [Дубликат]

11

На этот вопрос уже есть ответ:

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

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

public Collection<Project> getProjects() {
        String json = getJsonData(methods.get(Project.class)); //Gets a json list, ie [1, 2, 3, 4]
        Gson gson = new Gson();
        Type collectionType = new TypeToken<Collection<Project>>() {}.getType();
        return gson.fromJson(json, collectionType);
    }

Естественно, я попытался абстрагировать его, используя дженерики Java.

/*
     * Deserialize a json list and return a collection of the given type.
     * 
     * Example usage: getData(AccountQuota.class) -> Collection<AccountQuota>
     */
    @SuppressWarnings("unchecked")
    public <T> Collection<T> getData(Class<T> cls) {
        String json = getJsonData(methods.get(cls)); //Gets a json list, ie [1, 2, 3, 4]
        Gson gson = new Gson();
        Type collectionType = new TypeToken<Collection<T>>(){}.getType();
        return (Collection<T>) gson.fromJson(json, collectionType);
}

Общая версия кода не совсем работает.

public void testGetItemFromGetData() throws UserLoginError, ServerLoginError {
            Map<String,String> userData = GobblerAuthenticator.authenticate("[email protected]", "mypassword");
            String client_key = userData.get("client_key");
            GobblerClient gobblerClient = new GobblerClient(client_key);
            ArrayList<Project> machines = new ArrayList<Project>();
            machines.addAll(gobblerClient.getData(Project.class));
            assertTrue(machines.get(0).getClass() == Project.class);
            Log.i("Machine", gobblerClient.getData(Project.class).toString());
        }


java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.gobbler.synchronization.Machine
at com.gobblertest.GobblerClientTest.testGetItemFromGetData(GobblerClientTest.java:53)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:545)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551)

Рассматриваемый класс:

import java.util.Map;


public class Project {
    private int total_bytes_stored;
    private String name;
    private String user_data_guid;
    private int seqnum;
    private String guid;
    private Map current_checkpoint;
    private Map<String, String> upload_folder;
    // TODO: schema_version
    private boolean deleted;
    // TODO: download_folders

    public Project() {} // No args constructor used for GSON
}

Я не совсем знаком со всеми деталями дженериков Java или внутренних компонентов GSON, и мой поиск не был особенно информативным. Здесь есть куча вопросов по SO, но большинство из них относится к реализации методов, подобных оригинальному, который я имел И GSON docs не похоже на этот конкретный случай. Итак, еще раз, как я могу использовать Google GSON для десериализации массива JSON в коллекцию универсального типа?

Ваш Ответ

4   ответа
8

ты не можешь. Универсальный тип был стерт компилятором, и у Gson нет возможности узнать, какие у вас типы или каковы их поля.

Рад слышать, если просто дать заключение. Я понимаю, что тема, вероятно, немного сложна, но не могли бы вы объяснить, почему вкратц Ceasar Bautista
На самом деле вы можете проверить ответ @jevon Stackoverflow.com / а / 12576189/608805 raukodraug
Для обратной совместимости и простоты реализации JVM не знает типы параметров типа во время выполнения. Обычно вам все равно, но это одна из ситуаций, в которой вы это делаете. В Интернете есть много статей о «стирании Java», в которых есть больше деталей. Jesse Wilson
33

Из руководства пользователя Gson:

Вы можете решить эту проблему, указав правильный параметризованный тип для вашего универсального типа. Вы можете сделать это с помощью класса TypeToken.

Это значит, что теоретически вы можете обернуть свою Карту так:

Type t = new TypeToken<Map<String,Machine>>() {}.getType();
Map<String,Machine> map = (Map<String,Machine>) new Gson().fromJson("json", t);

Или (в моем случае) мне пришлось обернуть список так:

Type t = new TypeToken<List<SearchResult>>() {}.getType();
List<SearchResult> list = (List<SearchResult>) new Gson().fromJson("json", t);

for (SearchResult r : list) {
  System.out.println(r);
}

(Я получаю исключение "java.lang.ClassCastException: com.google.gson.internal.StringMap не может быть приведен к my.SearchResult".)

Да, у меня все отлично работает. Но при запутывании с использованием Proguard приведенного выше кода, получается «анонимный класс (List <SearchResult>) не найден». Я что-то упустил? Еще нужно изменить или добавить код для сохранения анонимного класса в конфигурационном файле proguard . Karthick
5

зом.

public class JSONReader<T> {

    .....

    private Class<T> persistentClass;

    public Class<T> getPersistentClass() {
        if (persistentClass == null) {
            this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        }
        return persistentClass;
    }

    public List<T> read(Reader reader) throws IOException {
        JsonReader jsonReader = new JsonReader(reader);
        List<T> objs = new ArrayList<T>();
        jsonReader.beginArray();      
        while (jsonReader.hasNext()) {
            T obj = (new Gson()).fromJson(jsonReader, getPersistentClass());
            if (logger.isFine()) {
                logger.fine(obj.toString());
            }
            objs.add(obj);
        }
        jsonReader.endArray();
        jsonReader.close();
        return objs;
    }

    .....
}

Вы можете вызвать вышеуказанный метод, используя приведенные ниже операторы (преобразуйте строку Json в StringReader и передайте объект StringReader в метод):

public class Test extends JSONReader<MyClass> {

    .....

    public List<MyClass> parse(String jsonString) throws IOException {
        StringReader strReader = new StringReader(jsonString);
        List<MyClass> objs = read(strReader);
        return objs;
    }

    .....
}

Надеюсь, сработает!

Очень полезная альтернатива! Matthias van der Vlies
1

Map<String, Object> для представления объекта JSON иList<Map<String, Object>> для представления массива объектов JSON.

Очень просто разобрать это с помощью библиотеки GSON (я использовал версию 2.2.4 на момент написания статьи):

import com.google.gson.Gson;

import java.util.List;
import java.util.Map;

public class Test {
    public static void main(String[] args) {
        String json = "{\"name\":\"a name\"}";
        Gson GSON = new Gson();
        Map<String, Object> parsed = GSON.fromJson(json, Map.class);
        System.out.println(parsed.get("name"));

        String jsonList = "[{\"name\":\"a name\"}, {\"name\":\"a name2\"}]";
        List<Map<String, Object>> parsedList = GSON.fromJson(jsonList, List.class);
        for (Map<String, Object> parsedItem : parsedList) {
            System.out.println(parsedItem.get("name"));
        }
    }
}

Похожие вопросы