ฉันจะทำให้ JSON โหลดเข้าสู่ OrderedDict ได้หรือไม่


427

ตกลงฉันสามารถใช้ OrderedDict json.dumpได้ นั่นคือ OrderedDict สามารถใช้เป็นอินพุตไปยัง JSON

แต่มันสามารถใช้เป็นเอาต์พุตได้หรือไม่? ถ้าเป็นเช่นนั้นได้อย่างไร ในกรณีของฉันฉันต้องการที่จะloadเป็น OrderedDict เพื่อให้ฉันสามารถเก็บคำสั่งของคีย์ในไฟล์

หากไม่เป็นเช่นนั้นจะมีวิธีแก้ปัญหาบางอย่าง?


ไม่เคยพยายามที่จะรักษาความสงบแม้ว่าฉันจะได้เห็นว่ามันจะมีประโยชน์อย่างแน่นอน
feathj

1
ใช่ในกรณีของฉันฉันกำลังเชื่อมช่องว่างระหว่างภาษาและแอปพลิเคชันต่าง ๆ และ JSON ทำงานได้ดีมาก แต่การเรียงลำดับของคีย์เป็นปัญหาเล็กน้อย คงจะดีมากถ้ามีการติ๊กjson.loadเพื่อใช้ OrderedDicts แทน Dicts ใน Python
c00kiemonster

3
ข้อมูลจำเพาะ JSON กำหนดประเภทของวัตถุว่ามีคีย์ที่ไม่ได้เรียงลำดับ ... การคาดหวังว่าการสั่งซื้อคีย์ที่เฉพาะเจาะจงนั้นเป็นความผิดพลาด
Anentropic

3
การสั่งซื้อคีย์ไม่ได้เป็นข้อกำหนดสำหรับการใช้งานประเภทใด ๆ ส่วนใหญ่เป็นเพียงเพื่อการอ่านของมนุษย์ ถ้าฉันเพียงต้องการให้ json ของฉันพิมพ์สวยฉันไม่คาดว่าคำสั่งเอกสารใด ๆ จะเปลี่ยนแปลงเลย
ผักดอง

5
นอกจากนี้ยังช่วยหลีกเลี่ยงการคอมไพล์ขนาดใหญ่!
Richard Rast

คำตอบ:


609

ใช่คุณสามารถ. โดยการระบุobject_pairs_hookอาร์กิวเมนต์JSONDecoder อันที่จริงนี่เป็นตัวอย่างที่แท้จริงที่ระบุไว้ในเอกสาร

>>> json.JSONDecoder(object_pairs_hook=collections.OrderedDict).decode('{"foo":1, "bar": 2}')
OrderedDict([('foo', 1), ('bar', 2)])
>>> 

คุณสามารถส่งพารามิเตอร์นี้ไปที่json.loads(หากคุณไม่ต้องการอินสแตนซ์ตัวถอดรหัสเพื่อวัตถุประสงค์อื่น) ดังนี้:

>>> import json
>>> from collections import OrderedDict
>>> data = json.loads('{"foo":1, "bar": 2}', object_pairs_hook=OrderedDict)
>>> print json.dumps(data, indent=4)
{
    "foo": 1,
    "bar": 2
}
>>> 

การใช้งานjson.loadทำได้ในลักษณะเดียวกัน:

>>> data = json.load(open('config.json'), object_pairs_hook=OrderedDict)

3
ฉันงุนงง เอกสารบอกว่า object_pairs_hook ถูกเรียกใช้สำหรับตัวอักษรแต่ละตัวที่ถอดรหัสเป็นคู่ เหตุใดจึงไม่สร้าง OrderedDict ใหม่สำหรับแต่ละระเบียนใน JSON
ทิมคีด

3
อืม ... เอกสารเหล่านี้ค่อนข้างเป็นวลีที่คลุมเครือ สิ่งที่พวกเขาหมายถึงว่า "ผลลัพธ์ทั้งหมดของการถอดรหัสคู่ทั้งหมด" จะถูกส่งผ่านตามลำดับเป็นรายการobject_pairs_hookแทนที่จะ "แต่ละคู่จะถูกส่งผ่านไปยัง object_pairs_hook"
SingleNegationElimination

แต่สูญเสียลำดับเดิมของอินพุต json หรือไม่
SIslam

รู้สึกประหลาดใจที่เห็นjson.loadว่าไม่ได้สั่งให้มันเป็นค่าเริ่มต้น แต่ดูเหมือนว่ามันจะสะท้อนให้เห็นถึงสิ่งที่ json ทำเอง - มันไม่มีการ{}เรียงลำดับ แต่[]ใน json นั้นได้รับคำสั่งตามที่อธิบายไว้ที่นี่
cardamom

1
@RandomCertainty ใช่ทุกครั้งที่พบวัตถุ JSON ในขณะที่การแยกวิเคราะห์แหล่งที่มาOrderedDictจะถูกใช้เพื่อสร้างค่าหลามที่เกิดขึ้น
SingleNegationElimination

125

เวอร์ชั่นง่ายสำหรับ Python 2.7+

my_ordered_dict = json.loads(json_str, object_pairs_hook=collections.OrderedDict)

หรือสำหรับ Python 2.4 ถึง 2.6

import simplejson as json
import ordereddict

my_ordered_dict = json.loads(json_str, object_pairs_hook=ordereddict.OrderedDict)

4
Ahhh แต่ไม่รวม object_pairs_hook - ซึ่งเป็นสาเหตุที่คุณยังต้องการ simplejson ใน 2.6 ;)
mjhm

8
ต้องการที่จะทราบว่าsimplejsonและordereddictเป็นห้องสมุดแยกต่างหากที่คุณต้องการติดตั้ง
phunehehe

2
สำหรับ python 2.7+: "import json, collection" ในโค้ด, สำหรับ python2.6- "aptitude install python-pip" และ "order install edict ของ pip" ในระบบ
ZiTAL

นี่เป็นวิธีที่ง่ายกว่าและเร็วกว่าวิธีก่อนหน้ามากขึ้นด้วย JSONDecoder
Natim

ผิดปกติใน pypy ที่ JSON loads('{}', object_pairs_hook=OrderedDict)รวมจะล้มเหลว
Matthew Schinckel

37

ข่าวดี! ตั้งแต่เวอร์ชัน 3.6 การใช้งาน cPython นั้นยังคงรักษาลำดับการแทรกของพจนานุกรม ( https://mail.python.org/pipermail/python-dev/2016-September/146327.html ) ซึ่งหมายความว่าไลบรารี json ในขณะนี้เพื่อรักษาตามค่าเริ่มต้น สังเกตความแตกต่างของพฤติกรรมระหว่างไพ ธ อน 3.5 และ 3.6 รหัส:

import json
data = json.loads('{"foo":1, "bar":2, "fiddle":{"bar":2, "foo":1}}')
print(json.dumps(data, indent=4))

ใน py3.5 คำสั่งที่เป็นผลลัพธ์ไม่ได้กำหนด:

{
    "fiddle": {
        "bar": 2,
        "foo": 1
    },
    "bar": 2,
    "foo": 1
}

ในการใช้ cPython ของ python 3.6:

{
    "foo": 1,
    "bar": 2,
    "fiddle": {
        "bar": 2,
        "foo": 1
    }
}

ข่าวดีจริง ๆ คือสิ่งนี้ได้กลายเป็นข้อกำหนดทางภาษาของ python 3.7 (ตรงข้ามกับรายละเอียดการใช้ cPython 3.6+): https://mail.python.org/pipermail/python-dev/2017-Dmbermber/151283 .html

ดังนั้นคำตอบสำหรับคำถามของคุณจะกลายเป็น: อัพเกรดเป็น python 3.6! :)


1
ถึงแม้ว่าผมจะเห็นพฤติกรรมเช่นเดียวกับคุณในตัวอย่างที่กำหนดในการดำเนินงาน CPython ของงูใหญ่ 3.6.4, json.loads('{"2": 2, "1": 1}')กลายเป็น{'1': 1, '2': 2}สำหรับฉัน
fuglede

1
@ fuglede ดูเหมือนว่าจะdict.__repr__เรียงลำดับคีย์ในขณะที่การสั่งซื้อพื้นฐานจะถูกเก็บไว้ ในคำอื่น ๆjson.loads('{"2": 2, "1": 1}').items()คือdict_items([('2', 2), ('1', 1)])แม้ว่ามีrepr(json.loads('{"2": 2, "1": 1}')) "{'1': 1, '2': 2}"
Simon Charette

@SimonCharette หืมมมอาจเป็น; จริง ๆ แล้วฉันไม่สามารถทำซ้ำการสังเกตของฉันเองใน pkgs / main / win-64 :: python-3.6.4-h0c2934d_3 ของ conda ดังนั้นสิ่งนี้จะทดสอบได้ยาก
fuglede

การทำเช่นนี้ไม่ได้ช่วยอะไรมากนักเนื่องจากการเปลี่ยนชื่อคีย์จะทำลายลำดับของคีย์
Hubro

7

คุณสามารถเขียนรายการของคีย์เพิ่มเติมนอกเหนือจากการทิ้ง dict แล้วสร้างใหม่OrderedDictโดยทำซ้ำผ่านรายการได้หรือไม่


1
+1 สำหรับโซลูชันที่ใช้เทคโนโลยีต่ำ ฉันได้ทำไปแล้วเมื่อต้องจัดการกับปัญหาเดียวกันกับ YAML แต่การทำซ้ำนั้นเป็นเรื่องที่ค่อนข้างอ่อนแอ อาจมีเหตุผลที่จะหลีกเลี่ยงการสูญเสียคู่คีย์ - ค่าที่อยู่ใน dict แต่หายไปจากรายการของคีย์การตรึงมันไว้หลังจากรายการทั้งหมดที่สั่งไว้อย่างชัดเจน
Mu Mind

2
โซลูชันที่ใช้เทคโนโลยีระดับต่ำยังรักษาบริบทที่ไม่จำเป็นต้องเก็บรักษาไว้เป็นอย่างอื่นในรูปแบบที่ส่งออก (IOW; มีคนเห็น JSON และไม่มีอะไรที่ระบุว่า "คีย์เหล่านี้ควรอยู่ในลำดับนี้"
เหลืองอำพัน

อะไรเป็นตัวกำหนดว่ารายการของปุ่ม "ทิ้ง" อยู่ในลำดับที่ถูกต้อง? สิ่งที่เกี่ยวกับ dicts ซ้อนกัน? ดูเหมือนว่าทั้งการทุ่มตลาดจะต้องจัดการกับมันและการสร้างใหม่จะต้องทำซ้ำโดยใช้OrdereDicts
martineau

5

นอกเหนือจากการทิ้งรายการคำสั่งของกุญแจข้างพจนานุกรมอีกวิธีหนึ่งที่ใช้เทคโนโลยีระดับต่ำซึ่งมีความได้เปรียบในการพูดอย่างชัดเจนคือการถ่ายโอนข้อมูล (สั่ง) รายการคู่คีย์ - ค่า ordered_dict.items() ; OrderedDict(<list of key-value pairs>)โหลดง่าย สิ่งนี้จัดการกับพจนานุกรมที่เรียงลำดับแม้ว่าข้อเท็จจริงที่ว่า JSON จะไม่มีแนวคิดนี้ (พจนานุกรม JSON ไม่มีคำสั่งซื้อ)

เป็นเรื่องดีที่จะใช้ประโยชน์จากความจริงที่jsonทิ้งคำสั่ง OrderedDict ในลำดับที่ถูกต้อง อย่างไรก็ตามโดยทั่วไปแล้วจะมีความจำเป็นมากและไม่จำเป็นที่จะต้องอ่านพจนานุกรม JSON ทั้งหมดในรูปแบบ OrderedDict (ผ่านการobject_pairs_hookโต้แย้ง) ดังนั้นการแปลงเฉพาะพจนานุกรมที่ต้องสั่งนั้นก็มีเหตุผลเช่นกัน


4

คำสั่ง load ที่ใช้ปกติจะทำงานหากคุณระบุพารามิเตอร์object_pairs_hook :

import json
from  collections import OrderedDict
with open('foo.json', 'r') as fp:
    metrics_types = json.load(fp, object_pairs_hook=OrderedDict)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.