Как получить строковые объекты вместо Unicode от JSON?
Я использую Python 2 для разбора JSON из текстовых файлов с кодировкой ASCII.
При загрузке этих файлов с помощью json
или simplejson
все мои строковые значения передаются в объекты Unicode вместо строковых объектов. Проблема в том, что я должен использовать данные с некоторыми библиотеками, которые принимают только строковые объекты. Я не могу изменять библиотеки и не обновлять их.
Можно ли получить строковые объекты вместо Unicode?
пример
>>> import json
>>> original_list = ['a', 'b']
>>> json_list = json.dumps(original_list)
>>> json_list
'["a", "b"]'
>>> new_list = json.loads(json_list)
>>> new_list
[u'a', u'b'] # I want these to be of type 'str', not 'unicode'
Обновить
Этот вопрос был задан давно, когда я застрял с Python 2. Одним из простых и простых решений на сегодняшний день является использование последней версии Python - то есть Python 3 и forward.
Ответы
Ответ 1
Решение с object_hook
import json
def json_load_byteified(file_handle):
return _byteify(
json.load(file_handle, object_hook=_byteify),
ignore_dicts=True
)
def json_loads_byteified(json_text):
return _byteify(
json.loads(json_text, object_hook=_byteify),
ignore_dicts=True
)
def _byteify(data, ignore_dicts = False):
# if this is a unicode string, return its string representation
if isinstance(data, unicode):
return data.encode('utf-8')
# if this is a list of values, return list of byteified values
if isinstance(data, list):
return [ _byteify(item, ignore_dicts=True) for item in data ]
# if this is a dictionary, return dictionary of byteified keys and values
# but only if we haven't already byteified it
if isinstance(data, dict) and not ignore_dicts:
return {
_byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
for key, value in data.iteritems()
}
# if it anything else, return it in its original form
return data
Пример использования:
>>> json_loads_byteified('{"Hello": "World"}')
{'Hello': 'World'}
>>> json_loads_byteified('"I am a top-level string"')
'I am a top-level string'
>>> json_loads_byteified('7')
7
>>> json_loads_byteified('["I am inside a list"]')
['I am inside a list']
>>> json_loads_byteified('[[[[[[[["I am inside a big nest of lists"]]]]]]]]')
[[[[[[[['I am inside a big nest of lists']]]]]]]]
>>> json_loads_byteified('{"foo": "bar", "things": [7, {"qux": "baz", "moo": {"cow": ["milk"]}}]}')
{'things': [7, {'qux': 'baz', 'moo': {'cow': ['milk']}}], 'foo': 'bar'}
>>> json_load_byteified(open('somefile.json'))
{'more json': 'from a file'}
Как это работает и почему я буду использовать его?
Функция Mark Amery короче и яснее, чем эти, и что это за точка? Почему вы хотите использовать их?
Чисто для производительности. Отметка ответа полностью декодирует текст JSON с помощью строк unicode, а затем рекурсирует через все декодированное значение, чтобы преобразовать все строки в байтовые строки. Это имеет несколько нежелательных эффектов:
- Копия всей декодированной структуры создается в памяти
- Если ваш объект JSON действительно глубоко вложен (500 уровней или более), вы попадете на максимальную глубину рекурсии Python
Этот ответ смягчает обе эти проблемы производительности с помощью параметра object_hook
json.load
и json.loads
. Из документы:
object_hook
- необязательная функция, которая будет вызываться с результатом любого объектного литерала, декодированного (a dict
). Возвращаемое значение object_hook будет использоваться вместо dict
. Эта функция может использоваться для реализации пользовательских декодеров.
Так как словари, вложенные в многие словари, в глубине других словарей, передаются в object_hook
по мере их декодирования, мы можем побаловать любые строки или списки внутри них в этой точке и избежать необходимости глубокой рекурсии позже.
Отметка ответа не подходит для использования в качестве object_hook
в ее нынешнем виде, потому что она переписывается во вложенные словари. Мы предотвращаем эту рекурсию в этом ответе с параметром ignore_dicts
до _byteify
, который передается ему всегда, за исключением случаев, когда object_hook
передает ему новый dict
для байта. Флаг ignore_dicts
сообщает _byteify
игнорировать dict
, поскольку они уже были привязаны.
Наконец, наши реализации json_load_byteified
и json_loads_byteified
вызывают _byteify
(с ignore_dicts=True
) в результате, возвращаемом из json.load
или json.loads
, чтобы обрабатывать случай, когда текст JSON, декодируемый, не имеют dict
на верхнем уровне.
Ответ 2
В то время как есть несколько хороших ответов, я закончил использование PyYAML для анализа моих файлов JSON, поскольку он дает ключи и значения как строки str
вместо типа unicode
. Поскольку JSON является подмножеством YAML, он работает красиво:
>>> import json
>>> import yaml
>>> list_org = ['a', 'b']
>>> list_dump = json.dumps(list_org)
>>> list_dump
'["a", "b"]'
>>> json.loads(list_dump)
[u'a', u'b']
>>> yaml.safe_load(list_dump)
['a', 'b']
Примечания
Некоторые примечания:
-
Я получаю строковые объекты, потому что все мои записи ASCII закодированы. Если бы я использовал кодированные записи в Юникоде, я бы получил их обратно как объекты unicode - конверсии не было!
-
Вы должны (вероятно, всегда) использовать функцию PyYAML safe_load
; если вы используете его для загрузки файлов JSON, вам все равно не потребуется "дополнительная мощность" функции load
.
-
Если вы хотите, чтобы парсер YAML, у которого больше поддержки версии 1.2 спецификации (и правильно обрабатывал очень низкие номера), попробуйте Ruamel YAML: pip install ruamel.yaml
и import ruamel.yaml as yaml
было всем, что мне нужно в моих тестах.
Конверсия
Как уже говорилось, конверсии нет! Если вы не можете быть уверены, что имеете дело только со значениями ASCII (и вы не можете быть уверены в большинстве случаев), лучше используйте функцию преобразования:
Я использовал один из Mark Amery пару раз, он отлично работает и очень прост в использовании. Вместо этого вы можете использовать аналогичную функцию как object_hook
, так как это может повысить производительность больших файлов. Подробнее см. В ответе от Mirec Miskuf.
Ответ 3
Нет встроенной опции, чтобы функции json-модуля возвращали байтовые строки вместо строк unicode. Однако эта короткая и простая рекурсивная функция преобразует любой декодированный объект JSON с использованием строк юникода в строки байтов с кодировкой UTF-8:
def byteify(input):
if isinstance(input, dict):
return {byteify(key): byteify(value)
for key, value in input.iteritems()}
elif isinstance(input, list):
return [byteify(element) for element in input]
elif isinstance(input, unicode):
return input.encode('utf-8')
else:
return input
Просто вызовите это на выходе, который вы получаете от вызова json.load
или json.loads
.
Несколько примечаний:
- Чтобы поддерживать Python 2.6 или более раннюю версию, замените
return {byteify(key): byteify(value) for key, value in input.iteritems()}
на return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])
, поскольку словарные возможности не поддерживались до Python 2.7.
- Поскольку этот ответ рекурсирует через весь декодированный объект, он имеет пару нежелательных характеристик производительности, которых можно избежать при очень тщательном использовании параметров
object_hook
или object_pairs_hook
. Ответ Mirec Miskuf до сих пор является единственным, кому удается правильно это сделать, хотя, как следствие, это значительно сложнее, чем мой подход.
Ответ 4
Вы можете использовать параметр object_hook
для json.loads
для передачи в конвертер. После этого вам не нужно делать конверсию. Модуль json
всегда будет передавать только теги object_hook
, и он будет рекурсивно проходить в вложенных диктофонах, поэтому вам не придется самостоятельно переписывать вложенные dicts, Я не думаю, что я бы преобразовал строки unicode в числа, подобные шоу Wells. Если это строка в Юникоде, она была указана как строка в файле JSON, поэтому она должна быть строкой (или файл плох).
Кроме того, я попытался бы сделать что-то вроде str(val)
для объекта unicode
. Вы должны использовать value.encode(encoding)
с допустимой кодировкой, в зависимости от того, что ожидает ваша внешняя библиотека.
Итак, например:
def _decode_list(data):
rv = []
for item in data:
if isinstance(item, unicode):
item = item.encode('utf-8')
elif isinstance(item, list):
item = _decode_list(item)
elif isinstance(item, dict):
item = _decode_dict(item)
rv.append(item)
return rv
def _decode_dict(data):
rv = {}
for key, value in data.iteritems():
if isinstance(key, unicode):
key = key.encode('utf-8')
if isinstance(value, unicode):
value = value.encode('utf-8')
elif isinstance(value, list):
value = _decode_list(value)
elif isinstance(value, dict):
value = _decode_dict(value)
rv[key] = value
return rv
obj = json.loads(s, object_hook=_decode_dict)
Ответ 5
Это потому, что json не имеет разницы между строковыми объектами и объектами unicode. Это все строки в javascript.
Я думаю, что JSON прав на возвращение объектов Unicode. На самом деле, я не принимал бы ничего меньше, поскольку строки javascript на самом деле unicode
objects (то есть строки JSON (javascript) могут хранить любой символ юникода), поэтому имеет смысл создать unicode
объектов при переводе строк из JSON. Обычные строки просто не подходят, поскольку библиотека должна угадать требуемую кодировку.
Лучше использовать везде unicode
строковые объекты. Таким образом, ваш лучший вариант - обновить ваши библиотеки, чтобы они могли работать с объектами unicode.
Но если вам действительно нужны байты, просто скопируйте результаты в кодировку по вашему выбору:
>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']
Ответ 6
Существует легкая работа.
TL; DR - используйте ast.literal_eval()
вместо json.loads()
. Оба ast
и json
находятся в стандартной библиотеке.
Хотя это не "идеальный" ответ, он получает довольно много, если ваш план состоит в том, чтобы полностью игнорировать Unicode. В Python 2.7
import json, ast
d = { 'field' : 'value' }
print "JSON Fail: ", json.loads(json.dumps(d))
print "AST Win:", ast.literal_eval(json.dumps(d))
дает:
JSON Fail: {u'field': u'value'}
AST Win: {'field': 'value'}
Это становится более волосатым, когда некоторые объекты действительно являются строками Unicode. Полный ответ становится быстро волосистым.
Ответ 7
Я боюсь, что нет возможности добиться этого автоматически в библиотеке simplejson.
Сканер и декодер в simplejson предназначены для создания текста в Юникоде. Для этого библиотека использует функцию c_scanstring
(если она доступна, для скорости) или py_scanstring
, если версия C недоступна. Функция scanstring
вызывается несколько раз почти каждой процедурой, которую simplejson имеет для декодирования структуры, которая может содержать текст. Вам нужно либо обезвредить значение scanstring
в файле simplejson.decoder, либо подклассе JSONDecoder
и предоставить практически всю вашу реализацию всего, что может содержать текст.
Причина, по которой simplejson выводит unicode, однако, заключается в том, что json spec специально упоминает, что "строка представляет собой набор из нуля или более Unicode символы"... поддержка unicode принимается как часть самого формата. Реализация Simplejson scanstring
идет так далеко, что позволяет сканировать и интерпретировать экраны unicode (даже проверку ошибок для искаженных представлений нескольких байтов), поэтому единственный способ, который он может достоверно вернуть вам, - это как unicode.
Если у вас есть старая библиотека, которая нуждается в str
, я рекомендую вам либо трудоемко искать вложенную структуру данных после разбора (я признаю, что вы явно сказали, что хотите избежать... извините), или, возможно, оберните ваши библиотеки в каком-то фасаде, где вы можете массировать входные параметры на более узком уровне. Второй подход может быть более управляемым, чем первый, если ваши структуры данных действительно глубоко вложены.
Ответ 8
Ответ Майка Бреннана близок, но нет причин перешагивать всю структуру. Если вы используете параметр object_hook_pairs
(Python 2.7+):
object_pairs_hook
- это необязательная функция, которая будет вызываться с результатом любого литерала объекта, декодированного с упорядоченным списком пар. Возвращаемое значение object_pairs_hook
будет использоваться вместо dict
. Эта функция может использоваться для реализации пользовательских декодеров, которые полагаются на порядок декодирования пар ключей и значений (например, collections.OrderedDict
будет помнить порядок вставки). Если object_hook
также определен, приоритет object_pairs_hook
.
С его помощью вы получаете каждый объект JSON, переданный вам, поэтому вы можете выполнить декодирование без необходимости рекурсии:
def deunicodify_hook(pairs):
new_pairs = []
for key, value in pairs:
if isinstance(value, unicode):
value = value.encode('utf-8')
if isinstance(key, unicode):
key = key.encode('utf-8')
new_pairs.append((key, value))
return dict(new_pairs)
In [52]: open('test.json').read()
Out[52]: '{"1": "hello", "abc": [1, 2, 3], "def": {"hi": "mom"}, "boo": [1, "hi", "moo", {"5": "some"}]}'
In [53]: json.load(open('test.json'))
Out[53]:
{u'1': u'hello',
u'abc': [1, 2, 3],
u'boo': [1, u'hi', u'moo', {u'5': u'some'}],
u'def': {u'hi': u'mom'}}
In [54]: json.load(open('test.json'), object_pairs_hook=deunicodify_hook)
Out[54]:
{'1': 'hello',
'abc': [1, 2, 3],
'boo': [1, 'hi', 'moo', {'5': 'some'}],
'def': {'hi': 'mom'}}
Обратите внимание, что мне никогда не нужно вызывать крючок рекурсивно, так как каждый объект будет передан на крючок, когда вы используете object_pairs_hook
. Вам нужно заботиться о списках, но, как вы можете видеть, объект внутри списка будет правильно преобразован, и вам не придется рекурсивно выполнять его.
EDIT: Сотрудник указал, что Python2.6 не имеет object_hook_pairs
. Вы все равно можете использовать это будет Python2.6, сделав очень небольшое изменение. В верхнем крючке измените:
for key, value in pairs:
к
for key, value in pairs.iteritems():
Затем используйте object_hook
вместо object_pairs_hook
:
In [66]: json.load(open('test.json'), object_hook=deunicodify_hook)
Out[66]:
{'1': 'hello',
'abc': [1, 2, 3],
'boo': [1, 'hi', 'moo', {'5': 'some'}],
'def': {'hi': 'mom'}}
Использование object_pairs_hook
приводит к созданию менее одного словаря для каждого объекта в объекте JSON, который, если бы вы разбирали огромный документ, может стоить времени.
Ответ 9
Как правильно отмечает Mark (Amery): Использование дескриптора PyYaml в json-дампе работает только в том случае, если у вас только ASCII. По крайней мере, из коробки.
Два быстрых комментария к подходу PyYaml:
-
НИКОГДА использовать yaml.load для данных из поля. Его функция (!) Yaml выполняет произвольный код, скрытый внутри структуры.
-
Вы можете заставить его работать и для не ASCII с помощью этого:
def to_utf8(loader, node):
return loader.construct_scalar(node).encode('utf-8')
yaml.add_constructor(u'tag:yaml.org,2002:str', to_utf8)
Но производительность по сравнению с Mark Amery не соответствует:
Бросьте некоторые глубоко вложенные образцы dicts на два метода, я получаю это (с dt [j] = дельта времени json.loads(json.dumps(m))):
dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j]
dt[byteify recursion(Mark Amery)] =~ 5 * dt[j]
Итак, десериализация, включающая полное хождение по дереву и кодирование, а также в порядке величины реализации на основе json C. Я нахожу это замечательно быстрым, а также более надежным, чем нагрузка ямля на глубоко вложенные структуры. И меньше подверженности ошибкам безопасности, глядя на yaml.load.
= > Хотя я был бы признателен за указатель на конвертер, основанный только на C, функция byteify должна быть ответом по умолчанию.
Это особенно актуально, если ваша json-структура находится из поля, содержащего пользовательский ввод. Потому что тогда вам, вероятно, придется все равно ходить по вашей структуре - независимо от ваших желаемых внутренних структур данных (только для юникод-бутербродов или байтовых строк).
Почему?
Юникод нормализация. Для незнания: возьмите болеутоляющее средство и прочитайте это.
Итак, используя рекурсию byteify, вы убиваете двух зайцев одним камнем:
- получить ваши байты из вложенных дампов json
- получить значения пользовательских значений, нормализованные, чтобы вы нашли материал в своем хранилище.
В моих тестах оказалось, что замена input.encode('utf-8') на unicodedata.normalize('NFC', input).encode('utf-8') была даже быстрее, чем без учета NFC - но это сильно зависит от данных выборки, которые, как я полагаю.
Ответ 10
Получено, что simplejson
и json
- это два разных модуля, по крайней мере, так, как они относятся к юникоду. У вас есть json
в py 2.6+, и это дает вам значения unicode, тогда как simplejson
возвращает строковые объекты. Просто попробуйте easy_install-ing simplejson в вашей среде и посмотрите, работает ли это. Это было для меня.
Ответ 11
Итак, я столкнулся с той же проблемой. Угадайте, каков был первый результат Google.
Поскольку мне нужно передать все данные в PyGTK, строки unicode мне тоже не очень полезны. Поэтому у меня есть другой метод рекурсивного преобразования. На самом деле это также необходимо для преобразования типов JSON - json.dump() закладывает на любые нелитералы, такие как объекты Python. Однако не конвертирует индексы dict.
# removes any objects, turns unicode back into str
def filter_data(obj):
if type(obj) in (int, float, str, bool):
return obj
elif type(obj) == unicode:
return str(obj)
elif type(obj) in (list, tuple, set):
obj = list(obj)
for i,v in enumerate(obj):
obj[i] = filter_data(v)
elif type(obj) == dict:
for i,v in obj.iteritems():
obj[i] = filter_data(v)
else:
print "invalid object in data, converting to string"
obj = str(obj)
return obj
Ответ 12
Просто используйте pickle вместо json для сброса и загрузки, например:
import json
import pickle
d = { 'field1': 'value1', 'field2': 2, }
json.dump(d,open("testjson.txt","w"))
print json.load(open("testjson.txt","r"))
pickle.dump(d,open("testpickle.txt","w"))
print pickle.load(open("testpickle.txt","r"))
Вывод, который он производит, (правильные строки и целые числа):
{u'field2': 2, u'field1': u'value1'}
{'field2': 2, 'field1': 'value1'}
Ответ 13
Поддержка Python2 & 3 с помощью hook (от fooobar.com/questions/29132/...)
import requests
import six
from six import iteritems
requests.packages.urllib3.disable_warnings() # @UndefinedVariable
r = requests.get("http://echo.jsontest.com/key/value/one/two/three", verify=False)
def _byteify(data):
# if this is a unicode string, return its string representation
if isinstance(data, six.string_types):
return str(data.encode('utf-8').decode())
# if this is a list of values, return list of byteified values
if isinstance(data, list):
return [ _byteify(item) for item in data ]
# if this is a dictionary, return dictionary of byteified keys and values
# but only if we haven't already byteified it
if isinstance(data, dict):
return {
_byteify(key): _byteify(value) for key, value in iteritems(data)
}
# if it anything else, return it in its original form
return data
w = r.json(object_hook=_byteify)
print(w)
Возврат:
{'three': '', 'key': 'value', 'one': 'two'}
Ответ 14
Это уже поздно, но я создал этот рекурсивный заклинатель. Он работает для моих нужд, и я думаю, что он относительно завершен. Это может помочь вам.
def _parseJSON(self, obj):
newobj = {}
for key, value in obj.iteritems():
key = str(key)
if isinstance(value, dict):
newobj[key] = self._parseJSON(value)
elif isinstance(value, list):
if key not in newobj:
newobj[key] = []
for i in value:
newobj[key].append(self._parseJSON(i))
elif isinstance(value, unicode):
val = str(value)
if val.isdigit():
val = int(val)
else:
try:
val = float(val)
except ValueError:
val = str(val)
newobj[key] = val
return newobj
Просто передайте объект JSON следующим образом:
obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)
У меня это как частный член класса, но вы можете перепрофилировать метод по своему усмотрению.
Ответ 15
Я переписал Wells _parse_json() для обработки случаев, когда сам объект json является массивом (мой вариант использования).
def _parseJSON(self, obj):
if isinstance(obj, dict):
newobj = {}
for key, value in obj.iteritems():
key = str(key)
newobj[key] = self._parseJSON(value)
elif isinstance(obj, list):
newobj = []
for value in obj:
newobj.append(self._parseJSON(value))
elif isinstance(obj, unicode):
newobj = str(obj)
else:
newobj = obj
return newobj
Ответ 16
У меня был JSON dict в виде строки. Ключи и значения были объектами юникода, как в следующем примере:
myStringDict = "{u'key':u'value'}"
Я мог бы использовать функцию byteify
, предложенную выше, путем преобразования строки в объект dict
с помощью ast.literal_eval(myStringDict)
.
Ответ 17
здесь рекурсивный кодировщик, написанный на C:
https://github.com/axiros/nested_encode
Накладные расходы на производительность для "средних" структур примерно на 10% по сравнению с json.loads.
python speed.py
json loads [0.16sec]: {u'a': [{u'b': [[1, 2, [u'\xd6ster..
json loads + encoding [0.18sec]: {'a': [{'b': [[1, 2, ['\xc3\x96ster.
time overhead in percent: 9%
используя эту тестовую структуру:
import json, nested_encode, time
s = """
{
"firstName": "Jos\\u0301",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "\\u00d6sterreich",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
}
],
"children": [],
"spouse": null,
"a": [{"b": [[1, 2, ["\\u00d6sterreich"]]]}]
}
"""
t1 = time.time()
for i in xrange(10000):
u = json.loads(s)
dt_json = time.time() - t1
t1 = time.time()
for i in xrange(10000):
b = nested_encode.encode_nested(json.loads(s))
dt_json_enc = time.time() - t1
print "json loads [%.2fsec]: %s..." % (dt_json, str(u)[:20])
print "json loads + encoding [%.2fsec]: %s..." % (dt_json_enc, str(b)[:20])
print "time overhead in percent: %i%%" % (100 * (dt_json_enc - dt_json)/dt_json)
Ответ 18
Отметьте this ответ на аналогичный вопрос, например:
Префикс u- означает, что у вас есть строка Unicode. Когда вы действительно используете строку, она не будет отображаться в ваших данных. Не бросайте печатный результат.
Например, попробуйте следующее:
print mail_accounts[0]["i"]
Вы не увидите u.
Ответ 19
С Python 3.6 иногда я сталкиваюсь с этой проблемой. Например, когда вы получаете ответ от REST API и загружаете текст ответа в JSON, я все равно получаю строки unicode. Нашел простое решение с помощью json.dumps().
response_message = json.loads(json.dumps(response.text))
print(response_message)
Ответ 20
Я столкнулся с этой проблемой и, имея дело с JSON, я придумал небольшой цикл, который преобразует ключи unicode в строки. (simplejson
в GAE не возвращает строковые ключи.)
obj
- объект, декодированный из JSON:
if NAME_CLASS_MAP.has_key(cls):
kwargs = {}
for i in obj.keys():
kwargs[str(i)] = obj[i]
o = NAME_CLASS_MAP[cls](**kwargs)
o.save()
kwargs
- это то, что я передаю конструктору приложения GAE (который не любит клавиши unicode
в **kwargs
)
Не так прост, как решение от Wells, но намного меньше.
Ответ 21
Я адаптировал код из ответа Mark Amery, в частности, чтобы избавиться из isinstance
для плюсов печати.
Кодирование выполняется вручную, а ensure_ascii
отключено. Документы python для json.dump
говорят, что
Если security_ascii имеет значение True (по умолчанию), все символы, отличные от ASCII, выводятся с последовательностями \uXXXX
Отказ от ответственности: в доктрине я использовал венгерский язык. Некоторые заметные кодировки символов в Венгрии: cp852
используется кодировка IBM/OEM, например. в DOS (иногда называемый ascii, неправильно, я думаю, это зависит от настройки кодовой страницы), cp1250
используется, например. в Windows (иногда называемый ansi, в зависимости от настроек локали) и iso-8859-2
, иногда используемых на http-серверах. Тестовый текст Tüskéshátú kígyóbűvölő
приписывается Koltai László (родная форма личного имени) и находится от wikipedia.
# coding: utf-8
"""
This file should be encoded correctly with utf-8.
"""
import json
def encode_items(input, encoding='utf-8'):
u"""original from: https://stackoverflow.com/a/13101776/611007
adapted by SO/u/611007 (20150623)
>>>
>>> ## run this with `python -m doctest <this file>.py` from command line
>>>
>>> txt = u"Tüskéshátú kígyóbűvölő"
>>> txt2 = u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"
>>> txt3 = u"uúuutifu"
>>> txt4 = b'u\\xfauutifu'
>>> # txt4 shouldn't be 'u\\xc3\\xbauutifu', string content needs double backslash for doctest:
>>> assert u'\\u0102' not in b'u\\xfauutifu'.decode('cp1250')
>>> txt4u = txt4.decode('cp1250')
>>> assert txt4u == u'u\\xfauutifu', repr(txt4u)
>>> txt5 = b"u\\xc3\\xbauutifu"
>>> txt5u = txt5.decode('utf-8')
>>> txt6 = u"u\\u251c\\u2551uutifu"
>>> there_and_back_again = lambda t: encode_items(t, encoding='utf-8').decode('utf-8')
>>> assert txt == there_and_back_again(txt)
>>> assert txt == there_and_back_again(txt2)
>>> assert txt3 == there_and_back_again(txt3)
>>> assert txt3.encode('cp852') == there_and_back_again(txt4u).encode('cp852')
>>> assert txt3 == txt4u,(txt3,txt4u)
>>> assert txt3 == there_and_back_again(txt5)
>>> assert txt3 == there_and_back_again(txt5u)
>>> assert txt3 == there_and_back_again(txt4u)
>>> assert txt3.encode('cp1250') == encode_items(txt4, encoding='utf-8')
>>> assert txt3.encode('utf-8') == encode_items(txt5, encoding='utf-8')
>>> assert txt2.encode('utf-8') == encode_items(txt, encoding='utf-8')
>>> assert {'a':txt2.encode('utf-8')} == encode_items({'a':txt}, encoding='utf-8')
>>> assert [txt2.encode('utf-8')] == encode_items([txt], encoding='utf-8')
>>> assert [[txt2.encode('utf-8')]] == encode_items([[txt]], encoding='utf-8')
>>> assert [{'a':txt2.encode('utf-8')}] == encode_items([{'a':txt}], encoding='utf-8')
>>> assert {'b':{'a':txt2.encode('utf-8')}} == encode_items({'b':{'a':txt}}, encoding='utf-8')
"""
try:
input.iteritems
return {encode_items(k): encode_items(v) for (k,v) in input.iteritems()}
except AttributeError:
if isinstance(input, unicode):
return input.encode(encoding)
elif isinstance(input, str):
return input
try:
iter(input)
return [encode_items(e) for e in input]
except TypeError:
return input
def alt_dumps(obj, **kwargs):
"""
>>> alt_dumps({'a': u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"})
'{"a": "T\\xc3\\xbcsk\\xc3\\xa9sh\\xc3\\xa1t\\xc3\\xba k\\xc3\\xadgy\\xc3\\xb3b\\xc5\\xb1v\\xc3\\xb6l\\xc5\\x91"}'
"""
if 'ensure_ascii' in kwargs:
del kwargs['ensure_ascii']
return json.dumps(encode_items(obj), ensure_ascii=False, **kwargs)
Я также хотел бы выделить ответ Jarret Hardie, который ссылается на спецификация JSON, цитируя:
Строка представляет собой набор из нуля или более символов Unicode
В моем случае использования у меня были файлы с json. Это utf-8
закодированные файлы. ensure_ascii
приводит к правильно экранированным, но не очень читаемым json файлам, поэтому я адаптировал ответ Марка Эмери для удовлетворения моих потребностей.
Учение не особенно задумано, но я разделяю код в надежде, что он будет полезен для кого-то.