Làm cách nào để có được các đối tượng chuỗi thay vì Unicode từ JSON?


276

Tôi đang sử dụng Python 2 để phân tích JSON từ mã hóa ASCII các tệp văn bản .

Khi tải những tập tin này với một trong hai jsonhoặc simplejson, tất cả các giá trị chuỗi của tôi được đúc để Unicode đối tượng thay vì đối tượng chuỗi. Vấn đề là, tôi phải sử dụng dữ liệu với một số thư viện chỉ chấp nhận các đối tượng chuỗi. Tôi không thể thay đổi các thư viện cũng như cập nhật chúng.

Có thể lấy các đối tượng chuỗi thay vì Unicode?

Thí dụ

>>> import json
>>> original_list = ['a', 'b']
>>> json_list = json.dumps(original_list)
>>> json_list
'["a", "b"]'
>>> new_list = json.loads(json_list)
>>> new_list
[u'a', u'b']  # I want these to be of type `str`, not `unicode`

Cập nhật

Câu hỏi này đã được hỏi từ lâu , khi tôi bị mắc kẹt với Python 2 . Một giải pháp dễ dàng và sạch sẽ cho ngày hôm nay là sử dụng phiên bản Python gần đây - tức là Python 3 và chuyển tiếp.


1
Không có vấn đề gì với Python3, loại vật phẩm trong new_list làstr
Đi MYWay

1
Python 3k không phải là 'phiên bản gần đây của Python', nó chỉ là một nhánh thay thế.
dùng2589273

11
Thật kỳ lạ khi thấy nhận xét như vậy vào tháng 12 năm 2017 - Python 2 không được chấp nhận và sẽ không có bảo trì nào xảy ra sau ngày 1 tháng 1 năm 2020, tức là chưa đầy 2 năm: pythonclock.org
Zaar Hai

1
@ZaarHai Rất nhiều người bị mắc kẹt trong Python 2 trái với ý muốn của họ. Có nhiều ứng dụng nhúng phiên bản Python của riêng họ để tự động hóa và tạo kịch bản để mọi người phải sử dụng nó cho đến khi nhà cung cấp cập nhật (Tôi đang nhìn bạn Maya, Houdini, Nuke ..)
Geordie

1
@Geordie Tôi chắc chắn biết và hiểu điều đó. Nhận xét của tôi là về thuật ngữ - Python không phải là một "nhánh thay thế", nhưng đáng tiếc là thiếu sự thay thế (ý định chơi chữ) cho những người bị mắc kẹt với nó.
Zaar Hai

Câu trả lời:


101

Một giải pháp với object_hook

import json

def json_load_byteified(file_handle):
    return _byteify(
        json.load(file_handle, object_hook=_byteify),
        ignore_dicts=True
    )

def json_loads_byteified(json_text):
    return _byteify(
        json.loads(json_text, object_hook=_byteify),
        ignore_dicts=True
    )

def _byteify(data, ignore_dicts = False):
    # if this is a unicode string, return its string representation
    if isinstance(data, unicode):
        return data.encode('utf-8')
    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item, ignore_dicts=True) for item in data ]
    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict) and not ignore_dicts:
        return {
            _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
            for key, value in data.iteritems()
        }
    # if it's anything else, return it in its original form
    return data

Ví dụ sử dụng:

>>> json_loads_byteified('{"Hello": "World"}')
{'Hello': 'World'}
>>> json_loads_byteified('"I am a top-level string"')
'I am a top-level string'
>>> json_loads_byteified('7')
7
>>> json_loads_byteified('["I am inside a list"]')
['I am inside a list']
>>> json_loads_byteified('[[[[[[[["I am inside a big nest of lists"]]]]]]]]')
[[[[[[[['I am inside a big nest of lists']]]]]]]]
>>> json_loads_byteified('{"foo": "bar", "things": [7, {"qux": "baz", "moo": {"cow": ["milk"]}}]}')
{'things': [7, {'qux': 'baz', 'moo': {'cow': ['milk']}}], 'foo': 'bar'}
>>> json_load_byteified(open('somefile.json'))
{'more json': 'from a file'}

Làm thế nào để nó hoạt động và tại sao tôi sẽ sử dụng nó?

Chức năng của Mark Amery ngắn hơn và rõ ràng hơn những chức năng này, vậy quan điểm của chúng là gì? Tại sao bạn muốn sử dụng chúng?

Hoàn toàn cho hiệu suất . Câu trả lời của Mark giải mã hoàn toàn văn bản JSON trước tiên bằng các chuỗi unicode, sau đó đệ quy thông qua toàn bộ giá trị được giải mã để chuyển đổi tất cả các chuỗi thành chuỗi byte. Điều này có một vài tác dụng không mong muốn:

  • Một bản sao của toàn bộ cấu trúc được giải mã được tạo trong bộ nhớ
  • Nếu đối tượng JSON của bạn thực sự được lồng sâu (500 cấp trở lên) thì bạn sẽ đạt được độ sâu đệ quy tối đa của Python

Câu trả lời này giảm thiểu cả hai vấn đề về hiệu suất bằng cách sử dụng object_hooktham số của json.loadjson.loads. Từ các tài liệu :

object_hooklà một hàm tùy chọn sẽ được gọi với kết quả của bất kỳ đối tượng nào được giải mã theo nghĩa đen (a dict). Giá trị trả về của object_hook sẽ được sử dụng thay vì dict. Tính năng này có thể được sử dụng để thực hiện các bộ giải mã tùy chỉnh

Vì các từ điển lồng nhau nhiều cấp độ sâu trong các từ điển khác được chuyển đến object_hook khi chúng được giải mã , chúng ta có thể byte hóa bất kỳ chuỗi hoặc danh sách nào bên trong chúng tại thời điểm đó và tránh sự đệ quy sâu sau này.

Câu trả lời của Mark không phù hợp để sử dụng như object_hookhiện tại, vì nó đệ quy thành từ điển lồng nhau. Chúng tôi ngăn chặn sự đệ quy trong câu trả lời này với ignore_dictstham số tới _byteify, được chuyển đến nó mọi lúc trừ khi object_hookchuyển nó thành một dictbyte mới. Các ignore_dictslá cờ nói _byteifybỏ qua dicts kể từ khi họ đã được byteified.

Cuối cùng, việc triển khai json_load_byteifiedjson_loads_byteifiedgọi _byteify(với ignore_dicts=True) kết quả của chúng tôi được trả về từ json.loadhoặc json.loadsđể xử lý trường hợp văn bản JSON được giải mã không có dictmức cao nhất.


1
+1 cho cách tiếp cận ở đây; Tôi đã không thực sự nắm bắt nó khi lần đầu tiên đọc nó, nhưng cuối cùng cũng hiểu khi đọc lại nó dưới ánh sáng của câu trả lời của Travis Jensen. Tôi đã thực hiện một chỉnh sửa khá tích cực với hy vọng làm rõ cách thức hoạt động và lợi thế của nó so với câu trả lời của tôi là gì. Ý tưởng cốt lõi của mã vẫn chưa được xử lý, nhưng tôi đã sửa đổi khá nhiều thứ khác. Vui lòng quay lại chỉnh sửa của tôi nếu bạn phản đối điều này - đó là câu trả lời của bạn!
Đánh dấu Amery

Không có vấn đề Mark, cảm ơn rất nhiều. Tôi thích chỉnh sửa của bạn, nó giải thích nhiều hơn so với bản gốc của tôi. Có lẽ, một ngày nào đó, tôi sẽ học cách đưa ra những câu trả lời ngắn gọn hơn.
Mirec Miskuf

2
Đây là giải pháp tuyệt vời; hiệu quả và thanh lịch. Tuy nhiên, nếu bạn đang mắc kẹt trong lĩnh vực Python <2.7, như tôi, bạn sẽ cần phải thay thế dòng: return { byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.iteritems() }với return dict((_byteify(key, ignore_dicts=True), _byteify(value, ignore_dicts=True)) for key, value in data.iteritems())cho nó làm việc.
Richard Dunn

Tôi nghĩ bạn đã sai về vấn đề độ sâu đệ quy. Với bạn, tôi có thể lên tới 990 : json_loads_byteified('[' * 990 + ']' * 990). Với 991 nó gặp sự cố. Mark vẫn hoạt động với 991 : byteify(json.loads('[' * 991 + ']' * 991)). Nó gặp sự cố ở 992. Vì vậy, ít nhất trong bài kiểm tra này, Mark có thể tiến sâu hơn, trái với những gì bạn nói.
Stefan Pochmann

@MarkAmery Bạn nghĩ gì về nhận xét trên của tôi? (Tôi chỉ thấy trong lịch sử chỉnh sửa rằng thực sự bạn đã thêm yêu cầu đó).
Stefan Pochmann

180

Mặc dù có một số câu trả lời hay ở đây, cuối cùng tôi đã sử dụng PyYAML để phân tích các tệp JSON của mình, vì nó cung cấp các khóa và giá trị dưới strdạng chuỗi kiểu thay vì unicodekiểu. Vì JSON là tập con của YAML, nó hoạt động độc đáo:

>>> import json
>>> import yaml
>>> list_org = ['a', 'b']
>>> list_dump = json.dumps(list_org)
>>> list_dump
'["a", "b"]'
>>> json.loads(list_dump)
[u'a', u'b']
>>> yaml.safe_load(list_dump)
['a', 'b']

Ghi chú

Một số điều cần lưu ý mặc dù:

  • Tôi nhận được các đối tượng chuỗi vì tất cả các mục nhập của tôi được mã hóa ASCII . Nếu tôi sử dụng các mục được mã hóa unicode, tôi sẽ lấy lại chúng dưới dạng các đối tượng unicode - không có chuyển đổi!

  • Bạn nên (có thể luôn luôn) sử dụng safe_loadchức năng của PyYAML ; nếu bạn sử dụng nó để tải các tệp JSON, dù sao bạn cũng không cần "sức mạnh bổ sung" của loadhàm.

  • Nếu bạn muốn một trình phân tích cú pháp YAML hỗ trợ nhiều hơn cho phiên bản 1.2 của thông số kỹ thuật (và phân tích chính xác các số rất thấp ), hãy thử Ruamel YAML : pip install ruamel.yamlvà đó import ruamel.yaml as yamllà tất cả những gì tôi cần trong các thử nghiệm của mình.

Chuyển đổi

Như đã nêu, không có chuyển đổi! Nếu bạn không thể chắc chắn chỉ xử lý các giá trị ASCII (và bạn không thể chắc chắn hầu hết thời gian), tốt hơn nên sử dụng chức năng chuyển đổi :

Tôi đã sử dụng một từ Mark Amery một vài lần bây giờ, nó hoạt động rất tốt và rất dễ sử dụng. Bạn cũng có thể sử dụng một chức năng tương tự như một object_hookthay thế, vì nó có thể giúp bạn tăng hiệu suất trên các tệp lớn. Xem câu trả lời liên quan nhiều hơn một chút từ Mirec Miskuf cho điều đó.


8
Hãy cẩn thận một chút nếu bạn quyết định sử dụng câu trả lời này. Nó hoạt động hoàn hảo cho trường hợp của Brutus, nhưng chỉ vì anh ta biết rằng dữ liệu của mình chỉ chứa các ký tự mã hóa ASCII. Nếu bạn không có sự đảm bảo đó, câu trả lời này sẽ không hiệu quả. Ví dụ: hãy thử thực thi yaml.load(json.dumps([u'a', u'£', u'É']))tại trình vỏ Python và quan sát rằng bạn lấy lại ['a', u'\xa3', u'\xc9'](có chứa unicodechuỗi). Nếu bạn không thể chắc chắn rằng dữ liệu của bạn chỉ chứa các ký tự từ bộ ký tự ASCII, bạn nên sử dụng một cách tiếp cận khác thay thế (tôi khuyên bạn nên trả lời riêng).
Đánh dấu Amery

1
YAML cũng sử dụng [u'a', u'b']cẩn thận.
Carlos Calla

1
Điều này là tốt, nhưng nó không hoạt động với số lượng thấp .. xem tại đây: stackoverflow.com/questions/30458977/
Kẻ

@Oren: Đây không phải là lỗi trong thông số YAML mà là trong trình phân tích cú pháp PyYAML. Trình phân tích YAML từ ruamel hoạt động.
Brutus

Tôi muốn có thông số như ["a", "b"] không thích ['a', 'b'] @Brutus
user60679

141

Không có tùy chọn tích hợp để làm cho các hàm mô đun json trả về chuỗi byte thay vì chuỗi unicode. Tuy nhiên, hàm đệ quy ngắn và đơn giản này sẽ chuyển đổi bất kỳ đối tượng JSON được giải mã nào từ việc sử dụng chuỗi unicode sang chuỗi byte được mã hóa UTF-8:

def byteify(input):
    if isinstance(input, dict):
        return {byteify(key): byteify(value)
                for key, value in input.iteritems()}
    elif isinstance(input, list):
        return [byteify(element) for element in input]
    elif isinstance(input, unicode):
        return input.encode('utf-8')
    else:
        return input

Chỉ cần gọi điều này trên đầu ra bạn nhận được từ một json.loadhoặc json.loadsgọi.

Một vài lưu ý:

  • Để hỗ trợ Python 2.6 hoặc sớm hơn, hãy thay thế return {byteify(key): byteify(value) for key, value in input.iteritems()}bằng return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()]), vì hiểu từ điển không được hỗ trợ cho đến Python 2.7.
  • Vì câu trả lời này đệ quy qua toàn bộ đối tượng được giải mã, nó có một vài đặc điểm hiệu suất không mong muốn có thể tránh được bằng cách sử dụng rất cẩn thận các tham số object_hookhoặc object_pairs_hooktham số. Câu trả lời của Mirec Miskuf cho đến nay là người duy nhất có thể giải quyết vấn đề này một cách chính xác, mặc dù do đó, nó phức tạp hơn đáng kể so với cách tiếp cận của tôi.

1
Tôi thích điều này - nó không phải là một sự bỏ qua - nó nhận ra rằng khi mọi người nói "chuỗi" và "ascii", họ chủ yếu ngây thơ có nghĩa là họ muốn byte, không phải là ký tự unicode lý thuyết. (và không phải ascii vì họ vẫn muốn có dấu thăng ở đầu kia)
Danny Staple

Tôi thích điều này, nó hoạt động gần giống như máy in đẹp của tôi hoạt động, vì tôi biết rằng json không tạo ra tuple, bạn cũng nên thêm ngoại lệ cho tuple.
y.petremann

Điều này là không hiệu quả khủng khiếp, đòi hỏi bạn phải đi qua các nút đệ quy mà bạn có thể không cần. Mô-đun json cung cấp cho bạn các móc để thực hiện việc này hiệu quả hơn nhiều. Tuy nhiên, câu trả lời dưới đây object_hookthực sự tệ hơn nhiều so với câu trả lời này, nhưng, bằng cách sử dụng object_pairs_hook, bạn có thể đưa ra một phương pháp hiệu quả hợp lý không yêu cầu đệ quy hoặc xem lại các nút không chứa chuỗi.
Travis Jensen

1
@TravisJensen Thú vị. Các object_pairs_hookphương pháp có lẽ là rất nhẹ khó hiểu hơn cái này (bạn cần phải hiểu cách thức hoạt động tham số và lý do tại sao danh sách và dicts yêu cầu xử lý khác nhau), và lợi ích hiệu suất sẽ không quan trọng đối với hầu hết mọi người ... nhưng tôi mong đợi nó tồn tại, đặc biệt là đối với bất kỳ ai đối phó với một đối tượng JSON được lồng sâu khác thường.
Đánh dấu Amery

plus1 Đây là câu trả lời ngắn gọn nhất; ngoài PyYAML là một nỗi đau để cài đặt. Điều duy nhất tốt hơn là bằng cách nào đó vi luồng chuyển đổi để nó không sử dụng bộ nhớ 4X.
Personal_cloud

74

Bạn có thể sử dụng object_hooktham số json.loadsđể truyền trong bộ chuyển đổi. Bạn không phải thực hiện chuyển đổi sau khi thực tế. Các jsonmô-đun sẽ luôn luôn vượt qua object_hookdicts chỉ, và nó sẽ vượt qua trong đệ quy dicts lồng nhau, vì vậy bạn không cần phải recurse vào dicts lồng mình. Tôi không nghĩ rằng tôi sẽ chuyển đổi chuỗi unicode thành các số như chương trình Wells. Nếu đó là một chuỗi unicode, nó được trích dẫn dưới dạng một chuỗi trong tệp JSON, do đó, nó được coi là một chuỗi (hoặc tệp là xấu).

Ngoài ra, tôi sẽ cố gắng tránh làm một cái gì đó như str(val)trên một unicodeđối tượng. Bạn nên sử dụng value.encode(encoding)với một mã hóa hợp lệ, tùy thuộc vào những gì lib bên ngoài của bạn mong đợi.

Ví dụ:

def _decode_list(data):
    rv = []
    for item in data:
        if isinstance(item, unicode):
            item = item.encode('utf-8')
        elif isinstance(item, list):
            item = _decode_list(item)
        elif isinstance(item, dict):
            item = _decode_dict(item)
        rv.append(item)
    return rv

def _decode_dict(data):
    rv = {}
    for key, value in data.iteritems():
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        elif isinstance(value, list):
            value = _decode_list(value)
        elif isinstance(value, dict):
            value = _decode_dict(value)
        rv[key] = value
    return rv

obj = json.loads(s, object_hook=_decode_dict)

3
Điều này cũng ổn nếu đối tượng trong slà một JSON Object(một tập hợp các cặp khóa: value không có thứ tự với ký tự ':' ngăn cách khóa và giá trị, được phân tách bằng dấu phẩy và được đặt trong dấu ngoặc nhọn), nhưng không, nếu nó, giả sử JSON Array. Vì vậy, nếu được cung cấp JSON Arraynhư thế nào ["a", "b"], kết quả sẽ vẫn như vậy [u'a', u'b']. Không ai trong số các tham số kiểu móc hiện có sẵn khác json.loads()có thể thực hiện công việc.
martineau

2
Vì, như bạn đã đề cập, jsonmô-đun sẽ chuyển qua đệ quy trong các lồng nhau dict, không cần thiết phải kiểm tra chúng trong hai hàm - vì vậy hai elifmệnh đề kiểm tra chúng nên được loại bỏ.
martineau

1
Lưu ý rằng tên hàm bắt đầu bằng dấu gạch dưới có ý nghĩa đặc biệt cho câu lệnh nhập. Nếu bạn đặt các hàm này trong một tệp có tên Utility.txt và trong một tệp khác thì from Utility import *các hàm đó sẽ không được nhìn thấy vì dấu gạch dưới đó.
M Katz

1
Đây là một ý tưởng thực sự xấu. object_hookđược gọi cho mọi đối tượng json được phân tích cú pháp, vì vậy nếu bạn lặp lại những gì được cung cấp cho bạn, bạn đang "tái xác định" những thứ mà bạn đã "byte hóa". Hiệu suất sẽ tăng trưởng về mặt hình học với kích thước của đối tượng. Tôi đã bao gồm một câu trả lời ở đây sử dụng object_pairs_hookvà không gặp phải vấn đề đó.
Travis Jensen

38

Đó là bởi vì json không có sự khác biệt giữa các đối tượng chuỗi và các đối tượng unicode. Tất cả đều là chuỗi trong javascript.

Tôi nghĩ JSON là đúng khi trả về các đối tượng unicode . Trên thực tế, tôi sẽ không chấp nhận bất cứ điều gì ít hơn, vì các chuỗi javascript thực tế là unicodecác đối tượng (tức là chuỗi JSON (javascript) có thể lưu trữ bất kỳ loại ký tự unicode nào) để tạo ra unicodecác đối tượng khi dịch chuỗi từ JSON. Các chuỗi đơn giản sẽ không phù hợp vì thư viện sẽ phải đoán mã hóa bạn muốn.

Tốt hơn là sử dụng unicodecác đối tượng chuỗi ở mọi nơi. Vì vậy, lựa chọn tốt nhất của bạn là cập nhật các thư viện của bạn để chúng có thể xử lý các đối tượng unicode.

Nhưng nếu bạn thực sự muốn bytestrings, chỉ cần mã hóa kết quả sang mã hóa mà bạn chọn:

>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']

Cảm ơn nosklo, đó là những gì tôi đã làm đầu tiên. Nhưng như tôi đã nói, dữ liệu thực tế tôi sử dụng là khá lồng nhau và tất cả, vì vậy điều này đã giới thiệu một số chi phí. Tôi vẫn đang tìm kiếm một giải pháp tự động ... Có ít nhất một báo cáo lỗi ngoài đó mọi người phàn nàn về việc đơn giản trả về các đối tượng chuỗi thay vì unicode.
Brutus

1
@Brutus: Tôi nghĩ rằng json là đúng khi trả về các đối tượng unicode. Trên thực tế, tôi sẽ không chấp nhận bất cứ điều gì ít hơn, vì các chuỗi javascript thực tế là các đối tượng unicode. Ý tôi là các chuỗi json (javascript) có thể lưu trữ bất kỳ loại ký tự unicode nào, vì vậy sẽ rất hợp lý khi tạo các đối tượng unicode khi dịch từ json. Bạn thực sự nên sửa chữa thư viện của bạn thay vào đó.
nosklo

16

Có tồn tại một công việc dễ dàng xung quanh.

TL; DR - Sử dụng ast.literal_eval()thay vì json.loads(). Cả hai astjsonđang ở trong thư viện tiêu chuẩn.

Mặc dù không phải là một câu trả lời 'hoàn hảo', nhưng nó sẽ trở nên khá xa nếu kế hoạch của bạn là bỏ qua Unicode hoàn toàn. Trong Python 2.7

import json, ast
d = { 'field' : 'value' }
print "JSON Fail: ", json.loads(json.dumps(d))
print "AST Win:", ast.literal_eval(json.dumps(d))

cho:

JSON Fail:  {u'field': u'value'}
AST Win: {'field': 'value'}

Điều này sẽ có nhiều lông hơn khi một số đối tượng thực sự là chuỗi Unicode. Câu trả lời đầy đủ nhanh chóng có lông.


11
Tốt hơn là đảm bảo json của bạn không chứa bất kỳ null, truehoặc falsegiá trị nào, vì chúng không hợp lệ trong python và sẽ gây ra literal_eval()lỗi.
ʇsәɹoɈ

3
@ sәɹoɈ Cũng hy vọng tốt hơn JSON của bạn không chứa solidus ( \/) thoát trong chuỗi hoặc chuỗi thoát unicode (như "\u0061", đó là một cách viết khác "a"). Cú pháp theo nghĩa đen của Python không tương thích với JSON theo nhiều cách và tôi sẽ không tin câu trả lời này cho bất kỳ tập lệnh nào mà tôi sẽ không vứt đi.
Mark Amery

Mọi người đã đúng rằng nếu chuỗi thực sự là unicode thì câu trả lời này không thành công, nhưng nếu đó là trường hợp chúng ta sẽ không thể truyền tới một chuỗi. +1 cho một câu trả lời chỉ hoạt động khi nó hoạt động và ném một ngoại lệ khác
Stefan Sullivan

nếu có thể không sử dụng jsonđể kết xuất dữ liệu, chỉ sử dụng printnếu chạy python. Sau đó ast.literal_evalhoạt động
Jean-François Fabre

11

Câu trả lời của Mike Brennan rất gần, nhưng không có lý do gì để duyệt lại toàn bộ cấu trúc. Nếu bạn sử dụng tham số object_hook_pairs(Python 2.7+):

object_pairs_hooklà một hàm tùy chọn sẽ được gọi với kết quả của bất kỳ đối tượng nào được giải mã theo nghĩa đen với một danh sách các cặp theo thứ tự. Giá trị trả về của object_pairs_hooksẽ được sử dụng thay vì dict. Tính năng này có thể được sử dụng để thực hiện các bộ giải mã tùy chỉnh dựa trên thứ tự các cặp khóa và giá trị được giải mã (ví dụ: collections.OrderedDictsẽ ghi nhớ thứ tự chèn). Nếu object_hookcũng được xác định, object_pairs_hookưu tiên.

Với nó, bạn nhận từng đối tượng JSON được trao cho bạn, do đó bạn có thể thực hiện giải mã mà không cần đệ quy:

def deunicodify_hook(pairs):
    new_pairs = []
    for key, value in pairs:
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        new_pairs.append((key, value))
    return dict(new_pairs)

In [52]: open('test.json').read()
Out[52]: '{"1": "hello", "abc": [1, 2, 3], "def": {"hi": "mom"}, "boo": [1, "hi", "moo", {"5": "some"}]}'                                        

In [53]: json.load(open('test.json'))
Out[53]: 
{u'1': u'hello',
 u'abc': [1, 2, 3],
 u'boo': [1, u'hi', u'moo', {u'5': u'some'}],
 u'def': {u'hi': u'mom'}}

In [54]: json.load(open('test.json'), object_pairs_hook=deunicodify_hook)
Out[54]: 
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

Lưu ý rằng tôi không bao giờ phải gọi hook một cách đệ quy vì mọi đối tượng sẽ được trao cho hook khi bạn sử dụng object_pairs_hook. Bạn phải quan tâm đến danh sách, nhưng như bạn có thể thấy, một đối tượng trong danh sách sẽ được chuyển đổi chính xác và bạn không phải lặp lại để thực hiện điều đó.

EDIT: Một đồng nghiệp đã chỉ ra rằng Python2.6 không có object_hook_pairs. Bạn vẫn có thể sử dụng Python2.6 này bằng cách thực hiện một thay đổi rất nhỏ. Trong hook ở trên, thay đổi:

for key, value in pairs:

đến

for key, value in pairs.iteritems():

Sau đó sử dụng object_hookthay vì object_pairs_hook:

In [66]: json.load(open('test.json'), object_hook=deunicodify_hook)
Out[66]: 
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

Sử dụng object_pairs_hookkết quả trong một từ điển ít hơn được khởi tạo cho từng đối tượng trong đối tượng JSON, nếu bạn đang phân tích một tài liệu khổng lồ, có thể có giá trị trong khi.


1
Điều này là gọn gàng và có vẻ rất gần với việc xứng đáng với dấu kiểm màu xanh lá cây (mà Brutus có, đáng ngưỡng mộ, đã được thông qua một cách tự do khi có câu trả lời tốt hơn). Nhưng ... tại sao không thực sự xử lý danh sách đúng theo cách deunicodify_hookbạn thể hiện trong câu trả lời này? Hiện tại, bạn có một triển khai deunicodify_hookkhông lặp lại các danh sách và khử các chuỗi và danh sách trong chúng, và do đó, đầu ra mà bạn trưng bày không khớp với đầu ra mà hook của bạn sẽ thực sự tạo ra. Khắc phục điều đó, và câu trả lời này sẽ vượt trội so với tôi.
Mark Amery

Phù phiếm: Tôi cũng đề nghị thể hiện chức năng với trình thông dịch CPython thông thường chứ không phải là trình dịch bạn đang sử dụng ở đây (mà tôi nghĩ là IronPython)? Trình thông dịch CPython quen thuộc hơn với hầu hết người dùng Python và theo tôi, đẹp hơn.
Mark Amery

Điều này không hiệu quả với tôi nhưng tôi chắc chắn đó là một vài điều không hay về những gì tôi đang làm ... Tôi đang lưu trữ một danh sách từ một tài liệu json lớn hơn vào một tệp. Cho dù tôi tải nó có hoặc không có object_pairs_hook này, mọi mục đều xuất hiện unicode. Chết tiệt.
rsaw

1
@rsaw Điểm tốt! Vì object_pairs_hookchỉ được gọi cho các đối tượng , nếu văn bản JSON của bạn có danh sách các chuỗi ở cấp cao nhất, giải pháp này sẽ thất bại. Không có cách nào để khắc phục điều này mà không gọi một số chức năng trên thứ được trả về json.load; không có cái json.loadmóc nào có thể đảm bảo bạn sẽ có thể xử lý mọi chuỗi. Tôi nghĩ rằng đây là một lỗ hổng đủ lớn để tôi tiếp tục đề xuất giải pháp của mình hơn là sử dụng móc.
Mark Amery

-1 bởi vì tôi mới nhận ra rằng Mirec Miskuf đã đăng một câu trả lời móc câu đối tượng mà không có nhược điểm nào trong cách tiếp cận của Mike Brennan (viết lại các từ điển giống nhau nhiều lần) cũng không phải từ này (không thể xác định danh sách lồng nhau hoặc danh sách cấp cao nhất hoặc chuỗi). Tôi không chắc tại sao câu trả lời của anh ta lại mòn mỏi mà hầu như không được chú ý trong khi câu trả lời này - kém hơn - đã nhanh chóng giành được phiếu bầu.
Đánh dấu Amery

9

Tôi e rằng không có cách nào để tự động đạt được điều này trong thư viện Simplejson.

Máy quét và bộ giải mã trong Simplejson được thiết kế để tạo văn bản unicode. Để làm điều này, thư viện sử dụng một hàm gọi là c_scanstring(nếu có, với tốc độ) hoặc py_scanstringnếu phiên bản C không có sẵn. Các scanstringhàm được gọi nhiều lần bởi gần như mọi thói quen mà simplejson có để giải mã một cấu trúc có thể chứa văn bản. Bạn sẽ phải đánh dấu scanstringgiá trị trong Simplejson.decoder hoặc lớp con JSONDecodervà cung cấp khá nhiều toàn bộ việc thực hiện của riêng bạn đối với bất kỳ thứ gì có thể chứa văn bản.

Tuy nhiên, lý do mà Simplejson đưa ra unicode là do đặc tả json đề cập cụ thể rằng "Chuỗi là tập hợp các ký tự Unicode bằng 0 hoặc nhiều hơn" ... hỗ trợ cho unicode được coi là một phần của định dạng. Việc scanstringtriển khai của Simplejson đi xa đến mức quét và giải thích các lần thoát unicode (thậm chí kiểm tra lỗi đối với các biểu diễn bộ ký tự nhiều byte không đúng định dạng), do đó cách duy nhất có thể trả lại giá trị cho bạn là unicode.

Nếu bạn có một thư viện lâu đời cần một str, tôi khuyên bạn nên tìm kiếm cấu trúc dữ liệu lồng nhau sau khi phân tích cú pháp (mà tôi thừa nhận là những gì bạn nói rõ ràng muốn tránh ... xin lỗi), hoặc có thể bọc thư viện của bạn theo một số loại mặt tiền nơi bạn có thể xoa bóp các tham số đầu vào ở mức độ chi tiết hơn. Cách tiếp cận thứ hai có thể dễ quản lý hơn phương pháp thứ nhất nếu cấu trúc dữ liệu của bạn thực sự được lồng sâu.


4

Như Mark (Amery) lưu ý chính xác: Sử dụng trình giải nén của PyYaml trên bãi chứa json chỉ hoạt động nếu bạn chỉ có ASCII. Ít nhất là ra khỏi hộp.

Hai nhận xét nhanh về phương pháp PyYaml:

  1. KHÔNG BAO GIỜ sử dụng yaml.load trên dữ liệu từ trường. Đây là một tính năng (!) Của yaml để thực thi mã tùy ý ẩn trong cấu trúc.

  2. Bạn cũng có thể làm cho nó hoạt động cho cả ASCII thông qua điều này:

    def to_utf8(loader, node):
        return loader.construct_scalar(node).encode('utf-8')
    yaml.add_constructor(u'tag:yaml.org,2002:str', to_utf8)

Nhưng hiệu suất khôn ngoan không so sánh với câu trả lời của Mark Amery:

Ném một số mẫu được lồng sâu vào hai phương thức, tôi nhận được điều này (với dt [j] = delta delta của json.loads (json.dumps (m))):

     dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j]
     dt[byteify recursion(Mark Amery)] =~   5 * dt[j]

Vì vậy, khử lưu huỳnh bao gồm đi bộ hoàn toàn cây mã hóa, theo thứ tự độ lớn của việc thực hiện dựa trên C của json. Tôi thấy điều này nhanh đáng kể và nó cũng mạnh hơn tải trọng yaml tại các cấu trúc lồng nhau sâu. Và ít có lỗi bảo mật hơn, nhìn vào yaml.load.

=> Mặc dù tôi sẽ đánh giá cao một con trỏ tới bộ chuyển đổi chỉ dựa trên C, hàm byteify sẽ là câu trả lời mặc định.

Điều này đặc biệt đúng nếu cấu trúc json của bạn là từ trường, chứa đầu vào của người dùng. Bởi vì sau đó bạn có thể cần phải đi bộ nào trên cấu trúc của bạn - độc lập về mong muốn cấu trúc dữ liệu nội bộ của bạn ( 'bánh sandwich unicode' hoặc các chuỗi byte chỉ).

Tại sao?

Chuẩn hóa Unicode . Đối với những người không biết: Hãy uống thuốc giảm đau và đọc .

Vì vậy, bằng cách sử dụng đệ quy byteify, bạn giết hai con chim bằng một hòn đá:

  1. nhận được bytestrings của bạn từ bãi rác json lồng nhau
  2. lấy giá trị đầu vào của người dùng được chuẩn hóa, để bạn tìm thấy nội dung trong bộ lưu trữ của mình.

Trong các thử nghiệm của tôi, hóa ra việc thay thế input.encode ('utf-8') bằng unicodingata.n normalize ('NFC', input) .encode ('utf-8') thậm chí còn nhanh hơn w / o NFC - nhưng Điều đó phụ thuộc rất nhiều vào dữ liệu mẫu mà tôi đoán.


3

Gotcha là thế simplejsonjsonlà hai mô-đun khác nhau, ít nhất là theo cách chúng xử lý unicode. Bạn có jsontrong py 2.6+ và điều này cung cấp cho bạn các giá trị unicode, trong khi simplejsontrả về các đối tượng chuỗi. Chỉ cần thử easy_install-ing Simplejson trong môi trường của bạn và xem nó có hoạt động không. Nó đã làm cho tôi.


2

Chỉ cần sử dụng dưa chua thay vì json để đổ và tải, như vậy:

    import json
    import pickle

    d = { 'field1': 'value1', 'field2': 2, }

    json.dump(d,open("testjson.txt","w"))

    print json.load(open("testjson.txt","r"))

    pickle.dump(d,open("testpickle.txt","w"))

    print pickle.load(open("testpickle.txt","r"))

Đầu ra mà nó tạo ra là (chuỗi và số nguyên được xử lý chính xác):

    {u'field2': 2, u'field1': u'value1'}
    {'field2': 2, 'field1': 'value1'}

1
+1 cho một giải pháp không yêu cầu các gói bổ sung (như yaml ). Nhưng đôi khi - như trong trường hợp ban đầu của tôi - tôi cần phải có dữ liệu trong JSON, vì vậy, dưa chua không phải lúc nào cũng là lựa chọn tốt nhất. Ngoài ra, bạn có safe_loadtrong YAML, tôi không biết có thứ gì tương tự tồn tại cho dưa chua không .
Brutus

1

Vì vậy, tôi đã gặp vấn đề tương tự. Đoán kết quả đầu tiên của Google là gì.

Vì tôi cần chuyển tất cả dữ liệu cho PyGTK, nên chuỗi unicode cũng không hữu ích cho tôi. Vì vậy, tôi có một phương pháp chuyển đổi đệ quy khác. Nó thực sự cũng cần thiết cho chuyển đổi JSON an toàn - json.dump () sẽ bảo lãnh cho bất kỳ người không biết chữ nào, như các đối tượng Python. Không chuyển đổi chỉ mục dict mặc dù.

# removes any objects, turns unicode back into str
def filter_data(obj):
        if type(obj) in (int, float, str, bool):
                return obj
        elif type(obj) == unicode:
                return str(obj)
        elif type(obj) in (list, tuple, set):
                obj = list(obj)
                for i,v in enumerate(obj):
                        obj[i] = filter_data(v)
        elif type(obj) == dict:
                for i,v in obj.iteritems():
                        obj[i] = filter_data(v)
        else:
                print "invalid object in data, converting to string"
                obj = str(obj) 
        return obj

Vấn đề duy nhất có thể xảy ra ở đây là nếu bạn cần các khóa trong từ điển được chuyển đổi từ unicode. Mặc dù việc thực hiện này sẽ chuyển đổi các giá trị, nó vẫn duy trì các khóa unicode. Nếu bạn tạo một 'newobj', hãy sử dụng newobj [str (i)] = ... và gán obj = newobj khi bạn hoàn thành, các phím cũng sẽ được chuyển đổi.
Neal Stublen

Điều này có thể đẹp hơn với sự hiểu biết hoặc tốt hơn bằng cách chuyển đổi các khóa. Nó cũng không phổ biến; cả hai đều biến đổi các đối tượng tại chỗ (trong trường hợp từ điển) và trả về giá trị mới, không phù hợp với các phương thức thu thập tích hợp của Python, có thể làm thay đổi đối tượng hiện tại hoặc trả về một đối tượng mới, nhưng không phải cả hai.
Mark Amery

1

Tôi đã có một dict JSON như một chuỗi. Các khóa và giá trị là các đối tượng unicode như trong ví dụ sau:

myStringDict = "{u'key':u'value'}"

Tôi có thể sử dụng byteifyhàm được đề xuất ở trên bằng cách chuyển đổi chuỗi thành một dictđối tượng bằng cách sử dụng ast.literal_eval(myStringDict).


Ví dụ bạn đã đưa ra không phải là một ví dụ về JSON. {u'key':u'value'}không phải là JSON.
Mark Amery

2
Tôi hoàn toàn biết đó không phải là JSON. Đó là cách nó được phân tích cú pháp từ một nguồn bên ngoài trong tập lệnh python của tôi. Nếu nó là JSON trực tiếp như trong ví dụ sau, tôi sẽ không cần hàm byteify được đánh dấu là giải pháp: {"firstName": "John", "lastName": "Doe"}. Sẽ thật tuyệt nếu trước khi bỏ phiếu bạn đọc câu trả lời. Cảm ơn.
narko

1

Hỗ trợ Python2 & 3 bằng cách sử dụng hook (từ https://stackoverflow.com/a/33571117/558397 )

import requests
import six
from six import iteritems

requests.packages.urllib3.disable_warnings()  # @UndefinedVariable
r = requests.get("http://echo.jsontest.com/key/value/one/two/three", verify=False)

def _byteify(data):
    # if this is a unicode string, return its string representation
    if isinstance(data, six.string_types):
        return str(data.encode('utf-8').decode())

    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item) for item in data ]

    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict):
        return {
            _byteify(key): _byteify(value) for key, value in iteritems(data)
        }
    # if it's anything else, return it in its original form
    return data

w = r.json(object_hook=_byteify)
print(w)

Trả về:

 {'three': '', 'key': 'value', 'one': 'two'}

0

Đây là trò chơi muộn, nhưng tôi đã xây dựng caster đệ quy này. Nó hoạt động cho nhu cầu của tôi và tôi nghĩ nó tương đối đầy đủ. Nó có thể giúp bạn.

def _parseJSON(self, obj):
    newobj = {}

    for key, value in obj.iteritems():
        key = str(key)

        if isinstance(value, dict):
            newobj[key] = self._parseJSON(value)
        elif isinstance(value, list):
            if key not in newobj:
                newobj[key] = []
                for i in value:
                    newobj[key].append(self._parseJSON(i))
        elif isinstance(value, unicode):
            val = str(value)
            if val.isdigit():
                val = int(val)
            else:
                try:
                    val = float(val)
                except ValueError:
                    val = str(val)
            newobj[key] = val

    return newobj

Chỉ cần truyền cho nó một đối tượng JSON như vậy:

obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)

Tôi có nó như là một thành viên tư nhân của một lớp, nhưng bạn có thể sử dụng lại phương thức mà bạn thấy phù hợp.


Tôi đã gặp phải một vấn đề trong đó tôi đang cố phân tích JSON và chuyển ánh xạ kết quả sang một hàm dưới dạng ** kwargs. Có vẻ như tên tham số hàm không thể là unicode, vì vậy hàm _parseJSON của bạn rất tuyệt. Nếu có một cách dễ dàng hơn, ai đó có thể cho tôi biết.
Neal Stublen

1
Mã này có một vấn đề - bạn thực hiện một cuộc gọi đệ quy trong phần Danh sách, sẽ thất bại nếu các thành phần của danh sách không phải là từ điển.
I82Much

Bên cạnh lỗi được mô tả bởi @ I82Much, đây cũng được đặt tên xấu (thực tế nó không phân tích cú pháp JSON; json.loadstrước tiên cần một cuộc gọi), tự ý cố gắng chuyển đổi chuỗi thành int mà không giải thích được và không sao chép và dán sẵn sàng.
Mark Amery

0

Tôi viết lại _parse_json () của Wells để xử lý các trường hợp trong đó đối tượng json là một mảng (trường hợp sử dụng của tôi).

def _parseJSON(self, obj):
    if isinstance(obj, dict):
        newobj = {}
        for key, value in obj.iteritems():
            key = str(key)
            newobj[key] = self._parseJSON(value)
    elif isinstance(obj, list):
        newobj = []
        for value in obj:
            newobj.append(self._parseJSON(value))
    elif isinstance(obj, unicode):
        newobj = str(obj)
    else:
        newobj = obj
    return newobj

0

đây là một bộ mã hóa đệ quy được viết bằng C: https://github.com/axiros/nested_encode

Chi phí hoạt động cho các cấu trúc "trung bình" khoảng 10% so với json.loads.

python speed.py                                                                                            
  json loads            [0.16sec]: {u'a': [{u'b': [[1, 2, [u'\xd6ster..
  json loads + encoding [0.18sec]: {'a': [{'b': [[1, 2, ['\xc3\x96ster.
  time overhead in percent: 9%

sử dụng cấu trúc này:

import json, nested_encode, time

s = """
{
  "firstName": "Jos\\u0301",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "\\u00d6sterreich",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [],
  "spouse": null,
  "a": [{"b": [[1, 2, ["\\u00d6sterreich"]]]}]
}
"""


t1 = time.time()
for i in xrange(10000):
    u = json.loads(s)
dt_json = time.time() - t1

t1 = time.time()
for i in xrange(10000):
    b = nested_encode.encode_nested(json.loads(s))
dt_json_enc = time.time() - t1

print "json loads            [%.2fsec]: %s..." % (dt_json, str(u)[:20])
print "json loads + encoding [%.2fsec]: %s..." % (dt_json_enc, str(b)[:20])

print "time overhead in percent: %i%%"  % (100 * (dt_json_enc - dt_json)/dt_json)

0

Với Python 3.6, đôi khi tôi vẫn gặp phải vấn đề này. Ví dụ: khi nhận được phản hồi từ API REST và tải văn bản phản hồi vào JSON, tôi vẫn nhận được các chuỗi unicode. Tìm thấy một giải pháp đơn giản bằng cách sử dụng json.dumps ().

response_message = json.loads(json.dumps(response.text))
print(response_message)

-1

Tôi cũng gặp phải vấn đề này và phải xử lý JSON, tôi đã nghĩ ra một vòng lặp nhỏ chuyển đổi các khóa unicode thành chuỗi. (simplejson trên GAE không trả về khóa chuỗi.)

obj là đối tượng được giải mã từ JSON:

if NAME_CLASS_MAP.has_key(cls):
    kwargs = {}
    for i in obj.keys():
        kwargs[str(i)] = obj[i]
    o = NAME_CLASS_MAP[cls](**kwargs)
    o.save()

kwargslà những gì tôi chuyển đến hàm tạo của ứng dụng GAE (không thích unicodecác khóa trong **kwargs)

Không mạnh mẽ như giải pháp từ Wells, nhưng nhỏ hơn nhiều.


-1

Tôi đã điều chỉnh mã từ câu trả lời của Mark Amery , đặc biệt là để loại bỏ isinstancenhững ưu điểm của việc gõ vịt.

Việc mã hóa được thực hiện thủ công và ensure_asciibị vô hiệu hóa. Các tài liệu trăn json.dumpnói rằng

Nếu notify_ascii là True (mặc định), tất cả các ký tự không phải ASCII trong đầu ra sẽ được thoát với các chuỗi \ uXXXX

Disclaimer: trong doctest tôi đã sử dụng ngôn ngữ Hungary. Một số mã hóa ký tự liên quan đến Hungary đáng chú ý là: mã hóa cp852IBM / OEM được sử dụng, vd. trong DOS (đôi khi được gọi là ascii , tôi nghĩ không chính xác, nó phụ thuộc vào cài đặt codepage ), cp1250được sử dụng , vd. trong Windows (đôi khi được gọi là ansi , phụ thuộc vào cài đặt ngôn ngữ) và iso-8859-2đôi khi được sử dụng trên các máy chủ http. Văn bản kiểm tra Tüskéshátú kígyóbűvölőđược quy cho Koltai László (mẫu tên cá nhân bản địa) và từ wikipedia .

# coding: utf-8
"""
This file should be encoded correctly with utf-8.
"""
import json

def encode_items(input, encoding='utf-8'):
    u"""original from: https://stackoverflow.com/a/13101776/611007
    adapted by SO/u/611007 (20150623)
    >>> 
    >>> ## run this with `python -m doctest <this file>.py` from command line
    >>> 
    >>> txt = u"Tüskéshátú kígyóbűvölő"
    >>> txt2 = u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"
    >>> txt3 = u"uúuutifu"
    >>> txt4 = b'u\\xfauutifu'
    >>> # txt4 shouldn't be 'u\\xc3\\xbauutifu', string content needs double backslash for doctest:
    >>> assert u'\\u0102' not in b'u\\xfauutifu'.decode('cp1250')
    >>> txt4u = txt4.decode('cp1250')
    >>> assert txt4u == u'u\\xfauutifu', repr(txt4u)
    >>> txt5 = b"u\\xc3\\xbauutifu"
    >>> txt5u = txt5.decode('utf-8')
    >>> txt6 = u"u\\u251c\\u2551uutifu"
    >>> there_and_back_again = lambda t: encode_items(t, encoding='utf-8').decode('utf-8')
    >>> assert txt == there_and_back_again(txt)
    >>> assert txt == there_and_back_again(txt2)
    >>> assert txt3 == there_and_back_again(txt3)
    >>> assert txt3.encode('cp852') == there_and_back_again(txt4u).encode('cp852')
    >>> assert txt3 == txt4u,(txt3,txt4u)
    >>> assert txt3 == there_and_back_again(txt5)
    >>> assert txt3 == there_and_back_again(txt5u)
    >>> assert txt3 == there_and_back_again(txt4u)
    >>> assert txt3.encode('cp1250') == encode_items(txt4, encoding='utf-8')
    >>> assert txt3.encode('utf-8') == encode_items(txt5, encoding='utf-8')
    >>> assert txt2.encode('utf-8') == encode_items(txt, encoding='utf-8')
    >>> assert {'a':txt2.encode('utf-8')} == encode_items({'a':txt}, encoding='utf-8')
    >>> assert [txt2.encode('utf-8')] == encode_items([txt], encoding='utf-8')
    >>> assert [[txt2.encode('utf-8')]] == encode_items([[txt]], encoding='utf-8')
    >>> assert [{'a':txt2.encode('utf-8')}] == encode_items([{'a':txt}], encoding='utf-8')
    >>> assert {'b':{'a':txt2.encode('utf-8')}} == encode_items({'b':{'a':txt}}, encoding='utf-8')
    """
    try:
        input.iteritems
        return {encode_items(k): encode_items(v) for (k,v) in input.iteritems()}
    except AttributeError:
        if isinstance(input, unicode):
            return input.encode(encoding)
        elif isinstance(input, str):
            return input
        try:
            iter(input)
            return [encode_items(e) for e in input]
        except TypeError:
            return input

def alt_dumps(obj, **kwargs):
    """
    >>> alt_dumps({'a': u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"})
    '{"a": "T\\xc3\\xbcsk\\xc3\\xa9sh\\xc3\\xa1t\\xc3\\xba k\\xc3\\xadgy\\xc3\\xb3b\\xc5\\xb1v\\xc3\\xb6l\\xc5\\x91"}'
    """
    if 'ensure_ascii' in kwargs:
        del kwargs['ensure_ascii']
    return json.dumps(encode_items(obj), ensure_ascii=False, **kwargs)

Tôi cũng muốn làm nổi bật câu trả lời của Jarret Hardie tham chiếu thông số JSON , trích dẫn:

Chuỗi là một tập hợp gồm 0 hoặc nhiều ký tự Unicode

Trong trường hợp sử dụng của tôi, tôi đã có các tập tin với json. Chúng là utf-8các tập tin được mã hóa. ensure_asciidẫn đến các tệp json được thoát đúng nhưng không dễ đọc, đó là lý do tại sao tôi đã điều chỉnh câu trả lời của Mark Amery để phù hợp với nhu cầu của mình.

Các doctest không đặc biệt chu đáo nhưng tôi chia sẻ mã với hy vọng rằng nó sẽ hữu ích cho ai đó.


Tôi không chắc chắn tôi thấy những lợi ích của việc sử dụng vịt gõ ở đây? Chúng tôi biết rằng các bộ sưu tập được trả về json.loadssẽ là danh sách hoặc ký tự, không phải loại do người dùng xác định hoặc thư viện xác định thực hiện phương thức và phương thức ma thuật của họ, vậy tại sao không isinstancekiểm tra? Điều đó không dễ hiểu hơn việc kiểm tra sự tồn tại của iteritemshoặc liệu có iterchấp nhận đối tượng làm đối số không?
Đánh dấu Amery

@MarkAmery đây là về bãi, không tải. nếu bạn tạo dữ liệu để kết xuất - trái ngược với việc tải dữ liệu - bạn không thể chắc chắn đó là dữ liệu gì. ý tưởng là để cho nó đến từ bất cứ nơi nào trong mã.
n611x007

-2

Kiểm tra câu trả lời này cho một câu hỏi tương tự như thế này nói rằng

Tiền tố chỉ có nghĩa là bạn có một chuỗi Unicode. Khi bạn thực sự sử dụng chuỗi, nó sẽ không xuất hiện trong dữ liệu của bạn. Đừng bị ném bởi đầu ra in.

Ví dụ: thử điều này:

print mail_accounts[0]["i"]

Bạn sẽ không nhìn thấy một u.


Không đúng nếu ví dụ: bạn muốn định dạng thứ gì đó chứa chuỗi unicode, trong Py2. ví dụ '{}'.format({u'x' : u'y'})vẫn bao gồm các u.
Ponkadoodle
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.