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

474

образец кода:

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

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

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

Ягода Цакала
источник
9
+ для ברי צקלה :)))
rubmz

Ответы:

642

Используйте 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()и оставьте его объекту file для кодирования:

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 при использовании байтовых строк (тип 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']
ברי צקלה
Мартейн Питерс
источник
73

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

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))
Trần Quang Hiệp
источник
1
SyntaxError: не-ASCII символ '\ xc3' в файле json-utf8.py в строке 5, но кодировка не объявлена; см. python.org/dev/peps/pep-0263 для получения подробной информации
Алекс
Спасибо! Я не понимал, что это было так просто. Вам нужно быть осторожным только в том случае, если данные, которые вы конвертируете в json, не заслуживают доверия со стороны пользователя.
Карим Сонбол
@ Алекс, ты понял, как избежать этой проблемы?
Габриэль Ярмарка
@ Габриэль, честно говоря, я не помню. Это не было чем-то настолько важным, чтобы отложить отрывок :(
Алекс
Работал только для меня, используя codecsбиблиотеку. Спасибо!
igorkf
29

ОБНОВЛЕНИЕ: Это неправильный ответ, но все же полезно понять, почему это неправильно. Смотрите комментарии.

Как насчет unicode-escape?

>>> d = {1: "ברי צקלה", 2: u"ברי צקלה"}
>>> json_str = json.dumps(d).decode('unicode-escape').encode('utf8')
>>> print json_str
{"1": "ברי צקלה", "2": "ברי צקלה"}
monitorius
источник
9
unicode-escapeне обязательно: вы можете использовать json.dumps(d, ensure_ascii=False).encode('utf8')вместо этого. И не гарантируется, что json во всех случаях будет использовать те же правила, что и unicode-escapeкодек в Python, т. Е. Результат может совпадать или не совпадать в некоторых угловых случаях. Понижение - за ненужное и, возможно, неправильное обращение. Несвязанный: работает только для языков utf8 или если envvar задает здесь utf8 (вместо этого выведите Unicode). print json_strPYTHONIOENCODING
JFS
3
Другая проблема: любые двойные кавычки в строковых значениях потеряют свое экранирование, поэтому это приведет к некорректному выводу JSON .
Мартин Питерс
ошибка в Python3: AttributeError: у объекта 'str' нет атрибута 'decode'
Gank
1
Unicode-escape отлично работает! Я бы принял этот ответ как правильный.
рабочий
@jfs Нет, json.dumps(d, ensure_ascii=False).encode('utf8')не работает, по крайней мере для меня. Я получаю UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position ...ошибку. unicode-escapeВариант работает отлично однако.
тестирование turing
24

Обходной путь Peters 2 для 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"}
Джонатан Рэй
источник
2
«Краевой случай» был просто глупой непроверенной ошибкой с моей стороны. Ваш unicode(data)подход является лучшим вариантом, чем использование обработки исключений. Обратите внимание, что encoding='utf8'ключевое слово аргумент не имеет ничего общего с выводом, который json.dumps()производит; используется для декодирования strвходных данных, которые получает функция.
Мартин Питерс
2
@MartijnPieters: или проще: open('filename', 'wb').write(json.dumps(d, ensure_ascii=False).encode('utf8'))он работает независимо от того, dumpsвозвращает ли (только для ascii) объект str или unicode.
JFS
@JFSebastian: верно, потому что сначала str.encode('utf8') декодируется неявно. Но так же unicode(data), если дан strобъект. :-) Использование io.open()дает вам больше возможностей, включая использование кодека, который записывает спецификацию, и вы отслеживаете данные JSON чем-то другим.
Мартин Питерс
@MartijnPieters: .encode('utf8')вариант на основе работает как на Python 2 и 3 (один и тот же код). На unicodePython 3 нет. Не имеет отношения: файлы json не должны использовать BOM (хотя подтверждающий анализатор json может игнорировать BOM, см. Ошибку 3983 ).
JFS
добавление encoding='utf8'к json.dumpsрешает проблему. PS У меня есть текст на кириллице
Макс. L
8

Начиная с Python 3.7, следующий код работает нормально:

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

Вывод:

{"symbol": "ƒ"}
Nik
источник
2
также в Python 3.6 (только что проверено).
Ягода Цакала
7

Следующее мое понимание var читая ответ выше и гугл.

# 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

Вот мое решение с использованием 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)

где SYSTEM_ENCODING установлен в:

locale.setlocale(locale.LC_ALL, '')
SYSTEM_ENCODING = locale.getlocale()[1]
Нейт Сабес
источник
4

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

with codecs.open('file_path', 'a+', 'utf-8') as fp:
    fp.write(json.dumps(res, ensure_ascii=False))
Юйлинь ГУО
источник
1

Спасибо за оригинальный ответ здесь. С python 3 следующая строка кода:

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

было хорошо Попробуйте не писать слишком много текста в коде, если это не обязательно.

Это может быть достаточно для консоли Python. Однако, чтобы удовлетворить сервер, вам может потребоваться установить локаль, как описано здесь (если он на apache2) http://blog.dscpl.com.au/2014/09/setting-lang-and-lcall-when-using .html

в основном установите he_IL или любой другой языковой стандарт на Ubuntu, проверьте, что он не установлен

locale -a 

установите его там, где XX ваш язык

sudo apt-get install language-pack-XX

Например:

sudo apt-get install language-pack-he

добавьте следующий текст в / etc / apache2 / envvrs

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

Чем, надеюсь, вы не получите ошибок Python от Apache, например:

print (js) UnicodeEncodeError: кодек «ascii» не может кодировать символы в позиции 41-45: порядковый номер не в диапазоне (128)

Также в apache попробуйте сделать utf кодировкой по умолчанию, как описано здесь:
Как изменить кодировку по умолчанию на UTF-8 для Apache?

Сделайте это рано, потому что ошибки apache могут быть болезненными для отладки, и вы можете ошибочно думать, что это от python, что, возможно, не так в этой ситуации

SiVi
источник
1

Если вы загружаете строку JSON из файла и содержимое файла арабскими текстами. Тогда это будет работать.

Предположим, что файл как: arabic.json

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

Получить арабское содержимое из файла 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)

Чтобы использовать данные JSON в шаблоне Django, выполните следующие действия:

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

json.JSONDecoder().decode(json_data2)

сделанный! Теперь мы можем получить результаты в виде индекса JSON с арабским значением.

Чандан Шарма
источник
fh.close() fhне определено
AMC
Это исправлено сейчас. Это было быf.close()
Чандан Шарма
0

использовать Unicode-escape для решения проблемы

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

объяснять

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

Оригинальный ресурс: https://blog.csdn.net/chuatony/article/details/72628868

ChrisXiao
источник
-3

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

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

Вам нужны дополнительные настройки в site.py или sitecustomize.py, чтобы правильно настроить sys.getdefaultencoding (). site.py находится в lib / python2.7 /, а sitecustomize.py находится в lib / python2.7 / site-packages.

Если вы хотите использовать site.py, в def setencoding (): измените первое, если 0: на, если 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 вместо \ u экранированной строки json.

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

print sys.getdefaultencoding()

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

Обратите внимание, что вы не можете выполнить sys.setdefaultencoding ("utf-8") на интерактивной консоли Python.

Райан Х
источник
2
нет. Не делай этого. Изменение кодировки символов по умолчанию не имеет ничего общего с json's ensure_ascii=False. Укажите минимально полный пример кода, если вы думаете иначе.
JFS
Вы получаете это исключение только в том случае, если вы либо вводите байтовые строки, отличные от ASCII (например, не значения Unicode), либо пытаетесь объединить результирующее значение JSON (строка Unicode) со строкой байта не ASCII. Установка кодировки по умолчанию на UTF-8 по существу маскирует основную проблему, если вы не управляете своими строковыми данными должным образом.
Мартин Питерс