แจ็คสันจะเปลี่ยน JsonNode เป็น ArrayNode โดยไม่ต้องแคสต์ได้อย่างไร


116

ฉันกำลังเปลี่ยนไลบรารี JSON จาก org.json เป็น Jackson และฉันต้องการย้ายรหัสต่อไปนี้:

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

ตอนนี้ใน Jackson ฉันมีสิ่งต่อไปนี้:

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

อย่างไรก็ตามฉันไม่ชอบนักแสดงที่นั่นมีความเป็นไปได้สำหรับ a ClassCastException? มีวิธีการที่เทียบเท่ากับgetJSONArrayในorg.jsonเพื่อที่ฉันต้องจัดการข้อผิดพลาดที่เหมาะสมในกรณีที่ไม่ได้เป็นอาร์เรย์?


น่าเสียดายที่ฉันไม่สามารถใช้การแมปแบบเต็มได้เนื่องจากข้อมูลไม่ได้แก้ไขชื่อฟิลด์
Konrad Höffner

1
หากชื่อฟิลด์มาจากชุดที่ จำกัด คุณอาจต้องการกำหนดคลาสที่มีคุณสมบัติทั้งหมดและใช้FAIL_ON_UNKNOWN_PROPERTIESคุณสมบัติของ deserializer เพื่อรับ null ที่ส่งคืนในฟิลด์ที่ไม่ได้ใช้ แต่แน่นอนว่าเป็นเพียงตัวเลือกเท่านั้นหากชุดชื่อฟิลด์ค่อนข้าง จำกัด
fvu

ฉันคิดว่าวิธีนี้ไม่เหมาะกับกรณีของฉันที่สุด แต่ฉันจะจำไว้เผื่อว่าฉันจะมีปัญหากับชุดที่ จำกัด ซึ่งรู้ล่วงหน้า!
Konrad Höffner

คำตอบ:


247

ใช่การออกแบบตัวแยกวิเคราะห์ด้วยตนเองของ Jackson นั้นค่อนข้างแตกต่างจากไลบรารีอื่น ๆ โดยเฉพาะอย่างยิ่งคุณจะสังเกตเห็นว่าJsonNodeมีฟังก์ชันส่วนใหญ่ที่คุณมักจะเชื่อมโยงกับโหนดอาร์เรย์จาก API อื่น ๆ ดังนั้นคุณไม่จำเป็นต้องแคสต์ArrayNodeเพื่อใช้งาน นี่คือตัวอย่าง:

JSON:

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

รหัส:

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

เอาท์พุต:

"หนึ่ง"
"สอง"
"สาม"

สังเกตการใช้isArrayเพื่อตรวจสอบว่าจริง ๆ แล้วโหนดเป็นอาร์เรย์ก่อนที่จะทำซ้ำ การตรวจสอบไม่จำเป็นหากคุณมั่นใจในโครงสร้างข้อมูลของคุณอย่างแท้จริง แต่คุณจำเป็นต้องใช้การตรวจสอบ (และไม่แตกต่างจากไลบรารี JSON อื่น ๆ ส่วนใหญ่)


2
คุณช่วยฉันได้หลายชั่วโมง ขอบคุณ!
Igor Morais

ฉันขอทราบได้ไหมว่าเหตุใดจึงใช้ "final" ในบรรทัด "สำหรับ (final JsonNode objNode: arrNode)"
Anthony Vinay

5

ใน Java 8 คุณสามารถทำได้ดังนี้:

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

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

1

มีวิธีที่เทียบเท่ากับ getJSONArray ใน org.json หรือไม่เพื่อให้ฉันจัดการข้อผิดพลาดที่เหมาะสมในกรณีที่ไม่ใช่อาร์เรย์

ขึ้นอยู่กับข้อมูลที่คุณป้อน เช่นสิ่งที่คุณดึงมาจาก URL หากค่าของแอตทริบิวต์ "ชุดข้อมูล" เป็นอาร์เรย์ที่เชื่อมโยงไม่ใช่อาร์เรย์ธรรมดาคุณจะได้รับไฟล์ClassCastException.

แต่อีกครั้งความถูกต้องของเวอร์ชันเก่าของคุณก็ขึ้นอยู่กับอินพุตด้วย ในสถานการณ์ที่รุ่นใหม่ของคุณพ่นรุ่นเก่าจะโยนClassCastException JSONExceptionอ้างอิง: http://www.json.org/javadoc/org/json/JSONObject.html#getJSONArray(java.lang.String)


โอเคฉันสามารถจับ ClassCastException ได้ขอบคุณ! สำหรับรสนิยมของฉันมันค่อนข้างหรูหราน้อยกว่าการมี JsonException ที่เฉพาะเจาะจง แต่ถ้าเป็นไปไม่ได้อย่างอื่นก็ยังดี
Konrad Höffner

0

ฉันจะถือว่าในตอนท้ายของวันที่คุณต้องการใช้ข้อมูลใน ArrayNode โดยการทำซ้ำ สำหรับการที่:

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

หรือถ้าคุณเป็นสตรีมและฟังก์ชันแลมบ์ดา:

import com.google.common.collect.Streams;
Streams.stream(datasets.withArray("datasets").elements())
    .forEach( item -> System.out.print(item.toString()) )
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.