Ошибка Json.dump с 'должен быть unicode, а не str' TypeError

У меня есть json файл, который, случается, содержит множество китайских и японских (и других языков) символов. Я загружаю его в свой python 2.7 script с помощью io.open следующим образом:

with io.open('multiIdName.json', encoding="utf-8") as json_data:
    cards = json.load(json_data)

Я добавляю новое свойство json, все хорошо. Затем я пытаюсь записать его обратно в другой файл:

with io.open("testJson.json",'w',encoding="utf-8") as outfile:
        json.dump(cards, outfile, ensure_ascii=False)

Что, когда я получаю ошибку TypeError: must be unicode, not str

Я пробовал записывать outfile как двоичный файл (with io.open("testJson.json",'wb') as outfile:), но в итоге получилось:

{"multiverseid": 262906, "name": "\u00e6\u00b8\u00b8\u00e9\u009a\u00bc\u00e7\u008b\u00ae\u00e9\u00b9\u00ab", "language": "Chinese Simplified"}

Я думал, что открытие и запись его в одной кодировке будет достаточно, а также флаг secure_ascii, но явно нет. Я просто хочу сохранить символы, которые существовали в файле, прежде чем я запустил script, не превращая их в\u's.

Ответ 1

Можете ли вы попробовать следующее?

with io.open("testJson.json",'w',encoding="utf-8") as outfile:
  outfile.write(unicode(json.dumps(cards, ensure_ascii=False)))

Ответ 2

Причиной этой ошибки является абсолютно глупое поведение json.dumps в Python 2:

>>> json.dumps({'a': 'a'}, ensure_ascii=False)
'{"a": "a"}'
>>> json.dumps({'a': u'a'}, ensure_ascii=False)
u'{"a": "a"}'
>>> json.dumps({'a': 'ä'}, ensure_ascii=False)
'{"a": "\xc3\xa4"}'
>>> json.dumps({u'a': 'ä'}, ensure_ascii=False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/json/__init__.py", line 250, in dumps
    sort_keys=sort_keys, **kw).encode(obj)
  File "/usr/lib/python2.7/json/encoder.py", line 210, in encode
    return ''.join(chunks)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128)

Это связано с тем, что io.open с encoding устанавливает только принимает unicode объекты (что само по себе является правильным) приводит к проблемам.

Тип возврата полностью зависит от того, какой тип ключей или значений в словаре, если ensure_ascii=False, но str всегда возвращается, если ensure_ascii=True. Если вы случайно можете установить 8-битные строки в словари, вы не можете слепо преобразовать этот возвращаемый тип в unicode, потому что вам нужно установить кодировку, предположительно UTF-8:

>>> x = json.dumps(obj, ensure_ascii=False)
>>> if isinstance(x, str):
...     x = unicode(x, 'UTF-8')

В этом случае я считаю, что вы можете использовать json.dump для записи в открытый двоичный файл; однако, если вам нужно сделать что-то более сложное с результирующим объектом, вам, вероятно, понадобится приведенный выше код.


Одним из решений является прекратить все это безумие кодирования/декодирования, переключившись на Python 3.

Ответ 3

Модуль JSON обрабатывает для вас кодировку и декодирование, поэтому вы можете просто открывать входные и выходные файлы в двоичном режиме. Модуль JSON предполагает кодировку UTF-8, но может быть изменен с помощью атрибута encoding в методах load() и dump().

with open('multiIdName.json', 'rb') as json_data:
    cards = json.load(json_data)

то:

with open("testJson.json", 'wb') as outfile:
    json.dump(cards, outfile, ensure_ascii=False)

Забастовкa > Благодаря @Antti Haapala, модуль Python 2.x JSON предоставляет либо Unicode, либо str в зависимости от содержимого объекта.

Вам нужно будет добавить проверочный чек, чтобы убедиться, что результатом является Unicode перед тем, как писать через io:

with io.open("testJson.json", 'w', encoding="utf-8") as outfile:
    my_json_str = json.dumps(my_obj, ensure_ascii=False)
    if isinstance(my_json_str, str):
        my_json_str = my_json_str.decode("utf-8")

    outfile.write(my_json_str)