Menyimpan teks utf-8 di json.dumps sebagai UTF8, bukan sebagai \ u escape sequence

474

Kode sampel:

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

Masalahnya: itu tidak dapat dibaca manusia. Pengguna (cerdas) saya ingin memverifikasi atau bahkan mengedit file teks dengan dump JSON (dan saya lebih suka tidak menggunakan XML).

Apakah ada cara untuk membuat objek bersambung menjadi string UTF-8 JSON (bukan \uXXXX)?

Berry Tsakala
sumber
9
+ untuk ברי צקלה :)))
rubmz

Jawaban:

642

Gunakan ensure_ascii=Falsesakelar ke json.dumps(), lalu enkode nilai ke UTF-8 secara manual:

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

Jika Anda menulis ke file, cukup gunakan json.dump()dan serahkan ke objek file untuk menyandikan:

with open('filename', 'w', encoding='utf8') as json_file:
    json.dump("ברי צקלה", json_file, ensure_ascii=False)

Peringatan untuk Python 2

Untuk Python 2, ada beberapa peringatan yang perlu dipertimbangkan. Jika Anda menulis ini ke file, Anda bisa menggunakan io.open()alih-alih open()menghasilkan objek file yang mengkodekan nilai Unicode untuk Anda saat Anda menulis, kemudian gunakan json.dump()untuk menulis ke file itu:

with io.open('filename', 'w', encoding='utf8') as json_file:
    json.dump(u"ברי צקלה", json_file, ensure_ascii=False)

Apakah dicatat bahwa ada bug di jsonmodul mana ensure_ascii=Falsebendera dapat menghasilkan campuran dari unicodedan strbenda-benda. Solusinya untuk Python 2 adalah:

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

Dalam Python 2, saat menggunakan string byte (tipe str), disandikan ke UTF-8, pastikan juga mengatur encodingkata kunci:

>>> 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']
ברי צקלה
Martijn Pieters
sumber
72

Untuk menulis ke file

import codecs
import json

with codecs.open('your_file.txt', 'w', encoding='utf-8') as f:
    json.dump({"message":"xin chào việt nam"}, f, ensure_ascii=False)

Untuk mencetak ke stdout

import json
print(json.dumps({"message":"xin chào việt nam"}, ensure_ascii=False))
Trần Quang Hiệp
sumber
1
SyntaxError: Non-ASCII karakter '\ xc3' di file json-utf8.py pada baris 5, tetapi tidak ada encoding yang dideklarasikan; lihat python.org/dev/peps/pep-0263 untuk detailnya
Alex
Terima kasih! Saya tidak menyadari itu sesederhana itu. Anda hanya perlu berhati-hati jika data yang Anda konversi menjadi json adalah input pengguna yang tidak dipercaya.
Karim Sonbol
@Alex apakah Anda mengetahui cara menghindari masalah itu?
Gabriel Fair
@ Gabriel terus terang, saya tidak ingat. Itu bukan sesuatu yang begitu penting untuk menyingkirkan potongan :(
Alex
Hanya bekerja untuk saya menggunakan codecsperpustakaan. Terima kasih!
igorkf
29

UPDATE: Ini adalah jawaban yang salah, tetapi masih berguna untuk memahami mengapa itu salah. Lihat komentar.

Bagaimana dengan unicode-escape?

>>> d = {1: "ברי צקלה", 2: u"ברי צקלה"}
>>> json_str = json.dumps(d).decode('unicode-escape').encode('utf8')
>>> print json_str
{"1": "ברי צקלה", "2": "ברי צקלה"}
monitorius
sumber
9
unicode-escapetidak perlu: Anda bisa menggunakannya json.dumps(d, ensure_ascii=False).encode('utf8'). Dan tidak dijamin bahwa json menggunakan aturan yang sama persis seperti unicode-escapecodec dalam Python dalam semua kasus yaitu, hasilnya mungkin atau mungkin tidak sama dalam beberapa kasus sudut. Downvote adalah untuk konversi yang tidak perlu dan mungkin salah. Tidak terkait: print json_strhanya berfungsi untuk lokal utf8 atau jika PYTHONIOENCODINGenvvar menentukan utf8 di sini (cetak Unicode sebagai gantinya).
jfs
3
Masalah lain: setiap tanda kutip ganda dalam nilai string akan hilang, jadi ini akan menghasilkan output JSON yang rusak .
Martijn Pieters
kesalahan dalam Python3: AttributeError: objek 'str' tidak memiliki atribut 'decode'
Gank
1
unicode-escape berfungsi dengan baik! Saya akan menerima jawaban ini sebagai jawaban yang benar.
Pekerja
@ jfs Tidak, json.dumps(d, ensure_ascii=False).encode('utf8')tidak berfungsi, setidaknya untuk saya. Saya mendapatkan - UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position ...teror. The unicode-escapevarian bekerja dengan baik namun.
turingtested
24

Pemecahan masalah python 2 Peters gagal pada kasus tepi:

d = {u'keyword': u'bad credit  \xe7redit cards'}
with io.open('filename', 'w', encoding='utf8') as json_file:
    data = json.dumps(d, ensure_ascii=False).decode('utf8')
    try:
        json_file.write(data)
    except TypeError:
        # Decode data to Unicode first
        json_file.write(data.decode('utf8'))

UnicodeEncodeError: 'ascii' codec can't encode character u'\xe7' in position 25: ordinal not in range(128)

Itu menabrak bagian .decode ('utf8') dari baris 3. Saya memperbaiki masalah dengan membuat program lebih sederhana dengan menghindari langkah itu serta casing khusus ascii:

with io.open('filename', 'w', encoding='utf8') as json_file:
  data = json.dumps(d, ensure_ascii=False, encoding='utf8')
  json_file.write(unicode(data))

cat filename
{"keyword": "bad credit  çredit cards"}
Jonathan Ray
sumber
2
'Kasus tepi' hanyalah kesalahan bodoh yang belum teruji di pihak saya. unicode(data)Pendekatan Anda adalah opsi yang lebih baik daripada menggunakan penanganan pengecualian. Perhatikan bahwa encoding='utf8'argumen kata kunci tidak ada hubungannya dengan output yang json.dumps()menghasilkan; digunakan untuk mendekode strinput yang diterima fungsi.
Martijn Pieters
2
@ MartijnPieters: atau lebih sederhana: open('filename', 'wb').write(json.dumps(d, ensure_ascii=False).encode('utf8'))Ini berfungsi baik dumpsmengembalikan (ascii-only) str atau objek unicode.
jfs
@ JSFSebastian: benar, karena str.encode('utf8') menerjemahkan pertama kali secara implisit. Tapi begitu juga unicode(data), jika diberi strobjek. :-) Menggunakan io.open()memberi Anda lebih banyak opsi, termasuk menggunakan codec yang menulis BOM dan Anda mengikuti data JSON dengan sesuatu yang lain.
Martijn Pieters
@ MartijnPieters: .encode('utf8')varian berbasis-bekerja pada kedua Python 2 dan 3 (kode yang sama). Tidak ada unicodepada Python 3. Tidak terkait: file json tidak boleh menggunakan BOM (meskipun parser json yang mengonfirmasi dapat mengabaikan BOM, lihat errate 3983 ).
jfs
menambah encoding='utf8'untuk json.dumpsmemecahkan masalah. PS Saya punya teks cyrillic untuk dibuang
Max L
8

Pada Python 3.7 kode berikut berfungsi dengan baik:

from json import dumps
result = {"symbol": "ƒ"}
json_string = dumps(result, sort_keys=True, indent=2, ensure_ascii=False)
print(json_string)

Keluaran:

{"symbol": "ƒ"}
Nik
sumber
2
juga dalam python 3.6 (baru saja diverifikasi).
Berry Tsakala
7

Berikut ini adalah pemahaman saya atas jawaban bacaan di atas dan google.

# coding:utf-8
r"""
@update: 2017-01-09 14:44:39
@explain: str, unicode, bytes in python2to3
    #python2 UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 7: ordinal not in range(128)
    #1.reload
    #importlib,sys
    #importlib.reload(sys)
    #sys.setdefaultencoding('utf-8') #python3 don't have this attribute.
    #not suggest even in python2 #see:http://stackoverflow.com/questions/3828723/why-should-we-not-use-sys-setdefaultencodingutf-8-in-a-py-script
    #2.overwrite /usr/lib/python2.7/sitecustomize.py or (sitecustomize.py and PYTHONPATH=".:$PYTHONPATH" python)
    #too complex
    #3.control by your own (best)
    #==> all string must be unicode like python3 (u'xx'|b'xx'.encode('utf-8')) (unicode 's disappeared in python3)
    #see: http://blog.ernest.me/post/python-setdefaultencoding-unicode-bytes

    #how to Saving utf-8 texts in json.dumps as UTF8, not as \u escape sequence
    #http://stackoverflow.com/questions/18337407/saving-utf-8-texts-in-json-dumps-as-utf8-not-as-u-escape-sequence
"""

from __future__ import print_function
import json

a = {"b": u"中文"}  # add u for python2 compatibility
print('%r' % a)
print('%r' % json.dumps(a))
print('%r' % (json.dumps(a).encode('utf8')))
a = {"b": u"中文"}
print('%r' % json.dumps(a, ensure_ascii=False))
print('%r' % (json.dumps(a, ensure_ascii=False).encode('utf8')))
# print(a.encode('utf8')) #AttributeError: 'dict' object has no attribute 'encode'
print('')

# python2:bytes=str; python3:bytes
b = a['b'].encode('utf-8')
print('%r' % b)
print('%r' % b.decode("utf-8"))
print('')

# python2:unicode; python3:str=unicode
c = b.decode('utf-8')
print('%r' % c)
print('%r' % c.encode('utf-8'))
"""
#python2
{'b': u'\u4e2d\u6587'}
'{"b": "\\u4e2d\\u6587"}'
'{"b": "\\u4e2d\\u6587"}'
u'{"b": "\u4e2d\u6587"}'
'{"b": "\xe4\xb8\xad\xe6\x96\x87"}'

'\xe4\xb8\xad\xe6\x96\x87'
u'\u4e2d\u6587'

u'\u4e2d\u6587'
'\xe4\xb8\xad\xe6\x96\x87'

#python3
{'b': '中文'}
'{"b": "\\u4e2d\\u6587"}'
b'{"b": "\\u4e2d\\u6587"}'
'{"b": "中文"}'
b'{"b": "\xe4\xb8\xad\xe6\x96\x87"}'

b'\xe4\xb8\xad\xe6\x96\x87'
'中文'

'中文'
b'\xe4\xb8\xad\xe6\x96\x87'
"""
Cheney
sumber
5

Inilah solusi saya menggunakan json.dump ():

def jsonWrite(p, pyobj, ensure_ascii=False, encoding=SYSTEM_ENCODING, **kwargs):
    with codecs.open(p, 'wb', 'utf_8') as fileobj:
        json.dump(pyobj, fileobj, ensure_ascii=ensure_ascii,encoding=encoding, **kwargs)

tempat SYSTEM_ENCODING diatur ke:

locale.setlocale(locale.LC_ALL, '')
SYSTEM_ENCODING = locale.getlocale()[1]
Neit Sabes
sumber
4

Gunakan codec jika memungkinkan,

with codecs.open('file_path', 'a+', 'utf-8') as fp:
    fp.write(json.dumps(res, ensure_ascii=False))
Yulin GUO
sumber
1

Terima kasih atas jawaban aslinya di sini. Dengan python 3 baris kode berikut:

print(json.dumps(result_dict,ensure_ascii=False))

ok Pertimbangkan untuk mencoba tidak menulis terlalu banyak teks dalam kode jika itu tidak penting.

Ini mungkin cukup baik untuk konsol python. Namun, untuk memenuhi server Anda mungkin perlu mengatur lokal seperti yang dijelaskan di sini (jika ada di apache2) http://blog.dscpl.com.au/2014/09/setting-lang-and-lcall-when-using .html

pada dasarnya instal he_IL atau bahasa lokal apa pun di ubuntu periksa itu tidak diinstal

locale -a 

instal di mana XX adalah bahasa Anda

sudo apt-get install language-pack-XX

Sebagai contoh:

sudo apt-get install language-pack-he

tambahkan teks berikut ke / etc / apache2 / envvrs

export LANG='he_IL.UTF-8'
export LC_ALL='he_IL.UTF-8'

Daripada semoga Anda tidak mendapatkan kesalahan python dari apache seperti:

print (js) UnicodeEncodeError: codec 'ascii' tidak dapat menyandikan karakter di posisi 41-45: ordinal tidak dalam jangkauan (128)

Juga di apache coba utf melakukan pengkodean default seperti yang dijelaskan di sini:
Bagaimana cara mengubah pengkodean default ke UTF-8 untuk Apache?

Lakukan lebih awal karena kesalahan apache dapat menyulitkan untuk debug dan Anda dapat secara keliru menganggapnya dari python yang mungkin tidak terjadi dalam situasi itu

sivi
sumber
1

Jika Anda memuat string JSON dari file & isi file teks arab. Maka ini akan berhasil.

Asumsikan File seperti: arabic.json

{ 
"key1" : "لمستخدمين",
"key2" : "إضافة مستخدم"
}

Dapatkan konten arab dari file arabic.json

with open(arabic.json, encoding='utf-8') as f:
   # deserialises it
   json_data = json.load(f)
   f.close()


# json formatted string
json_data2 = json.dumps(json_data, ensure_ascii = False)

Untuk menggunakan Data JSON di Template Django ikuti langkah-langkah berikut:

# If have to get the JSON index in Django Template file, then simply decode the encoded string.

json.JSONDecoder().decode(json_data2)

selesai! Sekarang kita bisa mendapatkan hasilnya sebagai indeks JSON dengan nilai arab.

Chandan Sharma
sumber
fh.close() fhtidak terdefinisi.
AMC
Sudah dikoreksi sekarang. Itu akan menjadif.close()
Chandan Sharma
0

gunakan unicode-escape untuk menyelesaikan masalah

>>>import json
>>>json_string = json.dumps("ברי צקלה")
>>>json_string.encode('ascii').decode('unicode-escape')
'"ברי צקלה"'

menjelaskan

>>>s = '漢  χαν  хан'
>>>print('unicode: ' + s.encode('unicode-escape').decode('utf-8'))
unicode: \u6f22  \u03c7\u03b1\u03bd  \u0445\u0430\u043d

>>>u = s.encode('unicode-escape').decode('utf-8')
>>>print('original: ' + u.encode("utf-8").decode('unicode-escape'))
original:   χαν  хан

sumber asli :https://blog.csdn.net/chuatony/article/details/72628868

ChrisXiao
sumber
-3

Menggunakan sure_ascii = Salah di json.dumps adalah arah yang benar untuk menyelesaikan masalah ini, seperti yang ditunjukkan oleh Martijn. Namun, ini dapat menimbulkan pengecualian:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 1: ordinal not in range(128)

Anda memerlukan pengaturan tambahan di salah satu site.py atau sitecustomize.py untuk mengatur sys.getdefaultencoding () Anda benar. site.py berada di bawah lib / python2.7 / dan sitecustomize.py berada di bawah lib / python2.7 / paket-situs.

Jika Anda ingin menggunakan site.py, di bawah def setencoding (): ubah yang pertama jika 0: menjadi jika 1: sehingga python akan menggunakan lokal sistem operasi Anda.

Jika Anda lebih suka menggunakan sitecustomize.py, yang mungkin tidak ada jika Anda belum membuatnya. cukup cantumkan baris berikut:

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

Kemudian Anda dapat melakukan beberapa output json Cina dalam format utf-8, seperti:

name = {"last_name": u"王"}
json.dumps(name, ensure_ascii=False)

Anda akan mendapatkan string yang dikodekan utf-8, dan bukannya Anda melepaskan string json.

Untuk memverifikasi penyandian default Anda:

print sys.getdefaultencoding()

Anda harus mendapatkan "utf-8" atau "UTF-8" untuk memverifikasi pengaturan site.py atau sitecustomize.py Anda.

Harap dicatat bahwa Anda tidak dapat melakukan sys.setdefaultencoding ("utf-8") di konsol python interaktif.

Ryan X
sumber
2
tidak. Jangan lakukan itu. Memodifikasi karakter penyandian default tidak ada hubungannya dengan json's ensure_ascii=False. Berikan contoh kode lengkap minimal jika Anda berpikir sebaliknya.
jfs
Anda hanya mendapatkan pengecualian ini jika Anda memasukkan string byte non-ASCII (misalnya bukan nilai Unicode) atau mencoba menggabungkan nilai JSON yang dihasilkan (string Unicode) dengan string byte non-ASCII. Mengatur pengkodean default ke UTF-8 pada dasarnya menutupi masalah mendasar jika Anda tidak mengelola data string Anda dengan benar.
Martijn Pieters