Джексон, как преобразовать JsonNode в ArrayNode без кастинга?

116

Я меняю свою библиотеку JSON с org.json на Jackson и хочу перенести следующий код:

JSONObject datasets = readJSON(new URL(DATASETS));
JSONArray datasetArray =  datasets.getJSONArray("datasets");

Сейчас в Джексоне у меня есть следующее:

ObjectMapper m = new ObjectMapper();
JsonNode datasets = m.readTree(new URL(DATASETS));      
ArrayNode datasetArray = (ArrayNode)datasets.get("datasets");

Однако мне там не нравится актерский состав, есть ли возможность ClassCastException? Есть ли метод, эквивалентный getJSONArrayin, org.jsonчтобы у меня была правильная обработка ошибок, если это не массив?

Конрад Хёффнер
источник
К сожалению, я не могу использовать полное сопоставление, потому что данные не фиксируют имена полей.
Конрад Хёффнер
1
Если имена полей взяты из ограниченного набора, вы можете определить класс, содержащий все из них, и использовать FAIL_ON_UNKNOWN_PROPERTIESфункцию десериализатора, чтобы просто получить значения NULL, возвращаемые в неиспользуемых полях. Но это, конечно, только вариант, если набор имен полей относительно ограничен.
fvu
Хм, я думаю, что это решение не подходит для моего случая, но я запомню его на случай, если у меня возникнут проблемы с ограниченным набором, который известен заранее!
Конрад Хёффнер

Ответы:

247

Да, дизайн ручного парсера Джексона сильно отличается от других библиотек. В частности, вы заметите, что у JsonNodeнего есть большинство функций, которые вы обычно связываете с узлами массива из других API. Таким образом, вам не нужно приводить ArrayNodeк объекту для использования. Вот пример:

JSON:

{
    "objects" : ["One", "Two", "Three"]
}

Код:

final String json = "{\"objects\" : [\"One\", \"Two\", \"Three\"]}";

final JsonNode arrNode = new ObjectMapper().readTree(json).get("objects");
if (arrNode.isArray()) {
    for (final JsonNode objNode : arrNode) {
        System.out.println(objNode);
    }
}

Вывод:

«Один»
«Два»
«Три»

Обратите внимание на использование isArrayдля проверки того, что узел на самом деле является массивом перед повторением. В проверке нет необходимости, если вы абсолютно уверены в своей структуре данных, но она доступна, если она вам понадобится (и это не отличается от большинства других библиотек JSON).

Восприятие
источник
2
Вы сэкономили мне часы. Спасибо!
Игорь Мораис
Могу ли я узнать, почему "final" используется в строке "for (final JsonNode objNode: arrNode)"?
Энтони Винай
5

В Java 8 это можно сделать так:

import java.util.*;
import java.util.stream.*;

List<JsonNode> datasets = StreamSupport
    .stream(datasets.get("datasets").spliterator(), false)
    .collect(Collectors.toList())
Ори Поповски
источник
1

Есть ли метод, эквивалентный getJSONArray в org.json, чтобы у меня была правильная обработка ошибок, если это не массив?

Это зависит от вашего вклада; то есть материал, который вы получаете по URL. Если значение атрибута datasets является ассоциативным массивом, а не простым массивом, вы получитеClassCastException .

Но опять же, правильность вашей старой версии также зависит от ввода. В ситуации, когда ваша новая версия выдает a ClassCastException, старая версия выдает JSONException. Ссылка: http://www.json.org/javadoc/org/json/JSONObject.html#getJSONArray(java.lang.String)

Стивен С
источник
Хорошо, я мог просто поймать ClassCastException, спасибо! На мой вкус, это немного менее элегантно, чем наличие определенного исключения JsonException, но если это невозможно, в противном случае это все еще хорошо.
Конрад Хёффнер
0

Я предполагаю, что в конце дня вы захотите использовать данные в ArrayNode, повторяя их. Для этого:

Iterator<JsonNode> iterator = datasets.withArray("datasets").elements();
while (iterator.hasNext()) 
        System.out.print(iterator.next().toString() + " "); 

или если вам нравятся потоки и лямбда-функции:

import com.google.common.collect.Streams;
Streams.stream(datasets.withArray("datasets").elements())
    .forEach( item -> System.out.print(item.toString()) )
Wildhammer
источник