Jackson, ¿cómo transformar JsonNode en ArrayNode sin casting?

116

Estoy cambiando mi biblioteca JSON de org.json a Jackson y quiero migrar el siguiente código:

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

Ahora en Jackson tengo lo siguiente:

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

Sin embargo, no me gusta el elenco allí, ¿existe la posibilidad de un ClassCastException? ¿Existe un método equivalente a getJSONArrayin org.jsonpara que tenga un manejo adecuado de errores en caso de que no sea una matriz?

Konrad Höffner
fuente
Desafortunadamente, no puedo usar el mapeo completo porque los datos no fijan los nombres de los campos.
Konrad Höffner
1
Si los nombres de los campos provienen de un conjunto limitado, es posible que desee definir una clase con todos ellos y usar la FAIL_ON_UNKNOWN_PROPERTIESfunción del deserializador para obtener valores nulos devueltos en los campos no utilizados. Pero, por supuesto, solo es una opción si el conjunto de nombres de campo es relativamente limitado.
fvu
Hm, creo que esta solución no encaja mejor en mi caso, pero la recordaré en caso de que tenga un problema con un conjunto limitado que se conoce de antemano.
Konrad Höffner

Respuestas:

247

Sí, el diseño del analizador manual de Jackson es bastante diferente al de otras bibliotecas. En particular, notará que JsonNodetiene la mayoría de las funciones que normalmente asociaría con los nodos de matriz de otras API. Como tal, no es necesario convertir a un ArrayNodeuso. He aquí un ejemplo:

JSON:

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

Código:

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

Salida:

"Uno"
"Dos"
"Tres"

Tenga en cuenta el uso de isArraypara verificar que el nodo es realmente una matriz antes de iterar. La verificación no es necesaria si está absolutamente seguro de la estructura de sus datos, pero está disponible en caso de que la necesite (y esto no es diferente de la mayoría de las otras bibliotecas JSON).

Percepción
fuente
2
Me ahorraste horas. ¡Gracias!
Igor Morais
¿Puedo saber por qué se usa "final" en la línea "para (final JsonNode objNode: arrNode)"?
Anthony Vinay
5

En Java 8 puedes hacerlo así:

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

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

¿Existe un método equivalente a getJSONArray en org.json para que tenga un manejo adecuado de errores en caso de que no sea una matriz?

Depende de su opinión; es decir, las cosas que obtiene de la URL. Si el valor del atributo "conjuntos de datos" es una matriz asociativa en lugar de una matriz simple, obtendrá un ClassCastException.

Pero, de nuevo, la exactitud de su versión anterior también depende de la entrada. En la situación en la que su nueva versión arroje un ClassCastException, la versión anterior arrojará JSONException. Referencia: http://www.json.org/javadoc/org/json/JSONObject.html#getJSONArray(java.lang.String)

Stephen C
fuente
Ah, está bien, entonces podría atrapar una ClassCastException, ¡gracias! Para mi gusto, es un poco menos elegante que tener una JsonException específica, pero si no es posible de otra manera, sigue siendo bueno.
Konrad Höffner
0

Asumiría al final del día que desea consumir los datos en el ArrayNode iterando. Para eso:

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

o si te gustan las transmisiones y las funciones lambda:

import com.google.common.collect.Streams;
Streams.stream(datasets.withArray("datasets").elements())
    .forEach( item -> System.out.print(item.toString()) )
Martillo Salvaje
fuente