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

#java #arrays #json

#java #массивы #json

Вопрос:

Я использую эту библиотеку: 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: класс [Ljava.lang.Объект; не может быть приведен к классу java.util.Список ([Ljava.lang.Object; и java.util.Список находится в модуле java.base загрузчика ‘bootstrap’)

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

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

 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, чтобы быть уверенным).

Комментарии:

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

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

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

4. @Thomas Спасибо за предложение! Я попробую это завтра на работе и отчитаюсь.

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

Ответ №1:

java.lang.ClassCastException: класс [Ljava.lang.Объект; не может быть приведен к классу java.util.Список

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

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

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

Комментарии:

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

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

Ответ №2:

Чтобы расширить отличный ответ Томаса, вот решение для удобочитаемого 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 обратно в List

 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 resu<
}