รายการในวัตถุ JSON นั้นล้าสมัยโดยใช้“ json.dumps”?


157

ฉันใช้json.dumpsในการแปลงเป็น json เช่น

countries.append({"id":row.id,"name":row.name,"timezone":row.timezone})
print json.dumps(countries)

ผลลัพธ์ที่ฉันมีคือ:

[
   {"timezone": 4, "id": 1, "name": "Mauritius"}, 
   {"timezone": 2, "id": 2, "name": "France"}, 
   {"timezone": 1, "id": 3, "name": "England"}, 
   {"timezone": -4, "id": 4, "name": "USA"}
]

ฉันต้องการมีปุ่มตามลำดับต่อไปนี้: id, ชื่อ, เขตเวลา - แต่ฉันมีเขตเวลา, id, ชื่อ

ฉันจะแก้ไขได้อย่างไร

คำตอบ:


244

ทั้ง Python dict(ก่อน Python 3.7) และวัตถุ JSON เป็นคอลเล็กชันที่ไม่ได้เรียงลำดับ คุณสามารถผ่านsort_keysพารามิเตอร์เพื่อเรียงลำดับคีย์:

>>> import json
>>> json.dumps({'a': 1, 'b': 2})
'{"b": 2, "a": 1}'
>>> json.dumps({'a': 1, 'b': 2}, sort_keys=True)
'{"a": 1, "b": 2}'

หากคุณต้องการคำสั่งซื้อเฉพาะ คุณสามารถใช้collections.OrderedDict :

>>> from collections import OrderedDict
>>> json.dumps(OrderedDict([("a", 1), ("b", 2)]))
'{"a": 1, "b": 2}'
>>> json.dumps(OrderedDict([("b", 2), ("a", 1)]))
'{"b": 2, "a": 1}'

ตั้งแต่ Python 3.6คำสั่งอาร์กิวเมนต์ของคำหลักจะถูกเก็บรักษาไว้และสามารถเขียนใหม่ได้โดยใช้ไวยากรณ์ของ nicer:

>>> json.dumps(OrderedDict(a=1, b=2))
'{"a": 1, "b": 2}'
>>> json.dumps(OrderedDict(b=2, a=1))
'{"b": 2, "a": 1}'

ดูPEP 468 - รักษาคำที่ใช้สั่งซื้ออาร์กิวเมนต์

ถ้าป้อนข้อมูลของคุณจะได้รับเป็น JSON แล้วเพื่อรักษาคำสั่งซื้อ (จะได้รับOrderedDict) คุณสามารถผ่านobject_pair_hook, ตามที่แนะนำโดย @Fred Yankowski :

>>> json.loads('{"a": 1, "b": 2}', object_pairs_hook=OrderedDict)
OrderedDict([('a', 1), ('b', 2)])
>>> json.loads('{"b": 2, "a": 1}', object_pairs_hook=OrderedDict)
OrderedDict([('b', 2), ('a', 1)])

2
OrderedDict's init น่าเกลียดจริง ๆ
jean

3
@jean: ค่าเริ่มต้นไม่มีอะไรเกี่ยวข้องกับOrderedDict()คุณสามารถส่งผ่านdictไปยังOrderedDict()คุณสามารถส่งรายการของคู่ที่สั่งซื้อไปได้dict()เช่นกัน - แม้ว่าการสั่งซื้อจะหายไปในทั้งสองกรณีนี้
jfs

ฉันหมายถึงเริ่มต้นเมื่อรักษาคำสั่งต้องการพิมพ์ '(' และ ')' จำนวนมาก
jean

@jean: มีordereddict_literalsจากcodetransformerแพคเกจ (คุณภาพอัลฟา)
jfs

25
นอกจากนี้หากคุณโหลด JSON โดยใช้d = json.load(f, object_pairs_hook=OrderedDict)ภายหลังjson.dump(d)จะรักษาลำดับขององค์ประกอบดั้งเดิม
Fred Yankowski

21

ตามที่คนอื่น ๆ ได้กล่าวถึงคำสั่งพื้นฐานไม่ได้เรียงลำดับ อย่างไรก็ตามมีวัตถุ OrderedDict ในหลาม (มันถูกสร้างขึ้นใน pythons เมื่อเร็ว ๆ นี้หรือคุณสามารถใช้สิ่งนี้: http://code.activestate.com/recipes/576693/ )

ฉันเชื่อว่าการใช้ pythons json รุ่นใหม่จัดการกับ OrderedDicts ในตัวได้อย่างถูกต้อง แต่ฉันไม่แน่ใจ (และฉันไม่สามารถเข้าถึงการทดสอบได้ง่าย)

การใช้งาน pythons simplejson แบบเก่าไม่ต้องจัดการกับวัตถุ OrderedDict เป็นอย่างดี .. และแปลงเป็น dicts ปกติก่อนที่จะแสดงผลออกมา .. แต่คุณสามารถเอาชนะมันได้โดยทำสิ่งต่อไปนี้:

class OrderedJsonEncoder( simplejson.JSONEncoder ):
   def encode(self,o):
      if isinstance(o,OrderedDict.OrderedDict):
         return "{" + ",".join( [ self.encode(k)+":"+self.encode(v) for (k,v) in o.iteritems() ] ) + "}"
      else:
         return simplejson.JSONEncoder.encode(self, o)

ตอนนี้ใช้สิ่งนี้เราจะได้รับ:

>>> import OrderedDict
>>> unordered={"id":123,"name":"a_name","timezone":"tz"}
>>> ordered = OrderedDict.OrderedDict( [("id",123), ("name","a_name"), ("timezone","tz")] )
>>> e = OrderedJsonEncoder()
>>> print e.encode( unordered )
{"timezone": "tz", "id": 123, "name": "a_name"}
>>> print e.encode( ordered )
{"id":123,"name":"a_name","timezone":"tz"}

ซึ่งค่อนข้างสวยตามที่ต้องการ

อีกทางเลือกหนึ่งคือการใช้โปรแกรมเข้ารหัสเพื่อใช้คลาสแถวของคุณโดยตรงจากนั้นคุณไม่จำเป็นต้องใช้ดิจิทระดับกลางหรือ UnorderedDict


5
โปรดทราบว่าวัตถุ JSON ยังไม่ได้เรียงลำดับ ; ไคลเอ็นต์ JSON สามารถอ่านคำจำกัดความของวัตถุและละเว้นคำสั่งของคีย์ทั้งหมดและเป็นไปตาม RFC อย่างสมบูรณ์
Martijn Pieters

4
Martijn ถูกต้องสิ่งนี้ไม่ส่งผลต่อการปฏิบัติตาม RFC แต่แน่นอนว่ายังคงมีประโยชน์หากคุณต้องการมีรูปแบบที่สอดคล้องกันสำหรับ JSON ของคุณ (ตัวอย่างเช่นหากไฟล์อยู่ภายใต้การควบคุมเวอร์ชันหรือเพื่อให้ง่ายขึ้นสำหรับผู้อ่านของมนุษย์ เพื่อให้คำสั่งซื้อตรงกับเอกสารของคุณ)
Michael Anderson

3
ซึ่งในกรณีนี้คุณเพียงแค่ตั้งค่าsort_keysไปTrueเมื่อโทรjson.dumps(); สำหรับความเสถียรของการสั่งซื้อ (สำหรับการทดสอบการแคชที่เสถียรหรือการทำ VCS) คีย์การเรียงลำดับก็เพียงพอแล้ว
Martijn Pieters

7

ลำดับของพจนานุกรมไม่มีความสัมพันธ์ใด ๆ กับลำดับที่กำหนดไว้นี่เป็นความจริงของพจนานุกรมทั้งหมดไม่ใช่เฉพาะที่กลายเป็น JSON

>>> {"b": 1, "a": 2}
{'a': 2, 'b': 1}

อันที่จริงแล้วพจนานุกรมถูก "กลับหัว" ก่อนที่จะถึงjson.dumps:

>>> {"id":1,"name":"David","timezone":3}
{'timezone': 3, 'id': 1, 'name': 'David'}

6

เฮ้ฉันรู้ว่ามันสายเกินไปสำหรับคำตอบนี้ แต่เพิ่ม sort_keys และมอบหมาย false ให้กับมันดังนี้:

json.dumps({'****': ***},sort_keys=False)

สิ่งนี้ใช้ได้สำหรับฉัน


4

json.dump () จะรักษาลำดับของพจนานุกรมของคุณ เปิดไฟล์ในโปรแกรมแก้ไขข้อความแล้วคุณจะเห็น มันจะรักษาคำสั่งซื้อโดยไม่คำนึงว่าคุณจะส่ง OrderedDict

แต่ json.load () จะสูญเสียลำดับของวัตถุที่บันทึกยกเว้นว่าคุณบอกให้โหลดลงใน OrderedDict () ซึ่งทำด้วยพารามิเตอร์ object_pairs_hook ตามที่ JFSebastian สั่งไว้ข้างต้น

มิฉะนั้นมันจะเสียคำสั่งเพราะภายใต้การดำเนินงานปกติมันจะโหลดวัตถุพจนานุกรมที่บันทึกไว้ลงใน Dict ปกติและ Dict ปกติจะไม่รักษากลิ่นของรายการที่ได้รับ


นี่เป็นวิธีแก้ไขที่ดีกว่าเนื่องจากการดูแลรักษาลำดับการโหลดจะดูแลการสั่งซื้อเวลาถ่ายโอนข้อมูล ขอบคุณสำหรับคำตอบนี้
อรุณ R

2

ใน JSON เช่นเดียวกับใน Javascript ลำดับของคีย์วัตถุนั้นไม่มีความหมายดังนั้นมันจึงไม่สำคัญว่ามันจะเรียงตามลำดับอะไรมันเป็นวัตถุเดียวกัน


(และสิ่งเดียวกันก็มีจริงสำหรับ Python มาตรฐานdict)

12
แต่เนื่องจาก JSON เป็นการแทนสตริงจนกว่าจะถูกวิเคราะห์คำการเปรียบเทียบสตริง (เช่นใน doctests) อาจยังคงต้องมีคำสั่ง ดังนั้นฉันจะไม่พูดมันไม่สำคัญ
Michael Scott Cuthbert

1
ในขณะที่เป็นจริงของมาตรฐาน Javascript (สคริปต์ ECMA) การใช้งานทั้งหมดเก็บ (สตริง) คีย์ตามลำดับแหล่งที่มา
thebjorn

1
@ Paulpro จริงเหรอ? อันไหน? ฉันรู้ว่า Chrome พยายามที่จะปฏิบัติตามมาตรฐานที่นี่เพียงครั้งเดียว แต่ชกต่อยในการส่ง ( code.google.com/p/v8/issues/detail?id=164 ) ฉันไม่คิดว่าจะมีใครลองแบบเดียวกันหลังจากนั้น ...
thebjorn

2
@paulpro คุณตอบคำถามของ OP ได้ถูกต้อง อย่างไรก็ตามฉันต้องการเพิ่มว่ามีการใช้อย่างถูกต้องตามกฎหมายเพื่อรักษาความสงบเรียบร้อย ตัวอย่างเช่นหนึ่งอาจเขียนสคริปต์ที่อ่าน JSON ใช้การแปลงบางอย่างและเขียนผลลัพธ์กลับมา คุณต้องการเก็บรักษาลำดับไว้เพื่อให้เครื่องมือ diff จะแสดงการเปลี่ยนแปลงอย่างชัดเจน
Paul Rademacher
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.