Tôi có thể tải JSON vào OrderedDict không?


426

Ok để tôi có thể sử dụng OrderedDict json.dump. Đó là, OrderedDict có thể được sử dụng làm đầu vào cho JSON.

Nhưng nó có thể được sử dụng như một đầu ra? Nếu vậy thì thế nào? Trong trường hợp của tôi, tôi muốn loadvào OrderedDict để tôi có thể giữ thứ tự các khóa trong tệp.

Nếu không, có một số cách giải quyết?


Không bao giờ cố gắng duy trì trật tự, mặc dù tôi chắc chắn có thể thấy nó sẽ hữu ích như thế nào.
feathj

1
Vâng, trong trường hợp của tôi, tôi đang thu hẹp khoảng cách giữa các ngôn ngữ và ứng dụng khác nhau và JSON hoạt động rất tốt. Nhưng thứ tự của các phím là một vấn đề. Sẽ thật tuyệt vời khi có một cách đơn giản để đánh dấu vào json.loadviệc sử dụng OrderedDicts thay vì Dicts trong Python.
c00kiemonster

3
Thông số JSON xác định loại đối tượng là có các khóa không được sắp xếp ... mong đợi thứ tự khóa cụ thể là một lỗi
Anentropic

3
Thứ tự chính thường không cho bất kỳ loại yêu cầu chức năng. Nó chủ yếu chỉ dành cho khả năng đọc của con người. Nếu tôi chỉ muốn chiếc json của mình được in đẹp, tôi không mong đợi bất kỳ thứ tự tài liệu nào sẽ thay đổi.
Dưa chua

5
Nó cũng giúp tránh khác biệt lớn git!
Richard Rast

Câu trả lời:


609

Vâng, bạn có thể. Bằng cách chỉ định object_pairs_hookđối số cho JSONDecoder . Trong thực tế, đây là ví dụ chính xác được đưa ra trong tài liệu.

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

Bạn có thể truyền tham số này cho json.loads (nếu bạn không cần phiên bản Bộ giải mã cho các mục đích khác) như vậy:

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

Sử dụng json.loadđược thực hiện theo cách tương tự:

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

3
Tôi bối rối. Các tài liệu nói rằng object_pairs_hook được gọi cho mỗi chữ được giải mã thành các cặp. Tại sao điều này không tạo ra OrderedDict mới cho mỗi bản ghi trong JSON?
Tim Keat

3
Hmm ... các tài liệu có phần mơ hồ. Điều họ có nghĩa là "toàn bộ kết quả giải mã tất cả các cặp" sẽ được thông qua, theo thứ tự, như một danh sách object_pairs_hook, thay vì "mỗi cặp sẽ được chuyển đến object_pairs_hook"
SingleNegationElimination

Nhưng là mất thứ tự ban đầu của json đầu vào?
SIslam

Thật ngạc nhiên khi thấy rằng json.loadnó không giữ nó theo thứ tự theo mặc định, nhưng có vẻ như nó chỉ phản ánh những gì json làm - {}không có thứ tự, nhưng []trong json được đặt hàng như được mô tả ở đây
thảo quả

1
@RandomCerturdy có, mỗi khi gặp một đối tượng JSON trong khi phân tích nguồn, OrderedDictsẽ được sử dụng để xây dựng giá trị python kết quả.
SingleNegationElimination

125

Phiên bản đơn giản cho Python 2.7+

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

Hoặc cho Python 2.4 đến 2.6

import simplejson as json
import ordereddict

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

4
Ahhh, nhưng nó không bao gồm object_pairs_hook - đó là lý do tại sao bạn vẫn cần Simplejson trong 2.6. ;)
mjhm

8
Muốn lưu ý rằng simplejsonordereddictlà các thư viện riêng biệt mà bạn cần cài đặt.
phunehehe

2
cho python 2.7+: "nhập json, bộ sưu tập" trong mã, cho python2.6- "aptitude install python-pip" và "pip install orderdict" trong hệ thống
ZiTAL

Điều này dễ dàng và nhanh chóng hơn nhiều so với phương pháp trước đây với JSONDecoder.
Natim

Điều kỳ lạ là trong pypy, json đi kèm sẽ thất bại loads('{}', object_pairs_hook=OrderedDict).
Matthew Schinckel

37

Một số tin tức tuyệt vời! Kể từ phiên bản 3.6, việc triển khai cPython đã duy trì thứ tự chèn từ điển ( https://mail.python.org/pipermail/python-dev/2016-September/146327.html ). Điều này có nghĩa là thư viện json hiện đang giữ trật tự theo mặc định. Quan sát sự khác biệt trong hành vi giữa python 3.5 và 3.6. Mật mã:

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

Trong py3.5, thứ tự kết quả là không xác định:

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

Trong triển khai cPython của python 3.6:

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

Tin tức thực sự tuyệt vời là điều này đã trở thành một đặc tả ngôn ngữ kể từ python 3.7 (trái ngược với chi tiết triển khai của cPython 3.6+): https://mail.python.org/pipermail/python-dev/2017-December/151283 .html

Vì vậy, câu trả lời cho câu hỏi của bạn bây giờ trở thành: nâng cấp lên python 3.6! :)


1
Mặc dù tôi thấy hành vi tương tự như bạn trong ví dụ đã cho, nhưng trong triển khai CPython của Python 3.6.4, json.loads('{"2": 2, "1": 1}')trở thành {'1': 1, '2': 2}đối với tôi.
fuglede

1
@fuglede có vẻ như dict.__repr__sắp xếp các khóa trong khi thứ tự cơ bản được giữ nguyên. Nói cách khác, json.loads('{"2": 2, "1": 1}').items()dict_items([('2', 2), ('1', 1)])ngay cả khi repr(json.loads('{"2": 2, "1": 1}'))"{'1': 1, '2': 2}".
Simon Charette

@SimonCharette Hừm, có thể; Tôi thực sự không thể tái tạo quan sát của riêng mình trong pkgs / main / win-64 :: python-3.6.4-h0c2934d_3 của conda, vì vậy điều này sẽ rất khó để kiểm tra.
fuglede

Điều này thực sự không giúp được gì nhiều, vì các phím "đổi tên" vẫn sẽ phá hỏng thứ tự của các phím.
Hubro

7

Bạn luôn có thể viết ra danh sách các khóa ngoài việc bỏ lệnh, và sau đó xây dựng lại OrderedDictbằng cách lặp qua danh sách?


1
+1 cho giải pháp công nghệ thấp. Tôi đã làm điều đó khi xử lý vấn đề tương tự với YAML, nhưng việc phải sao chép là hơi khập khiễng, đặc biệt là khi định dạng cơ bản giữ trật tự. Cũng có thể có ý nghĩa để tránh mất các cặp khóa-giá trị trong dict nhưng bị thiếu trong danh sách các khóa, giải quyết chúng sau khi tất cả các mục được sắp xếp rõ ràng.
Mu Mind

2
Giải pháp công nghệ thấp cũng bảo tồn bối cảnh không nhất thiết phải được bảo tồn ở định dạng đã xuất (IOW; ai đó nhìn thấy JSON và không có gì rõ ràng nói rằng "các khóa này sẽ vẫn theo thứ tự này" nếu chúng thao tác trên nó).
Amber

Điều gì xác định rằng danh sách các khóa "đổ" theo đúng thứ tự? Những gì về dicts lồng nhau? Có vẻ như cả việc bán phá giá sẽ cần phải xử lý việc đó và việc tái thiết sẽ cần phải được thực hiện đệ quy bằng OrdereDicts.
martineau

5

Ngoài việc bỏ danh sách các khóa được sắp xếp cùng với từ điển, một giải pháp công nghệ thấp khác, có ưu điểm là rõ ràng, là kết xuất danh sách (theo thứ tự) các cặp giá trị khóa ordered_dict.items(); tải đơn giảnOrderedDict(<list of key-value pairs>) . Điều này xử lý một từ điển được đặt hàng mặc dù thực tế là JSON không có khái niệm này (từ điển JSON không có thứ tự).

Thật là tốt khi tận dụng thực tế là jsonbỏ OrderedDict theo đúng thứ tự. Tuy nhiên, nói chung là rất nặng nề và không nhất thiết phải đọc tất cả các từ điển JSON dưới dạng OrderedDict (thông qua object_pairs_hookđối số), do đó, một chuyển đổi rõ ràng chỉ các từ điển phải được đặt hàng cũng có ý nghĩa.


4

Lệnh tải thông thường được sử dụng sẽ hoạt động nếu bạn chỉ định tham số 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)
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.