GSON: десериализация пользовательских объектов

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

Изменить 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 является правильным способом.

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

Итак, я бы:

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

Ответ 1

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

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

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

  • 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, реализация выглядит так:

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

Ответ 2

Один из вариантов заключается в анализе строки 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 являются частью самого Джсона.

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

Изменить 1

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

Ответ 3

De-Serialize пример JSON в полный объект Example, используйте свойства имени объекта Example для построения строки требуемых вещей, забудьте о объекте Example.

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

Ответ 4

Пока вы транслируете 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 может быть выполнено без необходимости писать и ненужные структуры данных в память. Это может работать, если вы хотите только небольшое подмножество данных - I.e. Вам просто нужны имена людей или 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 для размещения вашего прецедента.

Ответ 5

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

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