Jackson, jak przekształcić JsonNode na ArrayNode bez rzutowania?

116

Zmieniam bibliotekę JSON z org.json na Jackson i chcę przeprowadzić migrację następującego kodu:

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

Teraz w Jackson mam:

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

Jednak nie podoba mi się tam obsada, czy jest taka możliwość ClassCastException? Czy istnieje metoda równoważna getJSONArrayin, org.jsonaby mieć właściwą obsługę błędów w przypadku, gdy nie jest to tablica?

Konrad Höffner
źródło
Niestety nie mogę użyć pełnego mapowania, ponieważ dane nie mają ustalonych nazw pól.
Konrad Höffner
1
Jeśli nazwy pól pochodzą z ograniczonego zestawu, możesz chcieć zdefiniować klasę zawierającą je wszystkie i użyć funkcji deserializatora, FAIL_ON_UNKNOWN_PROPERTIESaby po prostu uzyskać wartości null zwracane w nieużywanych polach. Ale to oczywiście tylko opcja, jeśli zestaw nazw pól jest stosunkowo ograniczony.
fvu
Hm, wydaje mi się, że to rozwiązanie nie pasuje najlepiej w moim przypadku, ale zapamiętam je na wypadek, gdy będę miał problem z znanym z góry limitowanym zestawem!
Konrad Höffner

Odpowiedzi:

247

Tak, projekt parsera ręcznego Jacksona różni się od innych bibliotek. W szczególności zauważysz, że JsonNodema większość funkcji, które zwykle kojarzysz z węzłami tablic z innych interfejsów API. W związku z tym nie musisz przesyłać do pliku, ArrayNodeaby go użyć. Oto przykład:

JSON:

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

Kod:

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);
    }
}

Wynik:

„Jeden”
„Dwa”
„Trzy”

Zwróć uwagę na użycie isArraydo sprawdzenia, czy węzeł jest w rzeczywistości tablicą przed iteracją. Sprawdzenie nie jest konieczne, jeśli masz absolutną pewność co do struktury danych, ale jest dostępne, jeśli tego potrzebujesz (i nie różni się to od większości innych bibliotek JSON).

Postrzeganie
źródło
2
Zaoszczędziłeś mi godziny. Dzięki!
Igor Morais
Czy mogę wiedzieć, dlaczego w wierszu „for (final JsonNode objNode: arrNode)” użyto wyrażenia „final”?
Anthony Vinay
5

W Javie 8 możesz to zrobić tak:

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

List<JsonNode> datasets = StreamSupport
    .stream(datasets.get("datasets").spliterator(), false)
    .collect(Collectors.toList())
Ori Popowski
źródło
1

Czy istnieje metoda równoważna getJSONArray w org.json, aby mieć odpowiednią obsługę błędów w przypadku, gdy nie jest to tablica?

To zależy od twojego wkładu; czyli rzeczy, które pobierasz z adresu URL. Jeśli wartością atrybutu „datasets” jest tablica asocjacyjna, a nie zwykła tablica, otrzymasz rozszerzenie ClassCastException.

Ale z drugiej strony, poprawność starej wersji zależy również od danych wejściowych. W sytuacji, gdy twoja nowa wersja wyrzuca a ClassCastException, wyrzuci stara wersja JSONException. Źródła: http://www.json.org/javadoc/org/json/JSONObject.html#getJSONArray(java.lang.String)

Stephen C.
źródło
Aha, więc mogę po prostu złapać ClassCastException, dzięki! Jak na mój gust jest to trochę mniej eleganckie niż posiadanie konkretnego JsonException, ale jeśli nie jest to możliwe, w przeciwnym razie jest nadal dobre.
Konrad Höffner
0

Zakładam, że na koniec dnia chcesz wykorzystać dane w ArrayNode, wykonując iterację. Za to:

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

lub jeśli lubisz strumienie i funkcje lambda:

import com.google.common.collect.Streams;
Streams.stream(datasets.withArray("datasets").elements())
    .forEach( item -> System.out.print(item.toString()) )
Wildhammer
źródło