Использование GSON для анализа массива с несколькими типами

Я хочу использовать GSON для анализа следующего json:

[
    [
        "hello",
        1,
        [2]
    ],
    [
        "world",
        3,
        [2]
    ]
]

Итак, это 1 массив, содержащий 2 массива. 2 внутренних массива сами по себе являются массивами, состоящими из типов String, int, array.

Я не уверен, как я могу использовать классы Java для моделирования массива, который имеет 3 разных типа (String, int, array). Я начинаю с:

// String json just contains the aforementioned json string.

ArrayList<ArrayList<XXX>> data = new ArrayList<ArrayList<XXX>>();

Type arrayListType = new TypeToken<ArrayList<ArrayList<XXX>>>(){}.getType();

data = gson.fromJson(json, arrayListType);

Но что должно быть там, где «XXX»? Я думаю, что это должен быть массив, но это должен быть массив с тремя разными типами данных. Итак, как я могу использовать Java для моделирования этого?

Может ли кто-нибудь помочь? Спасибо.


person Fofx    schedule 21.03.2011    source источник
comment
Код точно такой же, как я получаю его с сервера... к сожалению, я не могу изменить данные, которые я получаю. Конечно, это не делает данные JSON правильными. :П   -  person Fofx    schedule 21.03.2011
comment
Возможный дубликат Gson десериализовать массив JSON с несколькими типами объектов   -  person ed22    schedule 12.09.2017
comment
Это странный, но действительный Json..   -  person Leo Droidcoder    schedule 18.04.2018


Ответы (2)


Gson имеет специальную обработку для десериализации некоторых однокомпонентных массивов в тип, не являющийся массивом. Например, int data = gson.fromJson("[3]", int.class); присвоит данным значение int 3.

Конечно, десериализация однокомпонентного массива в тип, не являющийся массивом, не требуется. Например, предыдущий пример можно десериализовать как int[] data = gson.fromJson("[3]", int[].class);.

Gson также часто десериализует значение, отличное от строки, в строку, когда его об этом просят. Применив это к первому примеру, String data = gson.fromJson("[3]", String.class); работает так же хорошо.

Обратите внимание, что нельзя указать Gson десериализовать первый пример как тип Object. Object data = gson.fromJson("[3]", Object.class); приводит к исключению синтаксического анализа, жалующемуся на то, что [3] не является примитивом.

Применительно к примеру в исходном вопросе выше, если приемлемо рассматривать все значения как строки, тогда десериализация становится простой.

// output:
// hello 1 2 
// world 3 2 

public class Foo
{
  static String jsonInput = 
    "[" +
      "[\"hello\",1,[2]]," +
      "[\"world\",3,[2]]" +
    "]";

  public static void main(String[] args)
  {
    Gson gson = new Gson();
    String[][] data = gson.fromJson(jsonInput, String[][].class);
    for (String[] data2 : data)
    {
      for (String data3 : data2)
      {
        System.out.print(data3);
        System.out.print(" ");
      }
      System.out.println();
    }
  }
}

К сожалению, с Gson мне не удалось найти простой подход к десериализации, который позволял бы «лучше» привязываться к более конкретным и смешанным типам в массиве, поскольку Java не предоставляет синтаксиса для определения массива смешанных типов. Например, предпочтительным типом коллекции в исходном вопросе может быть List<List<String, int, List<int>>>, но это невозможно определить в Java. Итак, вы должны довольствоваться List<List<String>> (or String[][]) или обратиться к подходу с более "ручным" разбором.

(Да, Java допускает объявление типа List<List<Object>>, но Object не является достаточно специфичным типом для осмысленной десериализации. Кроме того, как обсуждалось, попытка десериализации [3] в Object приводит к исключению синтаксического анализа.)


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

// output: 
// [{MyThreeThings: first=hello, second=1, third=[2]}, 
//  {MyThreeThings: first=world, second=3, third=[4, 5]}]

import java.lang.reflect.Type;
import java.util.Arrays;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

public class FooToo
{
  static String jsonInput =
      "[" +
          "[\"hello\",1,[2]]," +
          "[\"world\",3,[4,5]]" +
      "]";

  public static void main(String[] args)
  {
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(MyThreeThings.class, new MyThreeThingsDeserializer());
    Gson gson = gsonBuilder.create();
    MyThreeThings[] things = gson.fromJson(jsonInput, MyThreeThings[].class);
    System.out.println(Arrays.toString(things));
  }
}

class MyThreeThings
{
  String first;
  int second;
  int[] third;

  MyThreeThings(String first, int second, int[] third)
  {
    this.first = first;
    this.second = second;
    this.third = third;
  }

  @Override
  public String toString()
  {
    return String.format(
        "{MyThreeThings: first=%s, second=%d, third=%s}",
        first, second, Arrays.toString(third));
  }
}

class MyThreeThingsDeserializer implements JsonDeserializer<MyThreeThings>
{
  @Override
  public MyThreeThings deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException
  {
    JsonArray jsonArray = json.getAsJsonArray();
    String first = jsonArray.get(0).getAsString();
    int second = jsonArray.get(1).getAsInt();
    JsonArray jsonArray2 = jsonArray.get(2).getAsJsonArray();
    int length = jsonArray2.size();
    int[] third = new int[length];
    for (int i = 0; i < length; i++)
    {
      int n = jsonArray2.get(i).getAsInt();
      third[i] = n;
    }
    return new MyThreeThings(first, second, third);
  }
}

В руководстве пользователя Gson рассматривается обработка десериализации коллекций смешанных типов на примере, аналогичном приведенному в раздел "Сериализация и десериализация коллекции с объектами произвольных типов".

person Programmer Bruce    schedule 02.06.2011

Во-первых, я думаю, что вы можете ошибиться в своем примере выше. Массив, состоящий из трех разных, — это очень необычный подход, если не сказать больше. Вероятно, ваша структура json представляет собой массив, содержащий кортежи. Затем эти кортежи включают массив.

Нравиться:

[
{
    "hello",
    1,
    [2]
},
{
    "world",
    3,
    [2]
}
]

XXX должен быть объектом, содержащим:

Строка

int (или целое число)

Массив (я думаю) целых чисел.

Затем вы создаете массив этих объектов и анализируете в нем json.

Однако ваш json кажется действительно плохо сформированным, так как все члены должны быть названы, например

[
{
    "str":"hello",
    "intVal":1,
    "intArr":[2]
},
{
    "str":"world",
    "intVal":3,
    "intArr":[2]
}
]

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

person uvesten    schedule 21.03.2011
comment
Я тоже так думаю... что если бы JSON был в формате: {str:hello, intVal:1, intArr:[2] }, то я мог бы просто создать свой собственный класс, который моделировал бы эти данные. К сожалению, данные действительно такие, как я опубликовал .... отсюда и моя проблема, и этот пост :) Я попробую ваше предложение и посмотрю, сработает ли оно. Спасибо за ваш ответ. - person Fofx; 21.03.2011
comment
Хорошо, я не уверен, что этот подход к кастингу сработает. Вы предлагаете мне сделать что-то вроде: ArrayList<ArrayList<Object>> data = new ArrayList<ArrayList<Object>>(); Type arrayListType = new TypeToken<ArrayList<ArrayList<Object>>>(){}.getType(); data = gson.fromJson(json, arrayListType); Я понимаю, как это может работать для хранения String или int, но 3-й элемент в массиве является массивом, и если этот массив содержит объект JSON {}, то как бы я это смоделировал? - person Fofx; 21.03.2011
comment
Я получаю следующую ошибку: com.google.gson.JsonParseException: Type information is unavailable, and the target is not a primitive: [2] Значит проблема с 3-м элементом... intArr - person Fofx; 21.03.2011