โมเดลวัตถุ JSON ที่รวดเร็วและมีประสิทธิภาพพร้อมรองรับการแยกวิเคราะห์และเขียนอย่างมีประสิทธิภาพในรูปแบบที่สอดคล้องกับ JSON
ห้องสมุดนี้ขึ้นอยู่กับที่เก็บ Neslib เท่านั้น มันรวมอยู่ใน submodule กับที่เก็บนี้
จุดเริ่มต้นหลักของไลบรารีนี้คืออินเตอร์เฟส IJsonDocument มันใช้สำหรับการแยกวิเคราะห์การโหลดและการบันทึกเอกสาร JSON และให้การเข้าถึงโมเดลวัตถุ JSON คุณสามารถแยกสตริง JSON ได้ดังนี้:
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.Parse( ' { "Answer" : 42 } ' );
end ;โปรดทราบว่าแตกต่างจากข้อกำหนด JSON อย่างเป็นทางการห้องสมุดนี้ ไม่ จำเป็นต้องใช้คำพูดรอบคีย์พจนานุกรม (ตราบใดที่คีย์ไม่มีช่องว่างหรืออักขระที่ไม่ใช่ตัวระบุอื่น ๆ ) ดังนั้นสิ่งต่อไปนี้ก็ใช้ได้เช่นกัน:
Doc := TJsonDocument.Parse( ' { Answer : 42 } ' ); คุณยังสามารถใช้วิธี Load เพื่อโหลดจากไฟล์หรือสตรีม
ในด้านเอาต์พุตคุณใช้ Save เพื่อบันทึกไปยังไฟล์หรือสตรีมหรือ ToJson เพื่อส่งออกไปยังสตริง JSON
นอกจากนี้คุณยังสามารถสร้างเอกสาร JSON ใหม่ได้ตั้งแต่เริ่มต้นโดยใช้วิธี CreateArray หรือวิธี CreateDictionary :
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.CreateArray;
Doc.Root.Add( 42 );
end ;อย่างที่คุณเห็นในตัวอย่างนี้คุณเข้าถึงโมเดลวัตถุเอกสาร JSON ผ่านคุณสมบัติรูท
หัวใจของโมเดลวัตถุ JSON คือประเภท TJsonValue นี่คือบันทึกที่สามารถเก็บค่า JSON ทุกประเภท
มันมีตัวดำเนินการแปลงโดยนัยต่าง ๆ เพื่อแปลง TJsonValue เป็นประเภทอื่น (Delphi) นอกจากนี้ยังมีวิธีการที่หลากหลาย To* ที่พยายามแปลง TJsonValue แต่ส่งคืนค่าเริ่มต้นที่ให้ไว้หากการแปลงล้มเหลว
คุณ (สามารถ) อย่าสร้างตัวเองของ TJsonValue ; วิธีเดียวที่จะสร้าง TJsonValue คือการเพิ่มค่าให้กับอาร์เรย์ JSON หรือพจนานุกรม:
var
Doc: IJsonDocument;
begin
Doc := TJsonDocument.CreateArray;
Doc.Root.Add( 42 );
end ; ตัวอย่างนี้เพิ่ม TJsonValue (ที่มีค่า 42) ให้กับอาร์เรย์ JSON ในการสร้างพจนานุกรมใหม่คุณใช้วิธี AddArray หรือ AddDictionary แทน:
var
Doc: IJsonDocument;
Dict: TJsonValue;
begin
Doc := TJsonDocument.CreateArray;
Dict := Doc.Root.AddDictionary;
Dict.AddOrSetValue( ' answer ' , 42 );
end ;สิ่งนี้สร้างพจนานุกรมใหม่และเพิ่มลงในอาร์เรย์รูท จากนั้นค่า 42 จะถูกเพิ่มลงในพจนานุกรมนี้ภายใต้ชื่อ 'คำตอบ'
ในการตรวจสอบประเภทของค่าให้ใช้คุณสมบัติ TJsonValue.ValueType หรือหนึ่งในวิธี TJsonValue.Is*
เมื่อพยายามใช้วิธีการเช่น Add (หรือ AddOrSetValue ) กับค่าที่ไม่ใช่อาร์เรย์ (หรือพจนานุกรม) จะมีการยกข้อยกเว้น
อย่างไรก็ตามการเข้าถึงรายการในอาร์เรย์ (โดยใช้คุณสมบัติ Items ) หรือค่าในพจนานุกรม (โดยใช้คุณสมบัติ Values ) จะไม่ส่งผลให้มีข้อยกเว้นแม้ว่าดัชนีอาร์เรย์จะหมดขอบเขต สิ่งนี้ช่วยให้สามารถใช้การเข้าถึงหลายอาร์เรย์/พจนานุกรมเข้าด้วยกันโดยไม่ต้องตรวจสอบความถูกต้องของแต่ละขั้นตอนกลาง ตัวอย่างเช่น:
I := Doc.Root.Items[ 3 ].Values[ ' foo ' ].Values[ ' bar ' ].Items[ 4 ].ToInteger( 0 );สิ่งนี้จะประสบความสำเร็จเสมอ แต่กลับ 0 หากค่าใด ๆ ของค่ากลางไม่สามารถใช้งานได้
อินเทอร์เฟซ IJsonDocument ทำให้ง่ายต่อการอ่านและเขียน JSON ลงในโมเดลวัตถุเอกสาร
อย่างไรก็ตามคุณสามารถเลือกที่จะอ่านหรือเขียน JSON ด้วยตนเองหากคุณต้องการ (เช่นเพื่อหลีกเลี่ยงการโหลดโมเดลวัตถุลงในหน่วยความจำ) คุณสามารถทำได้ด้วยอินเทอร์เฟ IJsonReader และ IJsonWriter ในหน่วย Neslib.Json.IO
อินเทอร์เฟซเหล่านี้เป็นอิสระอย่างสมบูรณ์จากการใช้งาน DOM ใด ๆ และไม่จำเป็นต้องใช้หน่วย Neslib.Json การใช้อินเทอร์เฟซเหล่านี้มีความซับซ้อนมากขึ้นและต้องใช้งานได้มากกว่านี้ ดูหน่วย Neslib.Json.IO สำหรับข้อมูลเพิ่มเติม
นอกจากนี้ยังมีการใช้งาน JsonPath ที่เหมือน XPath ที่คุณสามารถใช้สำหรับการสอบถามเอกสาร JSON
ไม่มีข้อกำหนด JSONPath อย่างเป็นทางการ แต่เวอร์ชันที่ใช้กันอย่างแพร่หลายที่สุดดูเหมือนว่าจะได้รับการพัฒนาโดย Stefan Goessner
jsonpath ดูเหมือน:
$.store.book[0].titleหรือ
$['store']['book'][0]['title'] การเป็นตัวแทนทั้งสองเหมือนกัน: คุณสามารถใช้สัญลักษณ์ DOT ( . ) หรือวงเล็บ ( [] ) เพื่อแสดงถึงเด็กของพจนานุกรม วงเล็บยังสามารถใช้กับดัชนีตัวเลขเพื่อแสดงถึงเด็กของอาร์เรย์ตามดัชนี
JSONPath ใช้คำพูดเดียว (') ภายในวงเล็บ นอกจากนี้เรายังอนุญาตให้ใช้คำพูดสองครั้ง (") เนื่องจากสิ่งเหล่านี้ใช้งานง่ายกว่าในสตริง Delphi
ในระยะสั้น:
$ ระบุรูทตามด้วยศูนย์เด็กหรือมากกว่า ( . หรือ [] ) A $ โดยตัวเองตรงกับเอกสารทั้งหมด* หรือ '*' ) ไวด์การ์ดเพื่อให้เข้ากับเด็กทุกคน ตัวอย่างเช่น $.store.book[*].author จับคู่ผู้แต่งหนังสือทุกเล่มในร้าน. ) สามารถใช้ Double Dot ( .. ) เพื่อค้นหาลูกหลานใด ๆ แทนที่จะเป็นเด็กทันที ตัวอย่างเช่น $..author ตรงกับผู้เขียนทั้งหมดโดยไม่คำนึงถึงความลึก สิ่งนี้เรียกว่าโคตรแบบเรียกซ้ำ$.store.book[0,2,3] ตรงกับหนังสือเล่มแรกที่สามและสี่[Start:End:Step] เพื่อให้ตรงกับชิ้นส่วน (ช่วง) ของเด็ก สิ่งนี้ตรงกับเด็กทุกคนตั้งแต่ดัชนี Start จนถึง End (แต่ไม่รวม) โดยใช้ขนาด Step ที่กำหนด (โดยปกติ 1) ทั้งหมดเป็นทางเลือก แต่อย่างน้อยหนึ่งค่า (และลำไส้ใหญ่) จะต้องได้รับ:Start จากการเริ่มต้นมันจะบอกเป็นค่า 0 ค่าลบแสดงถึงการชดเชยจากส่วนท้ายของอาร์เรย์End ชิ้นส่วนที่แยกออกจากจุดสิ้นสุดของอาร์เรย์ ค่าลบบ่งชี้และชดเชยจากส่วนท้ายของอาร์เรย์Step จะมีความหมายว่าเป็น 1List[2:] ตรงกับองค์ประกอบที่สามและทั้งหมดต่อไปนี้List[-2:] ตรงกับสององค์ประกอบสุดท้ายList[:2] ตรงกับสององค์ประกอบแรกList[:-2] ตรงกับทั้งหมดยกเว้นสององค์ประกอบสุดท้ายList[2:-2] ตรงกับองค์ประกอบทั้งหมด แต่สองและสองคนแรกList[-4:-2] ตรงกับองค์ประกอบที่ 3 และ 4 จากจุดสิ้นสุดList[::2] ตรงกับองค์ประกอบทั้งหมดกับดัชนีสม่ำเสมอJSONPath ยังมีผู้ให้บริการ @ เพื่ออนุญาตการแสดงออกของสคริปต์ที่กำหนดเอง เราไม่สนับสนุนผู้ให้บริการนี้
ตัวอย่างเอกสาร:
{ "store" : {
"book" : [
{ "category" : " reference " ,
"author" : " Nigel Rees " ,
"title" : " Sayings of the Century " ,
"price" : 8.95
},
{ "category" : " fiction " ,
"author" : " J. R. R. Tolkien " ,
"title" : " The Lord of the Rings " ,
"isbn" : " 0-395-19395-8 " ,
"price" : 22.99
}
],
"bicycle" : {
"color" : " red " ,
"price" : 19.95
}
}
}เส้นทางตัวอย่าง:
| การแสดงออก | ผลลัพธ์ |
|---|---|
$ | ตรงกับเอกสารรูท (ค่าเดียว) |
$..* | ตรงกับสมาชิกทั้งหมดในเอกสาร (ค่าจำนวนมาก) |
$.store.book[*].author | ผู้เขียนหนังสือทุกเล่มในร้านค้า |
$..author | ผู้เขียนทั้งหมด |
$.store.* | ทุกสิ่งในร้านค้า (2 เล่มและจักรยาน) |
$.store..price | ราคาของทุกสิ่งในร้านค้า |
$..book[2] | หนังสือเล่มที่สาม |
$..book[-1:] | หนังสือเล่มสุดท้ายตามลำดับ |
$..book[:2] | หนังสือสองเล่มแรก |
JSONPath API นั้นสั้นและเรียบง่าย มันประกอบด้วยบันทึก TJsonPath ด้วยวิธีการเพียงไม่กี่วิธี
สำหรับการจับคู่แบบครั้งเดียวใช้วิธี Match แบบคงที่:
var
Doc: IJsonDocument;
Matches: TArray<TJsonValue>;
begin
Doc := TJsonDocument.Load(...);
Matches := TJsonPath.Match(Doc, ' $.store.book[*].author ' );
end ;หากคุณวางแผนที่จะใช้เส้นทางเดียวกันในเอกสารหลายรายการ (ย่อย) แสดงว่าเร็วกว่าที่จะแยกวิเคราะห์เส้นทางหนึ่งครั้งจากนั้นใช้หลายครั้ง:
var
Doc1, Doc2: IJsonDocument;
Path: TJsonPath;
Matches1, Matches2: TArray<TJsonValue>;
begin
Doc1 := TJsonDocument.Load(...);
Doc2 := TJsonDocument.Load(...);
Path := TJsonPath.Create( ' $.store.book[*].author ' );
Matches1 := Path.Match(Doc1);
Matches2 := Path.Match(Doc2);
end ;นอกจากนี้คุณยังสามารถเรียกใช้เส้นทางบนต้นไม้ย่อย:
var
Doc: IJsonDocument;
Store: TJsonValue;
Matches: TArray<TJsonValue>;
begin
Doc := TJsonDocument.Load(...);
Store := Doc.Root.Values[ ' store ' ];
Matches := TJsonPath.Match(Store, ' $.book[*].author ' );
end ; หากคุณสนใจเพียงนัดเดียว (หรือครั้งแรก) คุณสามารถใช้ MatchSingle แทน:
var
Doc: IJsonDocument;
Match: TJsonValue;
begin
Doc := TJsonDocument.Load(...);
if (TJsonPath.MatchSingle(Store, ' $.book[*] ' , Match)) then
...
end ; การจัดการหน่วยความจำทั้งหมดในไลบรารี JSON นี้เป็นไปโดยอัตโนมัติ อินเทอร์เฟซ IJsonDocument เป็นเจ้าของทั้งหมดของ TJsonValue และทำลายพวกเขาเมื่อเอกสารถูกทำลาย (ออกนอกขอบเขต)
สิ่งเดียวที่คุณต้องระวังคือคุณไม่ควรใช้บันทึก tjsonvalue ใด ๆ อีกต่อไปหลังจากที่เอกสารถูกทำลาย การทำเช่นนี้จะนำไปสู่พฤติกรรมที่ไม่ได้กำหนดและอาจเกิดปัญหา
คุณสามารถปรับแต่งพฤติกรรมบางอย่างโดยใช้การกำหนดเงื่อนไขเหล่านี้:
JSON_UTF8 : ใช้ UTF8String แทน String ทุกที่ สตริงทั้งหมดจะถือว่าเป็นสตริง UTF-8 8 บิตแทนสตริง Unicode 16 บิต สิ่งนี้จะช่วยลดการใช้หน่วยความจำและเพิ่มความเร็วในการแยกวิเคราะห์เล็กน้อย อย่างไรก็ตามนี่หมายความว่าคุณจะต้องใช้ไลบรารี JSON นี้กับ UTF8Strings เช่นกันมิฉะนั้น Delphi จะแปลงโดยปริยายระหว่าง unicode strings และ UTF8strings ซึ่งอาจส่งผลกระทบต่อประสิทธิภาพJSON_STRING_INTERNING : เพื่อเปิดใช้งานการแทรกแซงสตริงสำหรับคีย์พจนานุกรม สิ่งนี้จะช่วยลดการใช้หน่วยความจำในกรณีที่ใช้คีย์เดียวกันหลายครั้ง (ซึ่งเป็นเรื่องปกติเมื่อ JSON ถูกส่งออกจากฐานข้อมูล) แต่ช้าลงเล็กน้อย หน่วย neslib.json ประกาศประเภท JsonString เป็นทั้ง String หรือ UTF8String ขึ้นอยู่กับ JSON_UTF8 กำหนด อย่างไรก็ตามนี่ไม่ได้หมายความว่า คุณ ต้องใช้ JsonString เช่นกัน หากคุณไม่สนใจเกี่ยวกับการกำหนด JSON_UTF8 คุณสามารถใช้สตริงปกติกับไลบรารีนี้ได้
neslib.json ได้รับใบอนุญาตภายใต้ใบอนุญาต BSD ที่เรียบง่าย
ดู License.txt สำหรับรายละเอียด