Сохранение текстов utf-8 в json.дампы как UTF8, а не как escape-последовательность



пример кода:



>>> import json
>>> json_string = json.dumps("ברי צקלה")
>>> print json_string
"u05d1u05e8u05d9 u05e6u05e7u05dcu05d4"


проблема: это не удобочитаемое. Мои (умные) пользователи хотят проверять или даже редактировать текстовые файлы с помощью дампов JSON. (и я бы предпочел не использовать XML)



есть ли способ сериализовать объекты в строку utf-8 json (вместо uXXXX)?



это не поможет:



>>> output = json_string.decode('string-escape')
"u05d1u05e8u05d9 u05e6u05e7u05dcu05d4"


этой работает, а если какие-либо подобъекты являются python-unicode, а не utf-8, он будет сбрасывать мусор:



>>> #### ok:
>>> s= json.dumps( "ברי צקלה", ensure_ascii=False)
>>> print json.loads(s)
ברי צקלה

>>> #### NOT ok:
>>> d={ 1: "ברי צקלה", 2: u"ברי צקלה" }
>>> print d
{1: 'xd7x91xd7xa8xd7x99 xd7xa6xd7xa7xd7x9cxd7x94',
2: u'xd7x91xd7xa8xd7x99 xd7xa6xd7xa7xd7x9cxd7x94'}
>>> s = json.dumps( d, ensure_ascii=False, encoding='utf8')
>>> print json.loads(s)['1']
ברי צקלה
>>> print json.loads(s)['2']
××¨× ×¦×§××
940   8  

8 ответов:

использовать ensure_ascii=False переключатель json.dumps(), затем кодируйте значение в UTF-8 вручную:

>>> json_string = json.dumps(u"ברי צקלה", ensure_ascii=False).encode('utf8')
>>> json_string
'"\xd7\x91\xd7\xa8\xd7\x99 \xd7\xa6\xd7\xa7\xd7\x9c\xd7\x94"'
>>> print json_string
"ברי צקלה"

если вы пишете в файл, вы можете использовать io.open() вместо open() чтобы создать файловый объект, который кодирует значения Unicode для вас, когда вы пишете, затем используйте json.dump() вместо того, чтобы писать в этот файл:

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

в Python 3, встроенный open() псевдоним io.open(). Обратите внимание, что есть ошибка в блоке json модуль где же ensure_ascii=False флаг может производить mix на unicode и str объекты. Обходной путь для 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))

если вы передаете байтовые строки (тип str в Python 2, bytes в Python 3) кодируется в UTF-8, убедитесь, что также установить encoding ключевые слова:

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

отметим, что код вторая выборка не допустимый Юникод; вы дали ему UTF-8 байт в качестве литерала Юникода, что бы никогда работы:

>>> s = u'\xd7\x91\xd7\xa8\xd7\x99 \xd7\xa6\xd7\xa7\xd7\x9c\xd7\x94'
>>> print s
××¨× ×¦×§××
>>> print s.encode('latin1').decode('utf8')
ברי צקלה

только когда я закодировал эту строку в Latin 1 (чьи кодовые точки unicode отображают один к одному в байты), затем декодируйте как UTF-8, вы видите ожидаемый результат. Это не имеет ничего общего с JSON и все, что связано с тем, что вы используете неправильный ввод. Результат называется a Mojibake.

если вы получили это значение Unicode из строкового литерала, оно было декодировано с использованием неправильного кодека. Это может быть ваш терминал неверно настроенный или что ваш текстовый редактор сохранил исходный код, используя другой кодек, чем то, что вы сказали Python для чтения файла. Или вы получили его из библиотеки, которая применила неправильный кодек. все это не имеет никакого отношения к библиотеке JSON.

легко, как пирог

записать в файл

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)

для печати в stdin

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

обновление: это неправильный ответ, но все же полезно понять, почему это неправильно. Смотрите комментарии.

как о unicode-escape?

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

Peters' python 2 обходной путь терпит неудачу на крайнем случае:

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)

он рухнул на землю .декодируйте ('utf8') часть строки 3. Я исправил проблему, сделав программу намного проще, избегая этого шага, а также специального корпуса 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"}

следующее мое понимание var чтение ответа выше и 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'
"""

вот мое решение с помощью json.дамп():

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)

где SYSTEM_ENCODING имеет значение:

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

используйте кодеки, если это возможно,

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

использование ensure_ascii=False в json.dumps-это правильное направление для решения этой проблемы, как указал Martijn. Однако, это может вызвать исключение:

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

вам нужны дополнительные настройки в любом site.py или sitecustomize.py чтобы установить свою систему.getdefaultencoding() правильно. site.py под Либ/вместо python2.7/ а sitecustomize.py под Либ/вместо python2.7/сайт-пакеты.

если вы хотите использовать site.py, в разделе def setencoding (): измените первый if 0: на if 1: так что python будет использовать локаль вашей операционной системы.

если вы предпочитаете использовать sitecustomize.py, который может не существовать, если вы его не создали. проще говоря эти строки:

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

затем вы можете сделать некоторые китайские JSON выход в формате utf-8, например:

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

вы получите кодированную строку utf-8, а не экранированную строку json.

чтобы проверить кодировку по умолчанию:

print sys.getdefaultencoding()

вы должны получить "utf-8" или "UTF-8", чтобы проверить ваш site.py или sitecustomize.py настройки.

обратите внимание, что вы не могли сделать в sys.setdefaultencoding ("utf-8") на интерактивной консоли python.

Comments

    Ничего не найдено.