Sauvegarde des textes utf-8 dans json.dumps en UTF8, pas en tant que séquence d'échappement \ u

474

exemple de code:

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

Le problème: ce n'est pas lisible par l'homme. Mes utilisateurs (intelligents) veulent vérifier ou même modifier des fichiers texte avec des vidages JSON (et je préfère ne pas utiliser XML).

Existe-t-il un moyen de sérialiser des objets en chaînes JSON UTF-8 (au lieu de \uXXXX)?

Berry Tsakala
la source
9
+ pour ברי צקלה :)))
rubmz

Réponses:

642

Utilisez le ensure_ascii=Falsecommutateur à json.dumps(), puis encodez la valeur en UTF-8 manuellement:

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

Si vous écrivez dans un fichier, utilisez-le json.dump()et laissez-le à l'objet fichier à encoder:

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

Avertissements pour Python 2

Pour Python 2, il y a d'autres mises en garde à prendre en compte. Si vous écrivez ceci dans un fichier, vous pouvez utiliser io.open()au lieu de open()pour produire un objet fichier qui code pour vous les valeurs Unicode pendant que vous écrivez, puis utilisez json.dump()plutôt pour écrire dans ce fichier:

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

Notez qu'il y a un bogue dans le jsonmodule où le ensure_ascii=Falsedrapeau peut produire un mélange d' objets unicodeet str. La solution de contournement pour Python 2 est alors:

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

En Python 2, lorsque vous utilisez des chaînes d'octets (type str), codées en UTF-8, assurez-vous de définir également le encodingmot - clé:

>>> 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
la source
73

Pour écrire dans un fichier

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)

Pour imprimer sur stdout

import json
print(json.dumps({"message":"xin chào việt nam"}, ensure_ascii=False))
Trần Quang Hiệp
la source
1
SyntaxError: caractère non ASCII '\ xc3' dans le fichier json-utf8.py à la ligne 5, mais aucun encodage n'a été déclaré; voir python.org/dev/peps/pep-0263 pour plus de détails
Alex
Je vous remercie! Je ne savais pas que c'était aussi simple que ça. Vous ne devez être prudent que si les données que vous convertissez en json sont des entrées utilisateur non fiables.
Karim Sonbol
@Alex, avez-vous compris comment éviter ce problème?
Gabriel Fair
@Gabriel franchement, je ne me souviens pas. Ce n'était pas quelque chose d'aussi important de mettre un extrait de côté :(
Alex
Ne fonctionnait que pour moi en utilisant la codecsbibliothèque. Merci!
igorkf
29

MISE À JOUR: C'est une mauvaise réponse, mais il est toujours utile de comprendre pourquoi elle est mauvaise. Voir les commentaires.

Et alors unicode-escape?

>>> d = {1: "ברי צקלה", 2: u"ברי צקלה"}
>>> json_str = json.dumps(d).decode('unicode-escape').encode('utf8')
>>> print json_str
{"1": "ברי צקלה", "2": "ברי צקלה"}
monitorius
la source
9
unicode-escapen'est pas nécessaire: vous pouvez utiliser à la json.dumps(d, ensure_ascii=False).encode('utf8')place. Et il n'est pas garanti que json utilise exactement les mêmes règles que le unicode-escapecodec en Python dans tous les cas, c'est-à-dire que le résultat peut être ou ne pas être le même dans certains cas. Le downvote est pour une conversion inutile et peut-être fausse. Indépendant: print json_strne fonctionne que pour les locales utf8 ou si PYTHONIOENCODINGenvvar spécifie utf8 ici (imprimez plutôt Unicode).
jfs
3
Autre problème: les guillemets doubles dans les valeurs de chaîne perdront leur échappement, ce qui entraînera une sortie JSON cassée .
Martijn Pieters
erreur dans Python3: AttributeError: l'objet 'str' n'a pas d'attribut 'decode'
Gank
1
unicode-escape fonctionne très bien! J'accepterais cette réponse comme correcte.
Travailleur
@jfs Non, json.dumps(d, ensure_ascii=False).encode('utf8')ne fonctionne pas, du moins pour moi. Je reçois une UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position ...erreur. La unicode-escapevariante fonctionne très bien cependant.
testée
24

La solution de contournement de Python 2 de Peters échoue sur un cas de bord:

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)

Il se bloquait sur la partie .decode ('utf8') de la ligne 3. J'ai résolu le problème en rendant le programme beaucoup plus simple en évitant cette étape ainsi que le boîtier spécial d'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
la source
2
Le «cas de bord» était simplement une erreur stupide et non testée de ma part. Votre unicode(data)approche est la meilleure option plutôt que d'utiliser la gestion des exceptions. Notez que l' encoding='utf8'argument mot - clé n'a rien à voir avec la sortie qui json.dumps()produit; il est utilisé pour décoder l' strentrée reçue par la fonction.
Martijn Pieters
2
@MartijnPieters: or simpler: open('filename', 'wb').write(json.dumps(d, ensure_ascii=False).encode('utf8'))Cela fonctionne si dumpsretourne (ascii uniquement) str ou objet unicode.
jfs
@JFSebastian: à droite, car str.encode('utf8') décode implicitement en premier. Mais il en va de même unicode(data)si on lui donne un strobjet. :-) L'utilisation io.open()vous offre cependant plus d'options, notamment l'utilisation d'un codec qui écrit une nomenclature et vous suivez les données JSON avec autre chose.
Martijn Pieters
@MartijnPieters: une .encode('utf8')variante basée sur fonctionne à la fois sur Python 2 et 3 (le même code). Il n'y en a pas unicodesur Python 3. Sans rapport: les fichiers json ne doivent pas utiliser la nomenclature (bien qu'un analyseur json confirmant puisse ignorer la nomenclature, voir errate 3983 ).
jfs
ajoutant encoding='utf8'à json.dumpsrésoudre le problème. PS J'ai un texte cyrillique à vider
Max L
8

Depuis Python 3.7, le code suivant fonctionne correctement:

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

Production:

{"symbol": "ƒ"}
Nik
la source
2
également en python 3.6 (juste vérifié).
Berry Tsakala
7

Ce qui suit est ma compréhension de la lecture de var var réponse ci-dessus et 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
la source
5

Voici ma solution en utilisant 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)

où SYSTEM_ENCODING est défini sur:

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

Utilisez des codecs si possible,

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

Merci pour la réponse originale ici. Avec python 3, la ligne de code suivante:

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

c'était OK. Pensez à ne pas écrire trop de texte dans le code si ce n'est pas impératif.

Cela pourrait être suffisant pour la console python. Cependant, pour satisfaire un serveur, vous devrez peut-être définir les paramètres régionaux comme expliqué ici (si c'est sur apache2) http://blog.dscpl.com.au/2014/09/setting-lang-and-lcall-when-using .html

installez essentiellement he_IL ou toute langue locale sur ubuntu, vérifiez qu'il n'est pas installé

locale -a 

installez-le où XX est votre langue

sudo apt-get install language-pack-XX

Par exemple:

sudo apt-get install language-pack-he

ajoutez le texte suivant à / etc / apache2 / envvrs

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

Avec un peu de chance, vous n'obtiendrez pas d'erreurs python sur apache comme:

print (js) UnicodeEncodeError: le codec 'ascii' ne peut pas coder les caractères en position 41-45: l'ordinal n'est pas dans la plage (128)

Toujours dans apache, essayez de faire utf l'encodage par défaut comme expliqué ici:
Comment changer l'encodage par défaut en UTF-8 pour Apache?

Faites-le tôt car les erreurs Apache peuvent être difficiles à déboguer et vous pouvez penser à tort que cela provient de Python, ce qui n'est peut-être pas le cas dans cette situation.

sivi
la source
1

Si vous chargez une chaîne JSON à partir d'un fichier et de son contenu, des textes arabes. Ensuite, cela fonctionnera.

Supposons un fichier comme: arabic.json

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

Obtenez le contenu arabe du fichier 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)

Pour utiliser les données JSON dans le modèle Django, procédez comme suit:

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

json.JSONDecoder().decode(json_data2)

terminé! Maintenant, nous pouvons obtenir les résultats sous forme d'index JSON avec une valeur arabe.

Chandan Sharma
la source
fh.close() fhn'est pas défini.
AMC
IL est maintenant corrigé. Ce seraitf.close()
Chandan Sharma
0

utiliser unicode-escape pour résoudre le problème

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

Explique

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

ressource d'origine: https://blog.csdn.net/chuatony/article/details/72628868

ChrisXiao
la source
-3

L'utilisation de Ensure_ascii = False dans json.dumps est la bonne direction pour résoudre ce problème, comme l'a souligné Martijn. Cependant, cela peut soulever une exception:

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

Vous avez besoin de paramètres supplémentaires dans site.py ou sitecustomize.py pour définir votre sys.getdefaultencoding () correctement. site.py est sous lib / python2.7 / et sitecustomize.py est sous lib / python2.7 / site-packages.

Si vous souhaitez utiliser site.py, sous def setencoding (): remplacez le premier if 0: par if 1: afin que python utilise les paramètres régionaux de votre système d'exploitation.

Si vous préférez utiliser sitecustomize.py, qui peut ne pas exister si vous ne l'avez pas créé. mettez simplement ces lignes:

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

Ensuite, vous pouvez faire une sortie json chinoise au format utf-8, telle que:

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

Vous obtiendrez une chaîne encodée en utf-8, plutôt que \ u une chaîne json échappée.

Pour vérifier votre encodage par défaut:

print sys.getdefaultencoding()

Vous devriez obtenir "utf-8" ou "UTF-8" pour vérifier vos paramètres site.py ou sitecustomize.py.

Veuillez noter que vous ne pouvez pas faire sys.setdefaultencoding ("utf-8") sur la console python interactive.

Ryan X
la source
2
non. Ne le fais pas. Modification de codage de caractères par défaut n'a rien à voir avec json« s ensure_ascii=False. Fournissez un exemple de code complet minimal si vous pensez le contraire.
jfs
Vous obtenez cette exception uniquement si vous alimentez des chaînes d'octets non ASCII (par exemple, pas des valeurs Unicode) ou si vous essayez de combiner la valeur JSON résultante (une chaîne Unicode) avec une chaîne d'octets non ASCII. La définition du codage par défaut sur UTF-8 masque essentiellement un problème sous-jacent si vous ne gérez pas correctement vos données de chaîne.
Martijn Pieters