python - 将json.dumps中的utf-8文本保存为UTF8,而不是\ u转义序列

样本代码:

>>> import json
>>> json_string = json.dumps("ברי צקלה")
>>> print json_string
"\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4"

问题是:它不是人类可读的。我的(智能)用户希望使用JSON转储来验证甚至编辑文本文件。(我宁愿不使用XML)
是否有方法将对象序列化为utf-8 json字符串(而不是\uxxxx)?
这没有帮助:
>>> output = json_string.decode('string-escape')
"\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4"

这是可行的,但是如果任何子对象是python unicode而不是utf-8,它将转储垃圾:
>>> #### ok:
>>> s= json.dumps( "ברי צקלה", ensure_ascii=False)    
>>> print json.loads(s)   
ברי צקלה

>>> #### NOT ok:
>>> d={ 1: "ברי צקלה", 2: u"ברי צקלה" }
>>> print d
{1: '\xd7\x91\xd7\xa8\xd7\x99 \xd7\xa6\xd7\xa7\xd7\x9c\xd7\x94', 
 2: u'\xd7\x91\xd7\xa8\xd7\x99 \xd7\xa6\xd7\xa7\xd7\x9c\xd7\x94'}
>>> s = json.dumps( d, ensure_ascii=False, encoding='utf8')
>>> print json.loads(s)['1']
ברי צקלה
>>> print json.loads(s)['2']
××¨× ×¦×§××


最佳答案:

使用ensure_ascii=False开关切换到json.dumps(),然后手动将值编码为utf-8:

>>> json_string = json.dumps(u"ברי צקלה", ensure_ascii=False).encode('utf8')
>>> json_string
'"\xd7\x91\xd7\xa8\xd7\x99 \xd7\xa6\xd7\xa7\xd7\x9c\xd7\x94"'
>>> print json_string
"ברי צקלה"

如果要将此写入文件,可以使用io.open()而不是open()生成一个文件对象,该对象在写入时为您编码Unicode值,然后使用json.dump()而不是写入该文件:
with io.open('filename', 'w', encoding='utf8') as json_file:
    json.dump(u"ברי צקלה", json_file, ensure_ascii=False)

在Python3中,内置的open()io.open()的别名。请注意,这里有一个bug in the json module标志,其中ensure_ascii=False标志可以生成unicodestr对象的混合。然后,python 2的解决方法是:
with io.open('filename', 'w', encoding='utf8') as json_file:
    data = json.dumps(u"ברי צקלה", ensure_ascii=False)
    # unicode(data) auto-decodes data to unicode if str
    json_file.write(unicode(data))

如果要传递编码为utf-8的字节字符串(python 2中键入str,python 3中键入bytes),请确保还设置了encoding关键字:
>>> d={ 1: "ברי צקלה", 2: u"ברי צקלה" }
>>> d
{1: '\xd7\x91\xd7\xa8\xd7\x99 \xd7\xa6\xd7\xa7\xd7\x9c\xd7\x94', 2: u'\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4'}

>>> s=json.dumps(d, ensure_ascii=False, encoding='utf8')
>>> s
u'{"1": "\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4", "2": "\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4"}'
>>> json.loads(s)['1']
u'\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4'
>>> json.loads(s)['2']
u'\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4'
>>> print json.loads(s)['1']
ברי צקלה
>>> print json.loads(s)['2']
ברי צקלה

请注意,您的第二个示例不是有效的Unicode;您将utf-8字节作为Unicode文本提供给它,但这将永远无法工作:
>>> s = u'\xd7\x91\xd7\xa8\xd7\x99 \xd7\xa6\xd7\xa7\xd7\x9c\xd7\x94'
>>> print s
××¨× ×¦×§××
>>> print s.encode('latin1').decode('utf8')
ברי צקלה

只有当我将该字符串编码为拉丁文1(其Unicode代码点将一对一映射到字节)然后解码为UTF-8时,您才能看到预期的输出。这与JSON无关,与使用错误的输入有关。结果称为Mojibake
如果您从字符串文字中获得该Unicode值,则使用错误的编解码器对其进行解码。它可能是您的终端配置错误,或者您的文本编辑器使用不同于您告诉Python读取文件的编解码器保存了源代码。或者您从应用了错误编解码器的库中获得它。这一切都与JSON库无关。