Guardar textos utf-8 en json.dumps como UTF8, no como \ u secuencia de escape

474

Código de muestra:

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

El problema: no es legible para humanos. Mis usuarios (inteligentes) quieren verificar o incluso editar archivos de texto con volcados JSON (y prefiero no usar XML).

¿Hay alguna manera de serializar objetos en cadenas JSON UTF-8 (en lugar de \uXXXX)?

Berry Tsakala
fuente
99
+ para ברי צקלה :)))
rubmz

Respuestas:

642

Use el ensure_ascii=Falseinterruptor para json.dumps(), luego codifique el valor en UTF-8 manualmente:

>>> 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 está escribiendo en un archivo, simplemente utilícelo json.dump()y déjelo al objeto de archivo para codificar:

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

Advertencias para Python 2

Para Python 2, hay algunas advertencias más a tener en cuenta. Si está escribiendo esto en un archivo, puede usarlo en io.open()lugar de open()producir un objeto de archivo que codifique valores Unicode para usted a medida que escribe, luego use json.dump()en su lugar para escribir en ese archivo:

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

Tenga en cuenta que hay un error en el jsonmódulo donde la ensure_ascii=Falsebandera puede producir una mezcla de unicodey strobjetos. La solución para Python 2 es:

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, cuando use cadenas de bytes (escriba str ), codificadas en UTF-8, asegúrese de establecer también la encodingpalabra clave:

>>> 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
fuente
73

Para escribir en un archivo

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)

Para imprimir en stdout

import json
print(json.dumps({"message":"xin chào việt nam"}, ensure_ascii=False))
Trần Quang Hiệp
fuente
1
SyntaxError: carácter no ASCII '\ xc3' en el archivo json-utf8.py en la línea 5, pero no se ha declarado codificación; ver python.org/dev/peps/pep-0263 para más detalles
Alex
¡Gracias! No me di cuenta de que era así de simple. Solo debe tener cuidado si los datos que está convirtiendo a json son datos no confiables del usuario.
Karim Sonbol
@ Alex, ¿descubriste cómo evitar ese problema?
Gabriel Fair
@ Gabriel, francamente, no me acuerdo. No era algo tan importante dejar a un lado el fragmento :(
Alex
Solo funcionó para mí usando la codecsbiblioteca. ¡Gracias!
igorkf
29

ACTUALIZACIÓN: Esta es una respuesta incorrecta, pero sigue siendo útil entender por qué está mal. Ver comentarios.

¿Qué tal unicode-escape?

>>> d = {1: "ברי צקלה", 2: u"ברי צקלה"}
>>> json_str = json.dumps(d).decode('unicode-escape').encode('utf8')
>>> print json_str
{"1": "ברי צקלה", "2": "ברי צקלה"}
monitorius
fuente
99
unicode-escapeno es necesario: podría usarlo json.dumps(d, ensure_ascii=False).encode('utf8')en su lugar. Y no está garantizado que json use exactamente las mismas reglas que el unicode-escapecódec en Python en todos los casos, es decir, el resultado puede o no ser el mismo en algunos casos de esquina. El voto negativo es para una conversión innecesaria y posiblemente incorrecta. Sin relación: print json_strfunciona solo para entornos locales utf8 o si PYTHONIOENCODINGenvvar especifica utf8 aquí (imprima Unicode en su lugar).
jfs
3
Otro problema: cualquier comilla doble en los valores de cadena perderá su escape, por lo que esto dará como resultado una salida JSON rota .
Martijn Pieters
error en Python3: AttributeError: el objeto 'str' no tiene atributo 'decode'
Gank
1
¡Unicode-escape funciona bien! Aceptaría esta respuesta como correcta.
Trabajador
@jfs No, json.dumps(d, ensure_ascii=False).encode('utf8')no funciona, al menos para mí. Me estoy poniendo el UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position ...error. La unicode-escapevariante funciona bien sin embargo.
probado el
24

La solución de Python 2 de Peters falla en un caso límite:

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)

Estaba fallando en la parte .decode ('utf8') de la línea 3. Solucioné el problema al simplificar mucho el programa al evitar ese paso y la carcasa especial de 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
fuente
2
El 'caso límite' fue simplemente un error tonto no probado de mi parte. Su unicode(data)enfoque es la mejor opción en lugar de utilizar el manejo de excepciones. Tenga en cuenta que el encoding='utf8'argumento de la palabra clave no tiene nada que ver con la salida que json.dumps()produce; se usa para decodificar la strentrada que recibe la función.
Martijn Pieters
2
@MartijnPieters: o más simple: open('filename', 'wb').write(json.dumps(d, ensure_ascii=False).encode('utf8'))funciona tanto si dumpsdevuelve (ascii-only) str o unicode object.
jfs
@JFSebastian: correcto, porque str.encode('utf8') decodifica implícitamente primero. Pero también lo hace unicode(data), si se le da un strobjeto. :-) Sin io.open()embargo, el uso le brinda más opciones, incluido el uso de un códec que escribe una lista de materiales y está siguiendo los datos JSON con otra cosa.
Martijn Pieters
@MartijnPieters: la .encode('utf8')variante basada en funciona tanto en Python 2 como en 3 (el mismo código). No hay unicodeen Python 3. Sin relación: los archivos json no deben usar BOM (aunque un analizador json confirmador puede ignorar BOM, ver error 3983 ).
jfs
agregando encoding='utf8'a json.dumpsresuelve el problema. PD: Tengo un texto cirílico para descargar
Max L,
8

A partir de Python 3.7, el siguiente código funciona bien:

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

Salida:

{"symbol": "ƒ"}
Nik
fuente
2
también en python 3.6 (recién verificado).
Berry Tsakala
7

Lo siguiente es mi comprensión de la respuesta de lectura var anterior y 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
fuente
5

Aquí está mi solución 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)

donde SYSTEM_ENCODING se establece en:

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

Use códecs si es posible,

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

Gracias por la respuesta original aquí. Con python 3 la siguiente línea de código:

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

estaba bien. Considere intentar no escribir demasiado texto en el código si no es imprescindible.

Esto podría ser lo suficientemente bueno para la consola de Python. Sin embargo, para satisfacer un servidor, es posible que deba establecer la configuración regional como se explica aquí (si está en apache2) http://blog.dscpl.com.au/2014/09/setting-lang-and-lcall-when-using .html

básicamente instale he_IL o cualquier idioma en ubuntu, verifique que no esté instalado

locale -a 

instálalo donde XX es tu idioma

sudo apt-get install language-pack-XX

Por ejemplo:

sudo apt-get install language-pack-he

agregue el siguiente texto a / etc / apache2 / envvrs

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

Esperamos que no aparezcan errores de Python desde Apache como:

print (js) UnicodeEncodeError: el códec 'ascii' no puede codificar caracteres en la posición 41-45: el ordinal no está en el rango (128)

También en apache intente hacer utf la codificación predeterminada como se explica aquí:
¿Cómo cambiar la codificación predeterminada a UTF-8 para Apache?

Hágalo temprano porque los errores de apache pueden ser difíciles de depurar y puede pensar erróneamente que es de Python, que posiblemente no sea el caso en esa situación

sivi
fuente
1

Si está cargando una cadena JSON desde un archivo y contenido de archivo de textos árabes. Entonces esto funcionará.

Asumir archivo como: arabic.json

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

Obtenga el contenido árabe del archivo 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)

Para usar datos JSON en la plantilla de Django, siga los pasos a continuación:

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

json.JSONDecoder().decode(json_data2)

¡hecho! Ahora podemos obtener los resultados como índice JSON con valor árabe.

Chandan Sharma
fuente
fh.close() fhes indefinido.
AMC
Está corregido ahora. Seríaf.close()
Chandan Sharma
0

use unicode-escape para resolver el problema

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

recurso original: https://blog.csdn.net/chuatony/article/details/72628868

ChrisXiao
fuente
-3

El uso de sure_ascii = False en json.dumps es la dirección correcta para resolver este problema, como señaló Martijn. Sin embargo, esto puede generar una excepción:

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

Necesita configuraciones adicionales en site.py o sitecustomize.py para configurar su sys.getdefaultencoding () correctamente. site.py está bajo lib / python2.7 / y sitecustomize.py está bajo lib / python2.7 / site-packages.

Si desea usar site.py, en def setencoding (): cambie el primer if 0: a if 1: para que python use la configuración regional de su sistema operativo.

Si prefiere usar sitecustomize.py, que puede no existir si no lo ha creado. simplemente pon estas líneas:

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

Luego puede hacer algo de salida json china en formato utf-8, como:

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

Obtendrá una cadena codificada utf-8, en lugar de una cadena json escapada.

Para verificar su codificación predeterminada:

print sys.getdefaultencoding()

Debería obtener "utf-8" o "UTF-8" para verificar su configuración de site.py o sitecustomize.py.

Tenga en cuenta que no puede hacer sys.setdefaultencoding ("utf-8") en la consola interactiva de Python.

Ryan X
fuente
2
No. No lo hagas La modificación de la codificación de caracteres predeterminada no tiene nada que ver con json's ensure_ascii=False. Proporcione un ejemplo de código completo mínimo si piensa lo contrario.
jfs
Solo obtiene esta excepción si alimenta cadenas de bytes no ASCII (por ejemplo, no valores Unicode) o intenta combinar el valor JSON resultante (una cadena Unicode) con una cadena de bytes no ASCII. Establecer la codificación predeterminada en UTF-8 está esencialmente ocultando un problema subyacente en el que no está administrando sus datos de cadena correctamente.
Martijn Pieters