Các mục trong đối tượng JSON không đúng thứ tự khi sử dụng, json.dumps '?


156

Tôi đang sử dụng json.dumpsđể chuyển đổi thành json như

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

Kết quả tôi có là:

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

Tôi muốn có các khóa theo thứ tự sau: id, name, múi giờ - nhưng thay vào đó tôi có múi giờ, id, tên.

Làm thế nào tôi nên sửa lỗi này?

Câu trả lời:


243

Cả Python dict(trước Python 3.7) và đối tượng JSON đều là các bộ sưu tập không có thứ tự. Bạn có thể truyền sort_keystham số, để sắp xếp các khóa:

>>> 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}'

Nếu bạn cần một đơn đặt hàng cụ thể; bạn có thể sử dụngcollections.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}'

Kể từ Python 3.6 , thứ tự đối số từ khóa được giữ nguyên và ở trên có thể được viết lại bằng cú pháp đẹp hơn:

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

Xem PEP 468 - Giữ nguyên thứ tự đối số từ khóa .

Nếu đầu vào của bạn được cung cấp dưới dạng JSON thì để duy trì thứ tự (để nhận OrderedDict), bạn có thể vượt qua object_pair_hook, như được đề xuất bởi @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
Khởi xướng của OrderedDict thực sự xấu xí
jean

3
@jean: giá trị ban đầu không liên quan gì OrderedDict(), bạn có thể chuyển một dictđến OrderedDict(), bạn cũng có thể chuyển một danh sách các cặp được đặt hàng dict()- mặc dù thứ tự bị mất trong cả hai trường hợp này.
jfs

Ý tôi là khởi tạo nó khi giữ trật tự, cần gõ nhiều '(' và ')'
jean

@jean: có ordereddict_literalstừ codetransformergói (alpha chất lượng)
JFS

25
Ngoài ra, nếu bạn tải JSON bằng cách sử dụng d = json.load(f, object_pairs_hook=OrderedDict), phần sau json.dump(d)sẽ giữ lại thứ tự của các phần tử gốc.
Fred Yankowski

21

Như những người khác đã đề cập các dict cơ bản là không có thứ tự. Tuy nhiên, có các đối tượng OrderedDict trong python. (Chúng được tích hợp trong những con trăn gần đây hoặc bạn có thể sử dụng: http://code.activestate.com/recipes/576693/ ).

Tôi tin rằng việc triển khai pythons json mới hơn xử lý chính xác các OrderedDicts được xây dựng, nhưng tôi không chắc chắn (và tôi không có quyền truy cập dễ dàng để kiểm tra).

Các triển khai đơn giản của pythons đơn giản không xử lý các đối tượng OrderedDict một cách độc đáo .. và chuyển đổi chúng thành các ký tự thông thường trước khi xuất chúng .. nhưng bạn có thể khắc phục điều này bằng cách làm như sau:

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)

bây giờ sử dụng cái này, chúng tôi nhận được:

>>> 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"}

Đó là khá nhiều như mong muốn.

Một cách khác là chuyên môn hóa bộ mã hóa để sử dụng trực tiếp lớp hàng của bạn và sau đó bạn không cần bất kỳ lệnh chính tả hoặc UnorderedDict trung gian nào.


5
Lưu ý rằng các đối tượng JSON không có thứ tự vẫn còn ; một máy khách JSON có thể đọc định nghĩa đối tượng và hoàn toàn bỏ qua thứ tự của các khóa và hoàn toàn tuân thủ RFC.
Martijn Pieters

4
Martijn là chính xác, điều này không ảnh hưởng đến việc tuân thủ RFC, nhưng chắc chắn nó vẫn có thể có giá trị nếu bạn muốn có một định dạng nhất quán cho JSON của mình (Ví dụ: nếu tệp nằm dưới sự kiểm soát phiên bản hoặc để người đọc dễ dàng hơn hiểu, để thực hiện thứ tự nhập phù hợp với tài liệu của bạn.)
Michael Anderson

3
Trong trường hợp này bạn chỉ cần thiết lập sort_keysđể Truekhi gọi json.dumps(); để ổn định đơn hàng (để kiểm tra, bộ nhớ đệm ổn định hoặc cam kết VCS), các phím sắp xếp là đủ.
Martijn Pieters

7

Thứ tự của một từ điển không có bất kỳ mối quan hệ nào với thứ tự mà nó được định nghĩa. Điều này đúng với tất cả các từ điển, không chỉ những từ được chuyển thành JSON.

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

Thật vậy, từ điển đã bị "đảo lộn" trước khi nó đạt đến json.dumps:

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

6

này, tôi biết câu trả lời này là quá muộn nhưng hãy thêm sort_keys và gán sai cho nó như sau:

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

cái này làm việc cho tôi


4

json.dump () sẽ bảo vệ ordder trong từ điển của bạn. Mở tệp trong trình soạn thảo văn bản và bạn sẽ thấy. Nó sẽ duy trì thứ tự bất kể bạn có gửi OrderedDict hay không.

Nhưng json.load () sẽ mất thứ tự của đối tượng đã lưu trừ khi bạn yêu cầu nó tải vào OrderedDict (), được thực hiện với tham số object_pairs_hook như JFSebastian đã hướng dẫn ở trên.

Mặt khác, nó sẽ mất thứ tự bởi vì trong hoạt động thông thường, nó tải đối tượng từ điển đã lưu vào một lệnh chính quy và một lệnh chính quy thông thường không giữ được mùi của các mục được đưa ra.


Đây thực sự là một sửa chữa tốt hơn vì việc duy trì trật tự trên tải sẽ đảm nhiệm việc sắp xếp thời gian đổ. Cảm ơn câu trả lời này.
Arun R

2

trong JSON, như trong Javascript, thứ tự các khóa đối tượng là vô nghĩa, vì vậy thực sự không quan trọng chúng được hiển thị theo thứ tự nào, nó là cùng một đối tượng.


(và điều tương tự cũng đúng với một Python chuẩn dict)

12
nhưng vì JSON là biểu diễn chuỗi cho đến khi được phân tích cú pháp, nên so sánh chuỗi (chẳng hạn như trong doctests) vẫn có thể yêu cầu thứ tự. Vì vậy, tôi sẽ không nói nó không bao giờ quan trọng.
Michael Scott Cuthbert

1
Mặc dù điều đó đúng với tiêu chuẩn Javascript (tập lệnh ECMA), tất cả các cài đặt đều giữ các khóa (chuỗi) theo thứ tự nguồn.
thebjorn

1
@Paulpro thật sao? cái nào? Tôi biết Chrome đã cố gắng tuân theo tiêu chuẩn ở đây một lần, nhưng đã bị dồn nén khi gửi ( code.google.com/p/v8/issues/detail?id=164 ). Tôi không nghĩ ai sẽ thử điều tương tự sau đó ...
thebjorn 14/12/14

2
@paulpro bạn đang giải quyết chính xác câu hỏi của OP. Tôi muốn thêm, tuy nhiên, có những sử dụng hợp pháp để giữ gìn trật tự. Ví dụ, người ta có thể viết một tập lệnh đọc JSON, áp dụng một số phép biến đổi và viết lại kết quả. Bạn sẽ muốn đơn hàng được bảo toàn để một công cụ tìm khác biệt hiển thị rõ ràng các thay đổi.
Paul Rademacher
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.