Я использую 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 и более поздних версий .
str
Ответы:
Решение с
object_hook
Пример использования:
Как это работает и зачем мне его использовать?
Функция Марка Эмери короче и понятнее, чем эти, так какой в них смысл? Почему вы хотите их использовать?
Чисто для исполнения . Ответ Марка полностью декодирует текст JSON сначала со строками в юникоде, а затем повторяется по всему декодированному значению для преобразования всех строк в строки байтов. Это имеет пару нежелательных эффектов:
Этот ответ устраняет обе эти проблемы с производительностью, используя
object_hook
параметрjson.load
иjson.loads
. Из документов :Поскольку словари, вложенные во многие уровни в других словарях, передаются по
object_hook
мере их декодирования , мы можем байтировать любые строки или списки внутри них на этом этапе и избежать необходимости глубокой рекурсии позже.Ответ Марка не подходит для использования в том виде,
object_hook
в каком он есть, поскольку он повторяется во вложенных словарях. Мы предотвращаем эту рекурсию в этом ответе сignore_dicts
параметром to_byteify
, который передается ему всегда, кроме случаев, когдаobject_hook
он передается новымdict
для byteify.ignore_dicts
Флаг говорит_byteify
игнорироватьdict
S , так как они уже byteified.Наконец, наши реализации
json_load_byteified
иjson_loads_byteified
вызов_byteify
(сignore_dicts=True
) для результата, возвращаемогоjson.load
илиjson.loads
обрабатывающего случай, когда декодируемый текст JSON не имеет adict
на верхнем уровне.источник
return { byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.iteritems() }
сreturn dict((_byteify(key, ignore_dicts=True), _byteify(value, ignore_dicts=True)) for key, value in data.iteritems())
для его работы.json_loads_byteified('[' * 990 + ']' * 990)
. С 991 вылетает. Марк все еще работает с 991byteify(json.loads('[' * 991 + ']' * 991))
. Он падает на 992. Так что, по крайней мере, в этом тесте Марк может пойти глубже, вопреки тому, что вы сказали.Хотя здесь есть несколько хороших ответов, в итоге я использовал PyYAML для анализа моих файлов JSON, поскольку он дает ключи и значения в
str
виде строк типа вместоunicode
типа. Поскольку JSON является подмножеством YAML, он прекрасно работает:Ноты
Некоторые вещи, чтобы отметить, хотя:
Я получаю строковые объекты, потому что все мои записи в кодировке ASCII . Если бы я использовал записи в кодировке Unicode, я бы вернул их обратно в качестве объектов Unicode - конвертации нет!
Вы должны (вероятно, всегда) использовать
safe_load
функцию PyYAML ; если вы используете его для загрузки файлов JSON, вам все равно не понадобится «дополнительная мощность»load
функции.Если вам нужен анализатор YAML, который имеет большую поддержку спецификации версии 1.2 (и правильно анализирует очень малые числа ), попробуйте Ruamel YAML :
pip install ruamel.yaml
и этоimport ruamel.yaml as yaml
было все, что мне было нужно в моих тестах.преобразование
Как указано, конверсии нет! Если вы не можете быть уверены, что имеете дело только со значениями ASCII (и не можете быть уверены в этом большую часть времени), лучше использовать функцию преобразования :
Я использовал пару раз от Mark Amery , он отлично работает и очень прост в использовании.
object_hook
Вместо этого вы можете использовать аналогичную функцию , так как она может повысить производительность больших файлов. Посмотрите чуть более сложный ответ от Mirec Miskuf для этого.источник
yaml.load(json.dumps([u'a', u'£', u'É']))
в оболочке Python и обратите внимание, что вы вернетесь['a', u'\xa3', u'\xc9']
(который содержитunicode
строки). Если вы не можете быть уверены, что ваши данные содержат только символы из набора символов ASCII, вам следует использовать другой подход (я рекомендую свой собственный ответ).[u'a', u'b']
осторожность.Нет встроенной опции, чтобы функции модуля json возвращали строки байтов вместо строк Юникода. Однако эта короткая и простая рекурсивная функция преобразует любой декодированный объект JSON из строк Unicode в строки байтов в кодировке UTF-8:
Просто вызовите это на выходе, который вы получаете от
json.load
илиjson.loads
вызова.Пара заметок:
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
. Ответ Мирека Мискуфа до сих пор является единственным, который справляется правильно, хотя, как следствие, он значительно сложнее, чем мой подход.источник
object_hook
Хотя приведенный ниже ответ на самом деле гораздо хуже, чем этот, но, используяobject_pairs_hook
, вы можете придумать достаточно эффективный метод, который не требует рекурсии или повторного посещения узлов, которые не содержат строк.object_pairs_hook
метод, возможно, немного сложнее для понимания, чем этот (вам нужно понять, как работает параметр и почему списки и разметки требуют другой обработки), и выигрыш в производительности не будет иметь значения для большинства людей ... но я ожидаю он существует, особенно для тех, кто имеет дело с необычно глубоко вложенным объектом JSON.Вы можете использовать
object_hook
параметр дляjson.loads
передачи в конвертере. Вам не нужно делать преобразование после факта.json
Модуль всегда будет проходитьobject_hook
dicts только, и он будет рекурсивно пройти в вложенной dicts, так что вам не придется рекурсия в вложенную dicts себя. Я не думаю, что я бы конвертировал строки Юникода в числа, как показывает Уэллс. Если это строка в кодировке Unicode, она указана в виде строки в файле JSON, поэтому предполагается, что это строка (или файл плохой).Кроме того , я стараюсь не делать что - то подобное
str(val)
наunicode
объекте. Вы должны использоватьvalue.encode(encoding)
правильную кодировку, в зависимости от того, что ожидает ваша внешняя библиотека.Так, например:
источник
s
является JSONObject
(неупорядоченная коллекция пар ключ: значение с символом ':', разделяющим ключ и значение, разделенные запятыми и заключенными в фигурные скобки), но не если это, скажем, JSONArray
. Так что если дано JSONArray
как["a", "b"]
результат все равно будет[u'a', u'b']
. Ни один из других доступных в настоящее время параметров настройки типа ловушки для неjson.loads()
может выполнить работу либо.json
модуль будет рекурсивно передавать вложенныеdict
s, нет необходимости проверять их в двух функциях - поэтому дваelif
пункта, которые их проверяют, должны быть удалены.from Utility import *
, функции не будут видны из-за этого подчеркивания.object_hook
вызывается для каждого анализируемого объекта json, поэтому, если вы вернетесь к тому, что вам дано, вы «перебиваете» вещи, которые вы уже «байтифицировали». Производительность будет расти геометрически с размером объекта. Я включил здесь ответ, который используетobject_pairs_hook
и не страдает от этой проблемы.Это потому, что у json нет различий между строковыми и юникодными объектами. Они все строки в JavaScript.
Я думаю, что JSON подходит для возврата объектов в кодировке Unicode . На самом деле, я бы не стал соглашаться на меньшее, поскольку строки javascript на самом деле являются
unicode
объектами (т.е. строки JSON (javascript) могут хранить любые символы Юникода), поэтому имеет смысл создаватьunicode
объекты при переводе строк из JSON. Простые строки просто не подходят, поскольку библиотека должна будет угадать, какую кодировку вы хотите.Лучше
unicode
везде использовать строковые объекты. Поэтому лучше всего обновить ваши библиотеки, чтобы они могли работать с объектами Unicode.Но если вам действительно нужны байтовые строки, просто закодируйте результаты в кодировку по вашему выбору:
источник
Существует легкий обходной путь.
TL; DR - использовать
ast.literal_eval()
вместоjson.loads()
. Оба такast
иjson
есть в стандартной библиотеке.Хотя это и не идеальный ответ, он довольно далеко уходит, если вы планируете полностью игнорировать Юникод. В Python 2.7
дает:
Это становится более странным, когда некоторые объекты действительно являются строками Unicode. Полный ответ быстро становится волосатым.
источник
null
,true
илиfalse
значение, потому что они не действуют в питоне и приведутliteral_eval()
к сбою.\/
) внутри строки или escape-последовательность Unicode (например"\u0061"
, это другой способ записи"a"
). Буквальный синтаксис Python несовместим с JSON по нескольким причинам, и я бы не стал доверять этому ответу для любого сценария, который я не собирался выбрасывать.json
для сброса данных, просто используйте,print
если запущен Python. Затемast.literal_eval
работаетОтвет Майка Бреннана близок, но нет причин пересматривать всю структуру. Если вы используете параметр
object_hook_pairs
(Python 2.7+):С его помощью вы получаете каждый объект JSON, поэтому вы можете выполнять декодирование без необходимости рекурсии:
Обратите внимание, что мне никогда не придется вызывать ловушку рекурсивно, так как каждый объект будет передан ловушке, когда вы используете
object_pairs_hook
. Вы должны заботиться о списках, но, как видите, объект в списке будет правильно преобразован, и вам не нужно повторяться, чтобы это произошло.РЕДАКТИРОВАТЬ: сотрудник отметил, что Python2.6 не имеет
object_hook_pairs
. Вы все еще можете использовать это Python2.6, сделав очень небольшое изменение. В крюке выше, измените:в
Тогда используйте
object_hook
вместоobject_pairs_hook
:Использование
object_pairs_hook
результатов в одном экземпляре словаря для каждого объекта в объекте JSON, что, если вы анализируете огромный документ, может стоить того.источник
deunicodify_hook
что вы демонстрируете в этом ответе? На данный момент у вас есть реализацияdeunicodify_hook
, которая не перебирает списки и не деуникодифицирует строки и списки внутри них, и, следовательно, вывод, который вы демонстрируете, не соответствует выводу, который ваш хук фактически произведет. Исправьте это, и этот ответ будет выше моего.object_pairs_hook
для объектов вызывается единственное , если ваш текст JSON имеет список строк на верхнем уровне, это решение не будет выполнено. Нет способа исправить это, не вызвав какую-либо функцию для возвращаемой вещиjson.load
; ни один изjson.load
крючков не может гарантировать, что вы сможете справиться с каждой строкой. Я думаю, что это достаточно большой недостаток для меня, чтобы продолжать рекомендовать свое решение, используя крючки.Боюсь, что нет способа достичь этого автоматически в библиотеке simplejson.
Сканер и декодер в simplejson предназначены для вывода текста в Юникоде. Для этого в библиотеке используется функция, которая называется
c_scanstring
(если она доступна, для скорости) илиpy_scanstring
если версия C недоступна.scanstring
Функция вызывается несколько раз почти в каждой программе , которая имеет simplejson для декодирования структуры , которая может содержать текст. Вам нужно будет либо monkeypatchscanstring
значение в simplejson.decoder, либоJSONDecoder
создать подкласс и предоставить практически собственную реализацию всего, что может содержать текст.Причина, по которой simplejson выводит Unicode, заключается в том, что спецификация json специально упоминает, что «Строка - это набор из нуля или более символов Unicode» ... поддержка Unicode предполагается как часть самого формата. Реализация Simplejson
scanstring
заходит так далеко, что сканирует и интерпретирует экранированные символы Юникода (даже проверку ошибок для искаженных многобайтовых представлений наборов символов), поэтому единственный способ, которым он может надежно вернуть значение, - это использовать Unicode.Если у вас есть устаревшая библиотека, в которой требуется библиотека
str
, я рекомендую вам либо тщательно проанализировать вложенную структуру данных после анализа (я признаю, что вы явно сказали, что вы хотели бы избежать ... извините), либо, возможно, обернуть свои библиотеки каким-то образом. фасад, где вы можете массировать входные параметры на более детальном уровне. Второй подход может быть более управляемым, чем первый, если ваши структуры данных действительно глубоко вложены.источник
Как правильно замечает Марк (Амери): Использование десериализатора PyYaml в дампе json работает, только если у вас есть только ASCII. По крайней мере, из коробки.
Два быстрых комментария к подходу PyYaml:
НИКОГДА не используйте yaml.load для данных из поля. Свойство (!) В yaml - выполнять произвольный код, скрытый внутри структуры.
Вы можете заставить это работать также для не ASCII через это:
Но производительность не сравнится с ответом Марка Эмери:
Бросая некоторые глубоко вложенные образцы диктов на два метода, я получаю это (с dt [j] = временная дельта json.loads (json.dumps (m))):
Таким образом, десериализация, включая полное обход дерева и кодирование, вполне соответствует порядку реализации на основе языка j в Си. Я нахожу это удивительно быстрым и более надежным, чем нагрузка yaml в глубоко вложенных структурах. И менее подвержены ошибкам безопасности, глядя на yaml.load.
=> Хотя я был бы признателен за указатель на конвертер, основанный только на C, функция byteify должна быть ответом по умолчанию.
Это особенно верно, если ваша структура json происходит из поля, содержащего пользовательский ввод. Потому что тогда вам, вероятно, нужно все равно пройтись по вашей структуре - независимо от ваших желаемых внутренних структур данных («сэндвич Юникод» или только байтовые строки).
Зачем?
Unicode нормализация . Для незнающих: возьмите обезболивающее и прочитайте это .
Таким образом, используя рекурсию byteify, вы убиваете двух зайцев одним выстрелом:
В моих тестах оказалось, что замена input.encode ('utf-8') на unicodedata.normalize ('NFC', input) .encode ('utf-8') была даже быстрее, чем без NFC - но это сильно зависит от выборки данных, я думаю.
источник
Гоча, что
simplejson
иjson
два различных модуля, по крайней мере , в порядке , они имеют дело с Юникод. Вы имеетеjson
в Py 2.6+, и это дает вам значения Unicode, тогда какsimplejson
возвращает строковые объекты. Просто попробуйте easy_install-ing simplejson в вашей среде и посмотрите, работает ли это. Это для меня.источник
Просто используйте pickle вместо json для dump и load, вот так:
Выходные данные: (строки и целые числа обрабатываются правильно):
источник
safe_load
в YAML, я не знаю, существует ли что-то подобное для маринада .Итак, я столкнулся с той же проблемой. Угадайте, какой был первый результат Google.
Поскольку мне нужно передать все данные в PyGTK, строки Unicode для меня тоже не очень полезны. Так что у меня есть другой метод рекурсивного преобразования. На самом деле это также необходимо для безопасных типов JSON-преобразований - json.dump () будет вызывать любые не-литералы, например объекты Python Не конвертирует индексы dict.
источник
У меня был JSON dict в виде строки. Ключи и значения были объектами Unicode, как в следующем примере:
Я мог бы использовать
byteify
предложенную выше функцию, преобразовав строку вdict
объект, используяast.literal_eval(myStringDict)
.источник
{u'key':u'value'}
это не JSON.Поддержка Python2 & 3 с помощью хука (с https://stackoverflow.com/a/33571117/558397 )
Возвращает:
источник
Это поздно для игры, но я построил этот рекурсивный заклинатель. Это работает для моих нужд, и я думаю, что это относительно полно. Это может помочь вам.
Просто передайте ему объект JSON следующим образом:
Он у меня есть как закрытый член класса, но вы можете использовать метод по своему усмотрению.
источник
json.loads
необходим вызов), произвольно пытается преобразовать строки в целые без объяснения причины и не копирует и не паста готова.Я переписал _parse_json () Уэллса для обработки случаев, когда сам объект json является массивом (мой вариант использования).
источник
Вот рекурсивный кодировщик, написанный на C: https://github.com/axiros/nested_encode
Повышение производительности для «средних» структур составляет около 10% по сравнению с json.loads.
используя эту тестовую структуру:
источник
С Python 3.6 иногда я все еще сталкиваюсь с этой проблемой. Например, когда я получаю ответ от REST API и загружаю текст ответа в JSON, я все равно получаю строки Unicode. Нашел простое решение с помощью json.dumps ().
источник
Я тоже столкнулся с этой проблемой, и, имея дело с JSON, я придумал небольшой цикл, который преобразует Unicode-ключи в строки. (
simplejson
на GAE не возвращает строковые ключи.)obj
это объект, декодированный из JSON:kwargs
это то, что я передаю в конструктор приложения GAE (который не любитunicode
ключи в**kwargs
)Не такой надежный, как решение от Уэллса, но гораздо меньше.
источник
Я приспособил код из ответа на Марк Эмери , в частности , для того , чтобы избавиться от
isinstance
для профи утка-типирования.Кодировка выполняется вручную и
ensure_ascii
отключена. Документы Python дляjson.dump
говорит, чтоОтказ от ответственности: в doctest я использовал венгерский язык. Некоторые известные венгерские кодировки символов:
cp852
используемая кодировка IBM / OEM, например. в DOS (иногда его называют ascii , я думаю, что это неправильно, это зависит от настроек кодовой страницы ),cp1250
например , используется. в Windows (иногда называемый ansi , в зависимости от настроек локали), аiso-8859-2
иногда используется на http-серверах. Тестовый текстTüskéshátú kígyóbűvölő
относится к Koltai László (родная личная форма имени) и из Википедии .Я также хотел бы подчеркнуть ответ на Jarret Гарди , который ссылается на JSON спецификации , процитировать:
В моем случае у меня были файлы с json. Это
utf-8
закодированные файлы.ensure_ascii
в результате получаются правильно экранированные, но не очень читаемые файлы json, поэтому я адаптировал ответ Марка Эмери для своих нужд.Документ не особо продуман, но я делюсь кодом в надежде, что он кому-нибудь пригодится.
источник
json.loads
будут списками или диктатами, а не каким-то определенным пользователем или определенным библиотекой типом, который реализует их методы и магические методы, так почему бы просто не выполнитьisinstance
проверку? Разве это не легче понять, чем проверять существование объектаiteritems
илиiter
принимать объект в качестве аргумента?Проверьте этот ответ на подобный вопрос, который утверждает, что
Префикс u означает, что у вас есть строка Unicode. Когда вы действительно используете строку, она не появится в ваших данных. Не поддавайтесь распечатке.
Например, попробуйте это:
Вы не увидите вас.
источник
'{}'.format({u'x' : u'y'})
все еще включает в себя.