Jackson como transformar JsonNode em ArrayNode sem lançar?


116

Estou mudando minha biblioteca JSON de org.json para Jackson e quero migrar o seguinte código:

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

Agora em Jackson eu tenho o seguinte:

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

Porém não gosto do elenco aí, existe a possibilidade de um ClassCastException? Existe um método equivalente a getJSONArrayin org.jsonpara que eu tenha o tratamento de erros adequado caso não seja um array?


Infelizmente, não posso usar o mapeamento completo porque os dados não corrigem nomes de campo.
— Konrad Höffner

1
Se os nomes dos campos vierem de um conjunto limitado, você pode definir uma classe com todos eles e usar o FAIL_ON_UNKNOWN_PROPERTIESrecurso do desserializador para obter apenas valores nulos retornados nos campos não utilizados. Mas é claro que isso é apenas uma opção se o conjunto de nomes de campo for relativamente limitado.
— fvu

Hm, acho que essa solução não se encaixa melhor no meu caso, mas vou me lembrar dela no caso de ter um problema com um conjunto limitado que é conhecido de antemão!
— Konrad Höffner

Respostas:


247

Sim, o design do analisador manual de Jackson é bastante diferente de outras bibliotecas. Em particular, você notará que JsonNodepossui a maioria das funções que você normalmente associa a nós de array de outras APIs. Como tal, você não precisa lançar para um ArrayNodepara usar. Aqui está um exemplo:

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

Resultado:

"Um"
"Dois"
"Três"

Observe o uso de isArraypara verificar se o nó é realmente uma matriz antes de iterar. A verificação não é necessária se você estiver absolutamente confiante em sua estrutura de dados, mas ela estará disponível caso você precise (e isso não é diferente da maioria das outras bibliotecas JSON).


2
Você me salvou horas. Obrigado!
— Igor Morais

Posso saber por que "final" é usado na linha "for (final JsonNode objNode: arrNode)"?
— Anthony Vinay

5

No Java 8, você pode fazer assim:

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

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

1

Existe um método equivalente a getJSONArray em org.json para que eu tenha o tratamento de erros adequado caso não seja um array?

Depende de sua entrada; ou seja, as coisas que você busca no URL. Se o valor do atributo "datasets" for uma matriz associativa em vez de uma matriz simples, você obterá a ClassCastException.

Mas, novamente, a exatidão de sua versão antiga também depende da entrada. Na situação em que sua nova versão lançar um ClassCastException, a versão antiga lançará JSONException. Referência: http://www.json.org/javadoc/org/json/JSONObject.html#getJSONArray(java.lang.String)


Ok, então eu poderia pegar uma ClassCastException, obrigado! Para o meu gosto, é um pouco menos elegante do que ter um JsonException específico, mas se não for possível de outra forma, ainda é bom.
— Konrad Höffner

0

Eu presumiria que, no final do dia, você deseja consumir os dados no ArrayNode iterando-o. Por isso:

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

ou se você gosta de streams e funções lambda:

import com.google.common.collect.Streams;
Streams.stream(datasets.withArray("datasets").elements())
    .forEach( item -> System.out.print(item.toString()) )
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.