Jackson bagaimana mengubah JsonNode menjadi ArrayNode tanpa casting?

116

Saya mengubah perpustakaan JSON saya dari org.json ke Jackson dan saya ingin memigrasi kode berikut:

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

Sekarang di Jackson saya memiliki yang berikut:

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

Namun saya tidak suka pemeran di sana, apakah ada kemungkinan untuk a ClassCastException? Apakah ada metode yang setara dengan getJSONArrayin org.jsonsehingga saya memiliki penanganan kesalahan yang tepat jika itu bukan array?

Konrad Höffner
sumber
Sayangnya saya tidak dapat menggunakan pemetaan penuh karena data tidak memperbaiki nama bidang.
Konrad Höffner
1
Jika nama bidang berasal dari himpunan terbatas, Anda mungkin ingin menentukan kelas yang menampilkan semuanya, dan menggunakan fitur deserializer FAIL_ON_UNKNOWN_PROPERTIESuntuk mendapatkan null yang dikembalikan di bidang yang tidak digunakan. Tapi itu tentu saja hanya sebuah pilihan jika set nama field relatif terbatas.
fvu
Hm Saya pikir solusi ini tidak sesuai dengan kasus saya, tetapi saya akan mengingatnya jika saya akan memiliki masalah dengan set terbatas yang diketahui sebelumnya!
Konrad Höffner

Jawaban:

247

Ya, desain parser manual Jackson sangat berbeda dari pustaka lain. Secara khusus, Anda akan melihat bahwa JsonNodememiliki sebagian besar fungsi yang biasanya Anda kaitkan dengan node array dari API lain. Dengan demikian, Anda tidak perlu melakukan cast ArrayNodeuntuk menggunakan. Berikut contohnya:

JSON:

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

Kode:

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

Keluaran:

"Satu"
"Dua"
"Tiga"

Perhatikan penggunaan isArrayuntuk memverifikasi bahwa node sebenarnya adalah array sebelum melakukan iterasi. Pemeriksaan tidak diperlukan jika Anda benar-benar yakin dengan struktur data Anda, tetapi tersedia jika Anda membutuhkannya (dan ini tidak berbeda dari kebanyakan pustaka JSON lainnya).

Persepsi
sumber
2
Anda menyelamatkan saya berjam-jam. Terima kasih!
Igor Morais
Bolehkah saya tahu mengapa "final" digunakan di baris "for (final JsonNode objNode: arrNode)"?
Anthony Vinay
5

Di Java 8 Anda bisa melakukannya seperti ini:

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

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

Apakah ada metode yang setara dengan getJSONArray di org.json sehingga saya memiliki penanganan kesalahan yang tepat jika itu bukan array?

Itu tergantung pada masukan Anda; yaitu hal-hal yang Anda ambil dari URL. Jika nilai atribut "dataset" adalah array asosiatif daripada array biasa, Anda akan mendapatkan file ClassCastException.

Tapi sekali lagi, kebenaran versi lama Anda juga tergantung pada masukannya. Dalam situasi di mana versi baru Anda melempar ClassCastException, versi lama akan melempar JSONException. Referensi: http://www.json.org/javadoc/org/json/JSONObject.html#getJSONArray(java.lang.String)

Stephen C
sumber
Ah ok jadi saya bisa menangkap ClassCastException, terima kasih! Untuk selera saya ini sedikit kurang elegan daripada memiliki JsonException tertentu tetapi jika tidak memungkinkan sebaliknya itu masih bagus.
Konrad Höffner
0

Saya akan berasumsi pada akhirnya Anda ingin mengkonsumsi data di ArrayNode dengan mengulanginya. Untuk itu:

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

atau jika Anda menyukai fungsi stream dan lambda:

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