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


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

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

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

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"}

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": "ƒ"}

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'
"""

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]

4

Use codecs, se possível,

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

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


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.


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


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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.