Salvando textos utf-8 no json.dumps como UTF8, não como \ u sequência de escape

474

Código de amostra:

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

O problema: não é legível por humanos. Meus usuários (inteligentes) desejam verificar ou editar arquivos de texto com despejos JSON (e prefiro não usar XML).

Existe uma maneira de serializar objetos em cadeias UTF-8 JSON (em vez de \uXXXX)?

Berry Tsakala
fonte
9
+ para ברי צקלה :)))
rubmz

Respostas:

642

Use a ensure_ascii=Falseopção para json.dumps(), em seguida, codifique o valor para 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())
"ברי צקלה"

Se você estiver gravando em um arquivo, basta usar json.dump()e deixar o objeto de arquivo para codificar:

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

Advertências para Python 2

Para o Python 2, há mais algumas ressalvas a serem consideradas. Se você estiver gravando isso em um arquivo, poderá usar em io.open()vez de open()produzir um objeto de arquivo que codifique valores Unicode para você enquanto escreve, e use json.dump()para gravar nesse arquivo:

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

Note que há um bug no jsonmódulo onde a ensure_ascii=Falsebandeira pode produzir uma mistura de unicodee strobjetos. A solução alternativa para o 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))

No Python 2, ao usar cadeias de bytes (tipo str), codificadas em UTF-8, certifique-se de definir também a encodingpalavra-chave:

>>> 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
fonte
72

Para gravar em um arquivo

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 em stdout

import json
print(json.dumps({"message":"xin chào việt nam"}, ensure_ascii=False))
Trần Quang Hiệp
fonte
1
SyntaxError: caractere não ASCII '\ xc3' no arquivo json-utf8.py na linha 5, mas nenhuma codificação declarada; veja python.org/dev/peps/pep-0263 para obter detalhes
Alex
Obrigado! Não percebi que era assim tão simples. Você só precisa ter cuidado se os dados que você está convertendo para json forem entradas não confiáveis ​​do usuário.
Karim Sonbol
@ Alex você descobriu como evitar esse problema?
Gabriel Fair
@ Gabriel francamente, não me lembro. Não era algo tão importante colocar o trecho de lado :( #
Alex Alex
Só funcionou para mim usando a codecsbiblioteca. Obrigado!
igorkf 23/04
29

ATUALIZAÇÃO: Esta é uma resposta errada, mas ainda é útil entender por que está errada. Ver comentários.

Que tal unicode-escape?

>>> d = {1: "ברי צקלה", 2: u"ברי צקלה"}
>>> json_str = json.dumps(d).decode('unicode-escape').encode('utf8')
>>> print json_str
{"1": "ברי צקלה", "2": "ברי צקלה"}
monitorius
fonte
9
unicode-escapenão é necessário: você pode usar json.dumps(d, ensure_ascii=False).encode('utf8'). E não é garantido que o json use exatamente as mesmas regras do unicode-escapecodec no Python em todos os casos, ou seja, o resultado poderá ou não ser o mesmo em alguns casos de canto. O voto negativo é para uma conversão desnecessária e possivelmente incorreta. Não relacionado: print json_strfunciona apenas para localidades utf8 ou se PYTHONIOENCODINGenvvar especifica utf8 aqui (em vez disso, imprima Unicode).
JFS
3
Outro problema: as aspas duplas nos valores das strings perderão seus escapes, resultando em uma saída JSON interrompida .
Martijn Pieters
erro na Python3: AttributeError: objeto 'str' não tem 'decodificação' atributo
Gank
1
unicode-escape funciona bem! Eu aceitaria esta resposta como correta.
Trabalhador
@jfs Não, json.dumps(d, ensure_ascii=False).encode('utf8')não está funcionando, pelo menos para mim. Estou recebendo UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position ...-error. A unicode-escapevariante funciona bem no entanto.
turingtested
24

Solução alternativa do python 2 de Peters falha em um caso de borda:

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)

Ele estava travando na parte .decode ('utf8') da linha 3. Corrigi o problema, tornando o programa muito mais simples, evitando essa etapa e a cobertura especial do 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
fonte
2
O "caso extremo" foi simplesmente um erro não testado e estúpido da minha parte. Sua unicode(data)abordagem é a melhor opção, em vez de usar o tratamento de exceções. Observe que o encoding='utf8'argumento da palavra - chave não tem nada a ver com a saída que json.dumps()produz; é usado para decodificar a strentrada que a função recebe.
Martijn Pieters
2
@MartijnPieters: ou mais simples: open('filename', 'wb').write(json.dumps(d, ensure_ascii=False).encode('utf8'))funciona se dumpsretorna objeto str ou unicode (somente ASCII).
jfs
@ JFSebastian: certo, porque str.encode('utf8') decodifica implicitamente primeiro. Mas o mesmo acontece unicode(data), se for dado um strobjeto. :-) No io.open()entanto, o uso oferece mais opções, incluindo o uso de um codec que grava uma BOM e você segue os dados JSON com outra coisa.
Martijn Pieters
@MartijnPieters: a .encode('utf8')variante baseada em funciona em Python 2 e 3 (o mesmo código). Não existe unicodeno Python 3. Não relacionado: os arquivos json não devem usar a BOM (embora um analisador json de confirmação possa ignorar a BOM, consulte a página 3983 incorreta ).
JFS
adicionar encoding='utf8'para json.dumpsresolver o problema. PS Eu tenho um texto cirílico para despejar
Max L
8

No Python 3.7, o código a seguir funciona bem:

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

Resultado:

{"symbol": "ƒ"}
Nik
fonte
2
também em python 3.6 (apenas verificado).
Berry Tsakala 13/02/19
7

A seguir, é minha compreensão var leitura resposta acima 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'
"""
Cheney
fonte
5

Aqui está a minha solução 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)

em que SYSTEM_ENCODING está definido como:

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

Use codecs, se possível,

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

Obrigado pela resposta original aqui. No python 3, a seguinte linha de código:

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

foi tudo bem. Considere não escrever muito texto no código, se não for imperativo.

Isso pode ser bom o suficiente para o console python. No entanto, para satisfazer um servidor, pode ser necessário definir o código do idioma conforme explicado aqui (se estiver no apache2) http://blog.dscpl.com.au/2014/09/setting-lang-and-lcall-when-using .html

basicamente instale o he_IL ou qualquer idioma no ubuntu, verifique se não está instalado

locale -a 

instale-o onde XX é o seu idioma

sudo apt-get install language-pack-XX

Por exemplo:

sudo apt-get install language-pack-he

adicione o seguinte texto em / etc / apache2 / envvrs

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

Espero que você não tenha erros de python no apache como:

print (js) UnicodeEncodeError: o codec 'ascii' não pode codificar caracteres na posição 41-45: ordinal fora do intervalo (128)

Também no apache, tente fazer utf a codificação padrão, conforme explicado aqui:
Como alterar a codificação padrão para UTF-8 para Apache?

Faça isso cedo, porque os erros do apache podem ser difíceis de depurar e você pode erroneamente pensar que é do python, o que possivelmente não é o caso nessa situação

sivi
fonte
1

Se você estiver carregando a string JSON de um arquivo e conteúdo do arquivo, os textos em árabe. Então isso vai funcionar.

Suponha que o arquivo seja: arabic.json

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

Obter o conteúdo em árabe do arquivo 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 dados JSON no modelo do Django, siga as etapas abaixo:

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

json.JSONDecoder().decode(json_data2)

feito! Agora podemos obter os resultados como índice JSON com valor em árabe.

Chandan Sharma
fonte
fh.close() fhestá indefinido.
AMC
Está corrigido agora. Seriaf.close()
Chandan Sharma
0

use unicode-escape para resolver o problema

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

explicar

>>>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
fonte
-3

O uso de allow_ascii = False no json.dumps é a direção certa para resolver esse problema, conforme apontado por Martijn. No entanto, isso pode gerar uma exceção:

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

Você precisa de configurações extras em site.py ou sitecustomize.py para definir seu sys.getdefaultencoding () correto. site.py está em lib / python2.7 / e sitecustomize.py está em lib / python2.7 / site-packages.

Se você deseja usar site.py, em def setencoding (): altere o primeiro se 0: para se 1: para que o python use a localidade do sistema operacional.

Se você preferir usar sitecustomize.py, que pode não existir se você não o tiver criado. basta colocar estas linhas:

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

Em seguida, você pode fazer alguma saída json chinesa no formato utf-8, como:

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

Você obterá uma string codificada utf-8, em vez de \ ua string json de escape.

Para verificar sua codificação padrão:

print sys.getdefaultencoding()

Você deve obter "utf-8" ou "UTF-8" para verificar as configurações do site.py ou sitecustomize.py.

Observe que você não pode executar sys.setdefaultencoding ("utf-8") no console python interativo.

Ryan X
fonte
2
não. Não faça isso. A modificação da codificação de caracteres padrão não tem nada a ver com json's ensure_ascii=False. Forneça um exemplo de código completo mínimo, se você pensar o contrário.
JFS
Você obtém essa exceção apenas se alimentar cadeias de bytes não ASCII (por exemplo, não valores Unicode) ou tentar combinar o valor JSON resultante (uma cadeia Unicode) com uma cadeia de bytes não ASCII. Definir a codificação padrão como UTF-8 está basicamente ocultando um problema subjacente, caso você não esteja gerenciando seus dados de string corretamente.
Martijn Pieters