что такое json в python
Работа с данными в формате JSON в Python
Перевод статьи Working With JSON Data in Python.
Начиная с момента своего создания JSON быстро стал стандартом де-факто для обмена данными между приложениями, а также их частями. И скорее всего вы читаете эту статью потому, что вам необходимо куда либо передать/принять данные или возможно вы через API своего приложения обрабатываете информацию в формате JSON, а затем сохраняете её. Так или иначе, но вы наконец добрались до этого непонятного JSON и теперь вам необходимо обработать данные в этом формате с помощью Python. К счастью это достаточно простая задача, и как в большинстве подобных случаев Python делает ее выполнение легким.
Итак, мы собираемся использовать JSON для хранения и обмена данными. JSON — это не что иное, как стандартизованный формат, который сообщество разработчиков уже достаточно давно использует для передачи и хранения данных. Имейте в виду, что JSON — не единственное решение, использующееся для подобного рода задач, но вероятно только XML и YAML являются единственными альтернативами о которых стоит упомянуть в первую очередь.
(Очень) Краткое введение в JSON
JavaScript Object Notation (JSON) создавался под влиянием парадигм языка JavaScript и связан с ним схожим синтаксисом описания объектного литерала. Существует отличный сайт, который введет вас в курс дела. Тем не менее JSON уже давно отделился от языка Javascript и существует как собственный стандарт, поэтому в этой статье мы можем с удовольствием избежать обсуждения особенностей программирования на JavaScript. В конечном итоге сообщество разработчиков в целом приняло JSON, так как его легко создавать, а также понимать как людям так и машинам.
Внимание, это JSON!
Файлы в формате JSON доступны для чтения и записи средствами всех языков программирования Cи-стиля, а Python как раз является таковым! Ниже приводится фрагмент кода, содержащий личные данные пользователя в виде литерала объекта и закодированного в формате JSON.
Как не трудно заметить, JSON поддерживает примитивные типы данных, такие как строки и числа, а также сложные: списки и объекты с произвольной вложенностью. Синтаксис представленного выше кода схож с синтаксисом словаря Python, то есть является универсальной нотацией для описания объектов.
Python изначально поддерживает JSON!
Изначально Python поставляется со стандартным (встроенным) модулем json для кодирования и декодирования данных в формате JSON. Для этого просто вставьте в начале вашего файла с кодом программы следующие инструкции:
Основные термины
Процесс кодирования JSON называется сериализацией (serialization). Этот термин обозначает преобразование данных в линейную последовательность байтов для хранения на диске или передачи по сети. Интересуясь материалами по этой тематике, вы также могли слышать термин «маршалинг» (marshaling), но это уже тема для отдельной статьи.
Соответственно, десериализация (deserialization) является обратным процессом, а технически декодированием данных из формата JSON в структуру данных в памяти.
На самом деле проще думать об этих двух взаимообратимых процессах как об обыкновенном чтении и записи данных: кодирование предназначено для записи данных на диск (или передачи по сети), а декодирование — для чтения данных в память и последующей обработки.
Сериализация JSON
Модуль json предоставляет удобный метод dump() для записи данных в файл. Существует также метод dumps() для записи данных в обычную строку. Типы данных Python кодируются в формат JSON в соответствии с интуитивно понятными правилами преобразования, представленными в виде таблице ниже.
Python | JSON |
---|---|
dict | object |
list,tuple | array |
str | string |
int, long, float | number |
True | true |
False | false |
None | null |
Пример простой процедуры сериализации данных
Теперь представим, что мы работаем в памяти с объектом следующего вида:
Обратите внимание, на то что метод dump() принимает два аргумента: объект данных, подлежащий сериализации и файлоподобный объект, в который они затем будут записаны после кодирования.
Некоторые полезные именованные аргументы
Напомним JSON должен быть легко читаем для людей. Но что если наши данные будут упакованы в одну строку без отступов и разделения по отдельным строкам. Кроме всего этого, у вас вероятно имеется свой стиль форматирования (styleguide) или же вам проще читать код отформатированный по вашим правилам.
ПРИМЕЧАНИЕ. Оба метода dump() и dumps() используют одни и те же именованные аргументы.
Десериализация JSON
JSON | Python |
---|---|
object | dict |
array | list |
string | str |
number (int) | int |
number (real) | float |
true | True |
false | False |
null | None |
Технически это преобразование не является в точности обратным к таблице для сериализации данных, рассмотренной нами выше. Это означает, что если вы кодируете объект в формат JSON, а затем декодируете его обратно, то вы можете получить уже не тот объект, каким он был изначально. Простым иллюстрирующим этот факт примером будет кодирование данных с типа кортеж tuple и получение после декодирования данных с типа список list :
Простой пример десериализации данных
Представим теперь, что у вас есть данные, хранящиеся на диске в виде файла, которые вы хотели бы обрабатывать в памяти. Как в задаче выше вы также можете использовать диспетчер контекста, но на этот раз для того, чтобы открыть существующий файл data_file.json в режиме чтения:
Здесь все довольно просто, но имейте в виду, что результат выполнения этого кода будет возвращать результат декодирования, в соответствии с нашей таблицей преобразования типов данных. Об этом важно помнить если вы загружаете из файла данные, состав которых вам заранее неизвестен.
Пример (как бы) из реальной жизни
Для демонстрации нашего «реального» примера мы будем использовать online-сервис JSONPlaceholder. Он представляет собой удаленный источник данных в формате JSON, получаемых по сети по запросу, и могут использоваться для отладки приложений. Вначале создадим файл сценария с именем scratch.py или под любым другим именем. Нам необходимо будет сформировать запрос request к служебному API JSONPlaceholder, для этого мы будем использовать модуль requests. Просто добавьте инструкции импорта в начало файла:
Вы также можете просмотреть содержимое входных данных с помощью браузера, перейдя во входную точку сервиса по следующей ссылке — TODO:
Теперь мы можем манипулировать данными прочитанными из файла в формате JSON и декодированными как с обыкновенным объектом Python. Если мы запустим следующие инструкции в консоли, то получим:
Кодирование и декодирование пользовательских объектов Python
Рассмотрим следующий пример и затем ответим на вопрос. Что произойдёт если мы попытаемся сериализовать класс Elf из приложения Dungeons & Dragons, фрагмент кода которого представлен ниже?
Не удивительно, но Python пожалуется, что Elf не может быть сериализован (not serializable):
И хотя модуль json может обрабатывать большинство встроенных типов данных Python, но по умолчанию он не понимает, как кодировать сложные пользовательские типы данных. Со стороны наш код похож на попытку поместить квадратный штифт в круглое отверстие. Как решать подобные задачи рассмотрим ниже.
Упрощение структур данных
Как работать со сложными структурами данных? Вы можете попытаться кодировать и декодировать JSON вручную контролируя весь процесс, но есть более изящное решение, которое сэкономит ваше время. Вместо того, чтобы попытаться напрямую перейти от пользовательского типа данных к формату JSON с неизвестным результатом, вы можете сделать это через следующий промежуточный шаг.
Все, что вам нужно сделать, это представить ваши данные с точки зрения встроенных в Python (нативных) типов данных, которые модуль json отлично понимает. По сути, вы должны перевести сложный объект в более простое представление, которое затем модуль json трансформирует в JSON. Это похоже на транзитивное свойство отношений элементов в математике: если A = B и B = C, то A = C.
Чтобы опробовать этот прием, нам понадобится любой сложный объект для кодирования. Для примера вы можете использовать любой пользовательский класс, который вам нравится. Но мы используем для этого встроенный в Python тип complex, который применяется для представления комплексных чисел. И по умолчанию он в принципе не сериализуем.
Теперь необходимо задать себе очень важный вопрос. Каков минимальный объем информации, нам необходим для воссоздания объекта? В случае комплексных чисел вам нужно знать значения реальных и мнимых частей числа, которые вы можете получить как атрибуты объекта типа complex:
После передачи полученных значений в конструктор complex() оператор сравнения __eq__ вернёт нам значение True :
Разбиение пользовательских типов данных до отдельных составляющих, состоящих из значений простых базовых типов, имеет решающее значение для успешного результата работы процессов сериализации и десериализации.
Кодирование пользовательских типов данных
Почему же мы кодировали комплексное число как кортеж (tuple)? Хороший вопрос. Это, безусловно, не единственный и не лучший выбор. На самом деле, это не очень хорошее представление данных, в особенности если захотите позже декодировать полученный результат. И вы вскоре убедитесь в этом. Другой общий подход заключается в применении подкласса стандартного класса JSONEncoder и переопределении его метода default() :
Декодирование пользовательских типов данных
Что же нам неизвестно ещё? В рассмотренном нами случае отсутствуют метаданные или информация о типе декодируемых данных. Теперь еще раз зададим себе вопрос: Каков минимальный объем информации, который необходим и достаточен для восстановления этого объекта?
Модуль json ожидает, что все пользовательские типы данных будут отображаться как обычные объекты. Создадим файл JSON complex_data.json и добавим туда объект, представляющий собой комплексное число:
Ключ __complex__ — это метаданные, о которых мы говорили выше. На самом деле не важно с каким значением он ассоциирован. Чтобы этот маленький хак работал, все, что вам нужно это проверить существует ли этот ключ:
Если ваш код не содержит ошибок, то вы получите список объектов типа complex :
Все готово
Поздравляю теперь вы можете использовать всю мощь формата JSON для реализации ваших приложений на языке Python.
Хотя примеры которыми мы рассмотрели, безусловно, чрезмерно упрощены, но иллюстрируют основы процесса работы с форматом данных JSON. Информация изложенная в этой статье поможет вам решить следующие задачи:
Рассмотрение особенностей использования стандартного модуля json упростит вам изучение других модулей Python, использующихся для сериализации данных: pickle и marshal.
Удачи вам в дальнейшем изучении языка Python!
Формат данных JSON в Python
Краткое руководство по использованию JSON в Python
JSON (JavaScript Object Notation) это легковесный формат обмена данными. Людям его легко читать и вести в нем записи, а компьютеры запросто справляются с его синтаксическим анализом и генерацией.
JSON основан на языке программирования JavaScript. Но этот текстовый формат не зависит от языка и среди прочих может использоваться в Python и Perl. В основном его применяют для передачи данных между сервером и веб-приложением.
JSON построен на двух структурах:
JSON в Python
В Python есть ряд пакетов, поддерживающих JSON, в частности metamagic.json, jyson, simplejson, Yajl-Py, ultrajson, и json. В этом руководстве мы будем использовать json, имеющий «родную» поддержку в Python. Для проверки данных JSON мы можем воспользоваться этим сайтом, предоставляющим JSON-линтер.
Ниже приведен пример записи JSON. Как видим, представление данных очень похоже на словари Python.
Конвертируем JSON в объекты Python
Конвертируем объекты Python в JSON
Теперь давайте сравним типы данных в Python и JSON.
Python | JSON |
dict | Object |
list | Array |
tuple | Array |
str | String |
int | Number |
float | Number |
True | true |
False | false |
None | null |
Ниже мы покажем, как сконвертировать некоторые объекты Python в типы данных JSON.
Кортеж Python — в массив JSON
Список Python — в массив JSON
Строка Python — в строку JSON
Булевы значения Python — в булевы значения JSON
Запись в файл JSON
Чтение файлов JSON
json.load vs json.loads
json.load используют для загрузки файла, а json.loads – для загрузки строки (loads расшифровывается как «load string»).
json.dump vs json.dumps
Аналогично, json.dump применяется, если нужно сохранить JSON в файл, а json.dumps (dump string) – если данные JSON нам нужны в виде строки для парсинга или вывода.
Работа с данными JSON в Data Science
Ограничения имплементации
Процесс кодирования в JSON называется сериализацией, а декодирования – десериализацией. Некоторые реализации десериализаторов имеют ограничения на:
Впрочем, подобные ограничения связаны только с типами данных Python и работой самого интерпретатора Python.
Формат JSON в разработке API
Эта программа отправит в браузер что-то вроде следующего:
Работа с данными JSON в Python
С самого начала JSON стал стандартом фактически для обмена информацией.
К счастью, это довольно распространенная задача, и, как и в случае большинства обычных задач, Python делает ее почти максимально легкой.
Краткая история JSON
Не так удивительно,J avaS cript O bject N otation был вдохновлен подмножеством языка программирования JavaScript, имеющим дело с литеральным синтаксисом объекта. У них есть отличный сайт, который объясняет все это. Не беспокойтесь: JSON уже давно стал независимым от языка и существует как собственный стандарт, поэтому, к счастью, мы можем избежать использование JavaScript.
В конечном счете, сообщество в целом приняло JSON, потому что людям и машинам легко создавать и понимать.
Смотри, это JSON!
Приготовьтесь, мы покажем вам немного реальной жизни JSON – точно так же, как вы видели бы там в дикой природе. Это нормально: JSON должен быть доступен для чтения любому, кто использовал язык C-стиля, а Python таковым является!
Как видите, JSON поддерживает примитивные типы, такие как строки и числа, а также вложенные списки и объекты.
Подождите, это похоже на словарь Python! Ведь да? На данный момент это довольно универсальная запись объектов, но я не думаю, что UON скатывается с языка так же хорошо.
Уф! Вы пережили свою первую встречу с каким-то диким JSON. Теперь вам просто нужно научиться приручать его.
Python поддерживает JSON изначально!
Просто подбросьте этого маленького парня вверху вашего файла:
Немного словарного запаса
Сериализация JSON
Что происходит после того, как компьютер обрабатывает много информации? Нужно взять дамп данных. Соответственно, json библиотека предоставляет dump() способ записи данных в файлы. Существует также dumps() метод (произносится как «dump-s») для записи в строку Python.
Простые объекты Python переводятся в JSON в соответствии с довольно интуитивным преобразованием.
Простой пример сериализации
Представьте, что вы работаете с объектом Python в памяти, который выглядит примерно так:
Крайне важно сохранить эту информацию на диске, поэтому ваша задача – записать ее в файл.
Обратите внимание, что dump() принимает два позиционных аргумента: (1) объект данных для сериализации и (2) файлоподобный объект, в который будут записаны байты.
Ура! Вы родили маленького мальчика JSON, и вы готовы выпустить его в дикую природу, чтобы он стал большим и сильным.
Некоторые полезные аргументы ключевого слова
Помните, что JSON должен быть легко читаемым людьми, но читаемый синтаксис не достаточен, если все это сжато вместе. Кроме того, у вас, вероятно, другой стиль программирования, чем у меня, и вам может быть легче читать код, когда он отформатирован по вашему вкусу.
ПРИМЕЧАНИЕ. Оба метода и используют dump() и dumps() одни и те же ключевые аргументы.
Есть и другие, sort_keys но я понятия не имею, что этот человек делает. Вы можете найти весь список в документах, если вам интересно.
Десериализация JSON
Теперь пришло время привести его в форму. В json библиотеке вы найдете load() и loads() для преобразования закодированных данных JSON в объекты Python.
Как и в случае с сериализацией, для десериализации существует простая таблица преобразования, хотя вы, вероятно, уже догадались, как она выглядит.
JSON | Python |
---|---|
object | dict |
array | list |
string | str |
number (целый) | int |
number (действительный) | float |
true | True |
false | False |
null | None |
Технически, это преобразование не является совершенной противоположностью таблице сериализации. По сути, это означает, что если вы кодируете объект сейчас, а потом декодируете его позже, вы не сможете получить точно такой же объект обратно. Я полагаю, это немного похоже на телепортацию: разбейте мои молекулы здесь и соедините их там. Я все еще тот же человек?
В действительности, это скорее похоже на то, как один друг переводит что-то на японский, а другой – обратно на русский. В любом случае, простейшим примером будет кодирование tuple и возврат list после декодирования, например так:
Простой пример десериализации
На этот раз представьте, что на диске хранятся некоторые данные, которыми вы хотите манипулировать в памяти. Вы по-прежнему будете использовать менеджер контекста, но на этот раз вы откроете существующий data_file.json в режиме чтения.
Пример из реального мира (sort of)
В качестве вводного примера вы будете использовать JSONPlaceholder, отличный источник поддельных данных JSON для практических целей.
Сначала создайте файл сценария с именем scratch.py или по желанию.
Вам нужно будет сделать запрос API к службе JSONPlaceholder, поэтому просто используйте requests пакет для выполнения тяжелой работы. Добавьте этот импорт вверху вашего файла:
Теперь вы будете работать со списком TODO, потому что … вы знаете, это обряд или что-то в этом роде.
Хорошо, время для некоторых действий. Вы можете увидеть структуру данных, посетив конечную точку в браузере, но вот пример TODO:
Да, ваша реализация лучше, но суть в том, что теперь вы можете манипулировать данными JSON как обычным объектом Python!
Я не знаю о вас, но когда я снова запускаю скрипт в интерактивном режиме, я получаю следующие результаты:
Это круто и все, но вы здесь, чтобы узнать о JSON. Для окончательного задания вы создадите файл JSON, который содержит заполненные TODO для каждого из пользователей, выполнивших максимальное количество TODO.
Отлично, вы избавились от всех данных, которые вам не нужны, и сохранили полезные вещи в совершенно новый файл! Запустите скрипт еще раз и проверьте, filtered_data_file.json все ли работает. Он будет в том же каталоге, что и scratch.py при запуске.
Теперь, когда вы продвинулись так далеко, держу пари, что вы чувствуете себя довольно жарко, верно? Я склонен согласиться с вами, хотя. Пока что плавание прошло гладко, но вы можете захлестнуть люки на этом последнем отрезке.
Кодирование и декодирование пользовательских объектов Python
Что происходит, когда мы пытаемся сериализовать Elf класс из того приложения Dungeons & Dragons, над которым вы работаете?
Неудивительно, что Python жалуется, что Elf не сериализуем (что вы бы знали, если бы когда-нибудь попытались написать Elf иначе):
Хотя json модуль может обрабатывать большинство встроенных типов Python, он не понимает, как кодировать настраиваемые типы данных по умолчанию. Это все равно что пытаться втиснуть квадратный колышек в круглое отверстие.
Упрощение структур данных
Теперь вопрос в том, как бороться с более сложными структурами данных. Ну, вы можете попытаться кодировать и декодировать JSON вручную, но есть немного более умное решение, которое сэкономит вам немного работы. Вместо прямого перехода от пользовательского типа данных к JSON вы можете добавить промежуточный шаг.
Хороший вопрос, который нужно задать себе при работе с пользовательскими типами: какой минимальный объем информации необходим для воссоздания этого объекта? В случае комплексных чисел вам нужно знать только действительные и мнимые части, к которым вы можете обращаться как к атрибутам complex объекта:
Передача одинаковых чисел в complex конструктор достаточна для выполнения __eq__ оператора сравнения:>>>
Разбиение пользовательских типов данных на их основные компоненты имеет решающее значение как для процессов сериализации, так и десериализации.
Кодирование пользовательских типов
Обратите внимание, что вы должны поднять TypeError если вы не получите объект, который вы ожидали. Таким образом, вы избежите случайной сериализации любых Elf. Теперь вы можете попробовать кодировать сложные объекты!
Другой распространенный подход – создать подкласс стандарта JSONEncoder и переопределить его default() метод:
Вместо того, чтобы поднимать TypeError себя, вы можете просто позволить базовому классу справиться с этим. Вы можете использовать это либо непосредственно в dump() методе через cls параметр, либо создав экземпляр кодировщика и вызвав его encode() метод:>>>
Расшифровка пользовательских типов
Хотя действительные и мнимые части комплексного числа абсолютно необходимы, на самом деле их недостаточно для воссоздания объекта. Вот что происходит, когда вы пытаетесь кодировать комплексное число с помощью ComplexEncoder а затем декодировать результат:
Все, что вы получите, это список, и вам придется передать значения в complex конструктор, если вы захотите снова создать этот сложный объект. Вспомните нашу дискуссию о телепортации. Отсутствуют метаданные или информация о типе данных, которые вы кодируете.
Я полагаю, что вопрос, который вы действительно должны задать себе: каков минимальный объем информации, который необходим и достаточен для воссоздания этого объекта?
json модуль ожидает, что все пользовательские типы должны быть выражены как objects в стандарте JSON. Для разнообразия вы можете создать файл JSON на этот раз complex_data.json и добавить следующее object представляющее комплексное число:
Видите умный бит? Этот «__complex__» ключ – метаданные, о которых мы только что говорили. На самом деле не имеет значения, что это за значение. Чтобы заставить этот маленький взломать работать, все, что вам нужно сделать, это убедиться, что ключ существует:
Если «__complex__» нет в словаре, вы можете просто вернуть объект и позволить декодеру по умолчанию справиться с ним.
Теперь то же, что и раньше:
Это не просто работает с одним объектом. Попробуйте вставить этот список комплексных чисел complex_data.json и снова запустить скрипт:
Если все пойдет хорошо, вы получите список complex объектов:
Все сделано!
Поздравляем, теперь вы можете использовать мощную силу JSON для любого и всех ваших бесчестный Python нуждается.
Хотя примеры, с которыми вы работали здесь, безусловно, надуманы и чрезмерно упрощены, они иллюстрируют рабочий процесс, который вы можете применить к более общим задачам:
То, что вы будете делать со своими данными после их загрузки в память, будет зависеть от вашего варианта использования. Как правило, ваша цель будет собирать данные из источника, извлекать полезную информацию и передавать эту информацию вместе или вести ее учет.