Использование 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 для моделирования этого?

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

Ответ 1

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

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

Gson также будет часто десериализовать значение non-String в String, когда его спросят. Применяя это к первому примеру, 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] для объекта приводит к исключению синтаксического анализа. )


Небольшое обновление: мне недавно пришлось десериализовать какой-то неряшливый 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 покрывает обработку десериализации коллекций смешанных типов аналогичным примером, например, в "Сериализация и десериализация коллекции с объектами произвольных типов" раздел.

Ответ 2

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

Вроде:

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

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

A Строка

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

Массив (я думаю) ints.

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

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

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

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