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


116

Я змінюю свою бібліотеку JSON з org.json на Джексон і хочу перенести наступний код:

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? Чи є спосіб еквівалента getJSONArrayв org.jsonтак що у мене є належна обробка помилок в разі , якщо це не масив?


На жаль, я не можу використовувати повне відображення, оскільки дані не фіксують імен полів.
Конрад Геффнер

1
Якщо імена полів походять з обмеженого набору, можливо, ви захочете визначити клас із усіма ними та скористатися FAIL_ON_UNKNOWN_PROPERTIESфункцією десеріалізатора, щоб просто повернути нулі у невикористаних полях. Але це, звичайно, лише варіант, якщо набір полів відносно обмежений.
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-адреси. Якщо значення атрибута "набори даних" є асоціативним масивом, а не простим масивом, ви отримаєте a ClassCastException.

Але знову ж таки, правильність вашої старої версії також залежить від інформації. У тій ситуації, коли ваша нова версія кидає 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()) )
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.