GSON: десериализация пользовательских объектов - PullRequest
11 голосов
/ 16 декабря 2011

Хорошо, поэтому я отредактировал вопрос, потому что он не был достаточно ясен.

Редактировать 2 : обновлен файл JSON.

Я использую GSON в приложении для Android, и мне нужно проанализировать JSON-файлы, которые поступают с сервера и являются слишком сложными. Я не хочу, чтобы моя структура объекта была слишком тяжелой, поэтому я хотел бы упростить содержание: , чтобы структура моего объекта не была структурой файла JSON.

Например, если в JSON у меня есть это:

{
    "object1":{
        "attribute1" : "test1",
        "attribute40" : "test40",
        "user":{
            "id":1,
            "name":"foo"
        }
        ,"example":{
            "total":10,
            "list":[
            {
                "tag":"tag1",
                "name":"object name 1",
                "pos":1
            },
            {
                "tag":"tag10",
                "name":"object name 10",
                "pos":10
            }
        ]
        }
    }
    "object2":{
        "attribute1":"test..."
    }
}

Я не хочу сохранять в моей текущей объектной структуре объект Example, который содержит ArrayList и int "total". Но я бы хотел оставить только простую строку со значением "object name 1;object name 2;...".

Кроме того, я хотел бы хранить только идентификатор пользователя, а не полный пользователя, поскольку у меня уже есть полный пользователь, сохраненный где-то еще, с другим вызовом API сервера.

Так что мой класс будет примерно таким:

class Foo{
    int userId;
    String example; //"object name 1;object name 2;..."
    ...
}

Так что я полагаю, что мы можем достичь этого с помощью специального десериализатора, но я не могу найти, как это сделать. Я хотел бы, если возможно, минимизировать память, поэтому я не думаю, что иметь полный пример объекта, а затем использовать его для построения моего String example - правильный путь.

В худшем случае, если это слишком сложно, я хотел бы иметь возможность сохранять хотя бы только список элементов Tag, когда я анализирую Пример объекта: поэтому мне нужен собственный десериализатор для избавления от int total.

Итак, я бы получил:

class Foo{
    int userId;
    ArrayList<Tag> example;
    ...
}

Ответы [ 5 ]

20 голосов
/ 08 января 2012

Я принял ответ, чтобы представить полное решение, разработанное в чате, и соответствовать измененной строке JSON.Код предполагает, что строка json содержит именно (обновленный) JSON из вопроса.Требуется заполнить следующий класс (setter и toString не указаны):

class Object1
{
    private String attribute1;
    private String attribute40;
    private int userId;
    private String  nameList;
}

GSON поддерживает (как большинство других REST-библиотек) три режима:

  • GSON_DOM
    Читает весь JSON через JsonParser.parse() и создает дерево DOM в памяти (доступ к объектной модели).Поэтому это решение подходит для небольших файлов JSON.
  • GSON_STREAM
    Чтение только фрагментов JSON через JsonReader.Код более сложный, но он подходит для больших файлов JSON.Начиная с Android 3.0 Honeycomb, потоковый синтаксический анализатор GSON включен как android.util.JsonReader.
  • GSON_BIND
    Привязка данных напрямую к классам с помощью отражения значительно минимизирует код.GSON допускает смешанный режим, что означает объединение GSON_DOM и GSON_BIND или GSON_STREAM и GSON_BIND, которые должен отображать этот ответ.

Для заполнения класса Object1 через GSON_DOM и GSON_BIND реализация выглядит так:

private static void deserializeViaObjectAccess(final String json)
{
    Gson gson = new Gson();

    // Read the whole JSON into meomory via GSON_DOM
    JsonParser parser = new JsonParser();
    JsonObject object1 = parser.parse(json).getAsJsonObject().getAsJsonObject("object1");

    // map the Object1 class via GSON_BIND
    // (bind common attributes which exist in JSON and as properties in the class)
    // mapper acts as factory
    Object1 result = gson.fromJson(object1, Object1.class);

    // manually read the attribute from the user object
    int userId = object1.getAsJsonObject("user").getAsJsonPrimitive("id").getAsInt();
    result.setUserId(userId);

    // manually read the attributes from the example object
    String names = "";
    JsonArray list = object1.getAsJsonObject("example").getAsJsonArray("list");
    for (int i = 0; i < list.size(); ++i)
    {
        JsonObject entry = list.get(i).getAsJsonObject();
        String name = entry.getAsJsonPrimitive("name").getAsString();

        names = i == 0 ? name : names + "; " + name;
    }
    result.setNameList(names);

    // Output the result
    log.debug(result.toString());
}

Чтобы заполнить класс Object1 через GSON_STREAM и GSON_BIND, реализация выглядит следующим образом:

В настоящий момент это возможно, только когда узел полностью загружен через GSON_BIND или GSON_STREAM.В этом примере необходимо, чтобы сам узел был разделен.Это возможно только в следующей версии 2.2.Я передам код позже, когда появится GSON 2.2. *

2 голосов
/ 07 января 2012

Один из вариантов - проанализировать строку JSON с использованием синтаксического анализатора, встроенного в Gson, как указано подробно здесь . Вы бы сделали что-то вроде этого:

com.google.gson.JsonParser parser = new JsonParser();
JsonObject object = parser.parse(data).getAsJsonObject();
JsonObject example = object.getAsJsonObject("example");
JsonArray list = example.getAsJsonArray("list");

JsonObject и JsonArray являются частью самого Gson.

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

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

Похоже, что вы можете использовать gson.fromJson и для пользовательского класса, а не только для общих типов Java, как указано в этом примере . Таким образом, вы должны проанализировать вашу строку JSON с помощью parse и вызвать fromJson для одного из внутренних объектов или массивов.

1 голос
/ 07 января 2012

Во время потоковой передачи в Jsons через http вы можете просто отказаться от текста и сохранить только свои пользовательские объекты.В этом случае вы будете постоянно отбрасывать ненужную информацию.

While stream not empty  

    Read the next block into a new string

    Deserialize the string to the your object

    Store the object in a myArrayList

Примечание: чтение всего JSON и его потребление в целом, скорее всего, необходимо, если вы хотите, чтобы ваше приложение было устойчивым.Если вы не хотите читать JSON как необработанный символьный поток (я сомневаюсь, что если ваш JSON не слишком большой, что этот ярлык необходим).

Тем не менее, чтение входного потока без наложения и правильная форма JSONтребования, могут быть выполнены без необходимости записи и ненужных структур данных в память.Это может сработать, если вам нужно только небольшое подмножество данных, т.е. вы просто хотите, чтобы имена людей или URL-адреса в JSON.Но он сломается, если вы захотите более сложные структуры данных.Тем не менее:

// пример синтаксического анализа URL-адресов из строк JSON без сохранения всей структуры данных

While input stream is not empty

     String x = nextLine

     If x contains "http"

         myArrayList.add(parseUrl(x)

Заключительные мысли:

Но в конечном итоге REST-запрос Jsons не похож на SQL- вы не можете произвольно и произвольно игнорировать определенные поля. Возможно, если вы действительно хотите использовать Jsons более легкого веса, более естественный подход заключается в том, чтобы сообщить или проверить, может ли ваш поставщик услуг RESt просто расширять параметры типов запросов RESt, чтобы они соответствовали вашему варианту использования.

1 голос
/ 16 декабря 2011

Десериализовать пример JSON в полный объект примера, использовать свойства имени объекта примера для создания строки из того, что вы хотите, забудьте об объекте примера.

Я не совсем понимаю второй вопрос полностью, но если у вас есть полный объект Test1, будут все поля / свойства, тогда вы можете создать объект Test2, который берет поля из Test1, которые он хочет. Например, ваш объект Test2 может принимать Test1 в качестве параметра в своем конструкторе и принимать только те свойства, которые ему нужны, игнорируя остальные.

0 голосов
/ 07 января 2012

Я бы предложил использовать библиотеку Джексона . Он предоставляется с лицензией Apache, поэтому вы можете использовать его для коммерческого использования бесплатно. В учебном пособии посмотрите заглавную "Пример потокового API". Это очень просто, и вы полностью контролируете процесс потоковой передачи. Таким образом, вы можете взять то, что вы хотите, и игнорировать все остальные вещи. Библиотека Джексона разделена на несколько банок. Баночка, которая поддерживает потоковый API, является самой маленькой и не использует никакой другой. Это ответ, я думаю.

Джексон может предоставить еще больше. В этой статье вы можете найти метод для чтения файла JSON на более высоком уровне в виде элементов, но с помощью ПРЕДЫДУЩЕЙ установки, какие объекты вам нужны, а какие нет. Таким образом, вы можете сразу же получить только те элементы, которые вам нужны.

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