Чтение массива в json-io: объект не может быть приведен к списку

Я использую эту библиотеку: https://github.com/jdereg/json-io

Это упрощенный пример того, что я пытаюсь сделать, чтобы воспроизвести ошибку:

import com.cedarsoftware.util.io.JsonReader;

Map args = new HashMap();
args.put(JsonReader.USE_MAPS, true);
List<String> a = (List<String>)JsonReader.jsonToJava("[\"1.1\",\"1.2\"]", args);

Во время выполнения это выдает:

java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class java.util.List ([Ljava.lang.Object; and java.util.List are in module java.base of loader 'bootstrap')

В качестве альтернативы args можно опустить (удалить две средние строки и удалить аргумент args для jsonToJava) с тем же эффектом.

Чтение словарей/карт работает нормально, например, этот код печатает «пример», как и ожидалось:

Map<String,Object> fields = (Map<String,Object>)JsonReader.jsonToJava("{\"example\":true}");
for (Map.Entry<String,Object> field : fields.entrySet()) {
    System.out.println(field.getKey());
}

Это только массивы, которые я не могу понять, как читать. Документация, которую мне удалось найти для библиотеки, довольно кратка и не показывает пример этого. Отслеживая исходный код, чтобы увидеть, каким должен быть объект, я оказываюсь в функции с именем readArray, где она внутренне использует ArrayList: https://github.com/jdereg/json-io/blob/master/src/main/java/com/cedarsoftware/util/io/JsonParser.java#L280. Похоже, то, что я делаю, должно быть приведено к списку (я также пробовал ArrayList, чтобы быть уверенным).

Первая строка не похожа на json, вы пробовали "{\"array\": [\"1.1\",\"1.2\"]}"?

Joakim Danielson 18.03.2019 17:26
[Ljava.lang.Object; — это массив Object, поэтому попробуйте выполнить приведение к Object[] и обрабатывать элементы массива по отдельности. Почему это не массив String[]? Поскольку массивы Json могут содержать что угодно, а синтаксический анализатор json не знает тип элементов, пока все они не будут проанализированы (но ему нужно будет где-то хранить их во время синтаксического анализа, поэтому Object — это самый простой в использовании тип — более сложный синтаксический анализатор может проверить тип всех элементов и построить результирующий массив соответствующим образом, но это довольно много усилий для небольшого возврата, поскольку вам все равно нужно знать тип и приведение).
Thomas 18.03.2019 17:29

@JoakimDanielson Да, я удалил объект упаковки для упрощения. Насколько я могу судить, json.org не говорит, что сообщение JSON должно начинаться с {. Он также отлично разбирается в nodejs с помощью JSON.parse(str). Но я попробовал, потому что тоже не был уверен, нужно ли его заворачивать, да это и не имело значения. Если я заменю «истинное» значение элемента «пример» второго фрагмента кода, я получу ту же проблему.

Luc 18.03.2019 18:15

@ Томас Спасибо за предложение! Завтра на работе попробую и отчитаюсь.

Luc 18.03.2019 18:16

@ Томас прав. Object[] возвращается массив. Если вы хотите контролировать процесс десериализации и предоставить ожидаемый тип, используйте библиотеку Jackson или Gson.

Michał Ziober 18.03.2019 21:31

@ Томас Это работает! Вы хотите опубликовать это как ответ? Я также не уверен, в чем именно разница, особенно потому, что они, похоже, используют ArrayList внутри, поэтому, насколько я понимаю, даже если функция возвращает объект, он должен быть приводимым.

Luc 19.03.2019 09:20
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
6
282
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class java.util.List

Обратите внимание, что [Ljava.lang.Object представляет тип массива для массивов объектов, поэтому это Object[]. Поскольку массивы json могут содержать что угодно, то есть строки, логические значения, числа, объекты или массивы, и в любой комбинации любой парсер JSON будет иметь 3 варианта:

  • Используйте Object в качестве типа элемента массива. Это приведет к Object[], как в вашем случае.
  • Используйте какой-либо тип элемента массива, предоставленный пользователем, т. е. если вы знаете, что массив будет содержать только строки, вы должны сообщить синтаксическому анализатору, что он должен выдать результат String[]. Если массив содержит что-либо, кроме строк, это, скорее всего, приведет к сбою.
  • Сложный синтаксический анализатор может сначала собрать элементы, определить их типы и попытаться использовать наиболее распространенные типы (если все элементы являются строками, это будет String). Однако API не может отразить это, потому что компилятор не знает, что будет содержать json во время выполнения. Следовательно, API может использовать только первые два параметра (во многих библиотеках, таких как Jackon или Gson, оба присутствуют), поэтому пользователю затем придется преобразовать либо массив, либо сами элементы, и в этом случае мало что можно получить, чтобы сделать синтаксический анализатор, который изощрен.

Теперь вы, наверное, спросите, почему синтаксический анализатор возвращает Object[] вместо List<Object>. Это решение, принятое разработчиками этой библиотеки, и я могу только догадываться, каковы причины. Однако сам json знает только массивы, поэтому логическим следствием будет использование массивов Java, если не предоставлена ​​​​другая информация о типе.

«[Ljava.lang.Object представляет тип массива для массивов объектов». Чтобы уточнить, если бы это был просто объект (не массив), ошибка содержала бы Ljava.lang.Object без [? Вот как можно было сказать, в чем дело?

Luc 19.03.2019 12:27

@Luc вывод (тип) для Object будет java.lang.Object, тип для String[] будет [Ljava.lang.String, поэтому [L указывает массив следующего типа (в случае классов или интерфейсов). Дополнительную информацию см. здесь: docs.oracle.com/javase/tutorial/reflect/special/…. В основном результат, который вы получаете, определяется Class.getName().

Thomas 19.03.2019 12:29

Чтобы расширить отличный ответ Томаса, вот решение для удобочитаемого json:

Преобразовать список объектов в json:

public String toJSONList(List<Foo> fooList) {
  Map<String, Object> args = new HashMap<>();
  args.put(JsonWriter.PRETTY_PRINT, true); // just to make sure it looks good
  args.put(JsonWriter.TYPE, false); //disable ugly @type properties
  return JsonWriter.objectToJson(fooList, args);
}

Он генерирует хороший удобочитаемый json со списком:

[
  {
    "id":100,
    "name":"John"
  },
  {
    "id":101,
    "name":"Tirion"
  }
]

Преобразуйте вышеуказанный json обратно в список

public List<Foo> fromJSONList(String json) {
Map<String, Object> args = new HashMap<>();
args.put(JsonReader.USE_MAPS, true);

JsonReader jsonReader = new JsonReader();
List<Foo> result = new ArrayList<>();
Object[] array = (Object[])JsonReader.jsonToJava(json, args);
for (int i=0; i<array.length; i++) {
  //we need to tell what is the type (remember that we have removed it above?)
  ((Map)array[i]).put("@type", "com.something.model.Foo");
  Foo foo =(Foo)jsonReader.jsonObjectsToJava((JsonObject) array[i]);
  result.add(foo);
}

return result;
}

Другие вопросы по теме