Вопрос-Ответ

Saving UTF-8 texts with json.dumps as UTF-8, not as a \u escape sequence

Сохранение текстов UTF-8 с помощью json.dumps в формате UTF-8, а не в виде escape-последовательности

Пример кода (в REPL):

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

Вывод:

"\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4"

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

Есть ли способ сериализовать объекты в строки JSON в формате UTF-8 (вместо \uXXXX)?

Переведено автоматически
Ответ 1

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

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

Если вы выполняете запись в файл, просто используйте json.dump() и предоставьте файловому объекту кодировать:

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

Предостережения для Python 2

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

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

Обратите внимание, что в json есть ошибка, из-за которой ensure_ascii=False флаг может создавать сочетание объектов 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))

В Python 2 при использовании байтовых строк (type str), закодированных в 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']
ברי צקלה
Ответ 2

Для записи в файл

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)

Для печати в стандартный вывод

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

Обходной путь Питерса на 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)

Сбой происходил в части .decode ('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"}
Ответ 4

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

Использование unicode-escape:

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