Salvataggio di testi utf-8 in json.dumps come UTF8, non come sequenza di escape


474

codice di esempio:

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

Il problema: non è leggibile dall'uomo. I miei utenti (intelligenti) vogliono verificare o persino modificare i file di testo con i dump JSON (e preferirei non utilizzare XML).

C'è un modo per serializzare gli oggetti nelle stringhe JSON UTF-8 (anziché \uXXXX)?


9
+ for ברי צקלה :)))
rubmz,

Risposte:


642

Utilizzare l' ensure_ascii=Falseopzione per json.dumps(), quindi codificare manualmente il valore in UTF-8:

>>> 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())
"ברי צקלה"

Se stai scrivendo su un file, usa json.dump()e lascialo nell'oggetto file per codificare:

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

Avvertenze per Python 2

Per Python 2, ci sono alcuni avvertimenti da tenere in considerazione. Se stai scrivendo questo in un file, puoi usare io.open()invece di open()produrre un oggetto file che codifica i valori Unicode per te mentre scrivi, quindi usa json.dump()invece per scrivere su quel file:

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

Nota che c'è un bug nel jsonmodulo in cui il ensure_ascii=Falseflag può produrre un mix di unicodee stroggetti. La soluzione alternativa per Python 2 è quindi:

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

In Python 2, quando si utilizzano stringhe di byte (tipo str), codificate in UTF-8, assicurarsi di impostare anche la encodingparola chiave:

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

72

Per scrivere su un 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)

Per stampare su stdout

import json
print(json.dumps({"message":"xin chào việt nam"}, ensure_ascii=False))

1
SyntaxError: carattere non ASCII '\ xc3' nel file json-utf8.py alla riga 5, ma nessuna codifica dichiarata; vedi python.org/dev/peps/pep-0263 per i dettagli
Alex

Grazie! Non mi ero reso conto che fosse così semplice. Devi solo fare attenzione se i dati che stai convertendo in json sono input dell'utente non attendibile.
Karim Sonbol,

@Alex hai capito come evitare questo problema?
Gabriel Fair,

@Gabriel, francamente, non ricordo. Non è stato qualcosa di così importante mettere da parte lo snippet :(
Alex,

Ha funzionato solo per me usando la codecslibreria. Grazie!
igorkf,

29

AGGIORNAMENTO: Questa è una risposta sbagliata, ma è comunque utile capire perché è sbagliata. Vedi commenti

Che ne dici unicode-escape?

>>> d = {1: "ברי צקלה", 2: u"ברי צקלה"}
>>> json_str = json.dumps(d).decode('unicode-escape').encode('utf8')
>>> print json_str
{"1": "ברי צקלה", "2": "ברי צקלה"}

9
unicode-escapenon è necessario: è possibile utilizzare json.dumps(d, ensure_ascii=False).encode('utf8')invece. E non è garantito che json usi esattamente le stesse regole del unicode-escapecodec in Python in tutti i casi, ad esempio, il risultato potrebbe essere o non essere lo stesso in alcuni casi angolari. Il downvote è per una conversione non necessaria e forse sbagliata. Non correlato: print json_strfunziona solo per le versioni locali di utf8 o se PYTHONIOENCODINGenvvar specifica utf8 qui (stampa invece Unicode).
jfs,

3
Un altro problema: le doppie virgolette nei valori di stringa perderanno la loro escape, quindi ciò comporterà un output JSON interrotto .
Martijn Pieters

errore in Python3: AttributeError: l'oggetto 'str' non ha attributo 'decodifica'
Gank

1
unicode-escape funziona bene! Accetterei questa risposta come corretta.
Lavoratore,

@jfs No, json.dumps(d, ensure_ascii=False).encode('utf8')non funziona, almeno per me. Sto diventando UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position ...-error. La unicode-escapevariante funziona comunque bene.
Turingtest del

24

La soluzione alternativa a Peters in Python 2 non riesce in un caso limite:

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)

Si stava arrestando in modo anomalo sulla parte .decode ('utf8') della riga 3. Ho risolto il problema rendendo il programma molto più semplice, evitando quel passaggio e il case speciale di 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"}

2
Il "caso limite" è stato semplicemente un errore non testato da parte mia. Il tuo unicode(data)approccio è l'opzione migliore piuttosto che utilizzare la gestione delle eccezioni. Si noti che l' encoding='utf8'argomento della parola chiave non ha nulla a che fare con l'output che json.dumps()produce; viene utilizzato per l' strinput di decodifica ricevuto dalla funzione.
Martijn Pieters

2
@MartijnPieters: o più semplice: open('filename', 'wb').write(json.dumps(d, ensure_ascii=False).encode('utf8'))funziona se dumpsrestituisce str (solo ASCII) o oggetto unicode.
jfs,

@JFSebastian: giusto, perché prima str.encode('utf8') decodifica implicitamente. Ma anche unicode(data)se viene dato un stroggetto. :-) L'utilizzo io.open()ti offre più opzioni, incluso l'uso di un codec che scrive una DBA e stai seguendo i dati JSON con qualcos'altro.
Martijn Pieters

@MartijnPieters: la .encode('utf8')variante -based funziona su Python 2 e 3 (lo stesso codice). Non esiste unicodePython 3. Non correlato: i file json non devono utilizzare la distinta componenti (sebbene un parser json di conferma possa ignorare la distinta componenti, vedere errore 3983 ).
jfs,

aggiungendo encoding='utf8'per json.dumpsrisolvere il problema. PS Ho un testo in cirillico da scaricare
Max L

8

A partire da Python 3.7 il seguente codice funziona bene:

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

Produzione:

{"symbol": "ƒ"}

2
anche in Python 3.6 (appena verificato).
Berry Tsakala,

7

Quanto segue è la mia comprensione var lettura risposta sopra e 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'
"""

5

Ecco la mia soluzione usando 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)

dove SYSTEM_ENCODING è impostato su:

locale.setlocale(locale.LC_ALL, '')
SYSTEM_ENCODING = locale.getlocale()[1]

4

Usa i codec se possibile,

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

1

Grazie per la risposta originale qui. Con python 3 la seguente riga di codice:

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

era ok. Prova a non scrivere troppo testo nel codice se non è un imperativo.

Questo potrebbe essere abbastanza buono per la console Python. Tuttavia, per soddisfare un server potrebbe essere necessario impostare la locale come spiegato qui (se è su apache2) http://blog.dscpl.com.au/2014/09/setting-lang-and-lcall-when-using .html

fondamentalmente installare he_IL o qualsiasi lingua locale su Ubuntu verificare che non sia installato

locale -a 

installalo dove XX è la tua lingua

sudo apt-get install language-pack-XX

Per esempio:

sudo apt-get install language-pack-he

aggiungi il seguente testo a / etc / apache2 / envvrs

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

Quindi spero che non si verifichino errori di Python da Apache come:

print (js) UnicodeEncodeError: il codec 'ascii' non può codificare i caratteri nella posizione 41-45: ordinale non nell'intervallo (128)

Anche in Apache prova a rendere utf la codifica predefinita come spiegato qui:
Come modificare la codifica predefinita in UTF-8 per Apache?

Fallo presto perché gli errori di Apache possono essere dolorosi per il debug e puoi erroneamente pensare che provenga da Python che probabilmente non è il caso in quella situazione


1

Se si sta caricando una stringa JSON da un file e contenuto di testi in arabo. Quindi funzionerà.

Assumi File come: arabic.json

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

Ottieni i contenuti arabi dal 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)

Per utilizzare i dati JSON nel modello Django seguire i passaggi seguenti:

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

json.JSONDecoder().decode(json_data2)

fatto! Ora possiamo ottenere i risultati come indice JSON con valore arabo.


fh.close() fhnon è definito.
AMC,

Ora è corretto. Sarebbef.close()
Chandan Sharma il

0

usa unicode-escape per risolvere il problema

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

spiegare

>>>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:   χαν  хан

risorsa originale :https://blog.csdn.net/chuatony/article/details/72628868


-3

L'uso di sure_ascii = False in json.dumps è la direzione giusta per risolvere questo problema, come sottolineato da Martijn. Tuttavia, ciò può sollevare un'eccezione:

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

Hai bisogno di impostazioni extra in site.py o sitecustomize.py per impostare sys.getdefaultencoding () in modo corretto. site.py è in lib / python2.7 / e sitecustomize.py è in lib / python2.7 / site-pacchetti.

Se vuoi usare site.py, sotto def setencoding (): modifica il primo if 0: in if 1: in modo che python utilizzi le impostazioni internazionali del tuo sistema operativo.

Se preferisci utilizzare sitecustomize.py, che potrebbe non esistere se non l'hai creato. metti semplicemente queste righe:

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

Quindi è possibile eseguire alcuni output json cinesi in formato utf-8, come ad esempio:

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

Otterrai una stringa codificata utf-8, anziché una stringa json con escape.

Per verificare la codifica predefinita:

print sys.getdefaultencoding()

Dovresti ottenere "utf-8" o "UTF-8" per verificare le impostazioni di site.py o sitecustomize.py.

Si noti che non è possibile eseguire sys.setdefaultencoding ("utf-8") sulla console interattiva di Python.


2
no. Non farlo La modifica della codifica dei caratteri predefinita non ha nulla a che fare con quella jsondi ensure_ascii=False. Fornisci un esempio di codice completo minimo se la pensi diversamente.
jfs,

Si ottiene questa eccezione solo se si inseriscono stringhe di byte non ASCII (ad esempio valori non Unicode) o si tenta di combinare il valore JSON risultante (una stringa Unicode) con una stringa di byte non ASCII. L'impostazione della codifica predefinita su UTF-8 sta essenzialmente mascherando un problema sottostante se non si gestiscono correttamente i dati della stringa.
Martijn Pieters
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.