Jackson come trasformare JsonNode in ArrayNode senza casting?


116

Sto cambiando la mia libreria JSON da org.json a Jackson e voglio migrare il codice seguente:

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

Ora a Jackson ho quanto segue:

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

Tuttavia non mi piace il cast lì, c'è la possibilità di un ClassCastException? Esiste un metodo equivalente a in getJSONArrayin org.jsonmodo da avere una corretta gestione degli errori nel caso in cui non sia un array?


Purtroppo non posso utilizzare la mappatura completa perché i dati non fissano i nomi dei campi.
Konrad Höffner

1
Se i nomi dei campi provengono da un insieme limitato, potresti voler definire una classe che li includa tutti e utilizzare la funzionalità del deserializzatore FAIL_ON_UNKNOWN_PROPERTIESper ottenere solo i valori nulli restituiti nei campi inutilizzati. Ma questa è ovviamente solo un'opzione se il nome del campo impostato è relativamente limitato.
fvu

Hm penso che questa soluzione non si adatti meglio al mio caso, ma la ricorderò nel caso avessi un problema con un set limitato che è noto in anticipo!
Konrad Höffner

Risposte:


247

Sì, il design del parser manuale di Jackson è abbastanza diverso dalle altre librerie. In particolare, noterai che JsonNodeha la maggior parte delle funzioni che normalmente assoceresti ai nodi di array da altre API. In quanto tale, non è necessario eseguire il cast su un ArrayNodeper utilizzarlo. Ecco un esempio:

JSON:

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

Codice:

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

Produzione:

"Uno"
"Due"
"Tre"

Notare l'uso di isArrayper verificare che il nodo sia effettivamente un array prima dell'iterazione. Il controllo non è necessario se sei assolutamente sicuro della struttura dei tuoi dati, ma è disponibile se ne hai bisogno (e questo non è diverso dalla maggior parte delle altre librerie JSON).


2
Mi hai risparmiato ore. Grazie!
Igor Morais

Posso sapere perché "final" viene utilizzato nella riga "for (final JsonNode objNode: arrNode)"?
Anthony Vinay

5

In Java 8 puoi farlo in questo modo:

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

List<JsonNode> datasets = StreamSupport
    .stream(datasets.get("datasets").spliterator(), false)
    .collect(Collectors.toList())

1

Esiste un metodo equivalente a getJSONArray in org.json in modo da poter gestire correttamente gli errori nel caso in cui non sia un array?

Dipende dal tuo input; cioè il materiale che recuperi dall'URL. Se il valore dell'attributo "datasets" è un array associativo piuttosto che un array semplice, otterrai un ClassCastException.

Ma poi di nuovo, la correttezza della tua vecchia versione dipende anche dall'input. Nella situazione in cui la tua nuova versione lancia un ClassCastException, la vecchia versione lancerà JSONException. Riferimento: http://www.json.org/javadoc/org/json/JSONObject.html#getJSONArray(java.lang.String)


Ah ok quindi potrei prendere una ClassCastException, grazie! Per i miei gusti è un po 'meno elegante di avere una JsonException specifica ma se non è possibile altrimenti va comunque bene.
Konrad Höffner

0

Presumo che alla fine della giornata si desideri consumare i dati nell'ArrayNode iterandoli. Per quello:

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

o se ti piacciono i flussi e le funzioni lambda:

import com.google.common.collect.Streams;
Streams.stream(datasets.withArray("datasets").elements())
    .forEach( item -> System.out.print(item.toString()) )
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.