Как распарсить json файл python

Обновлено: 06.07.2024

JSON в основном поддерживает 6 типов данных в JavaScript:

JSON построен на двух структурах:

  • Он хранит данные в парах имя / значение. Он рассматривается как объект, запись, словарь, хеш-таблица, список с ключами.
  • Упорядоченный список значений рассматривается как массив, вектор, список или последовательность.

Представление данных JSON похоже на словарь Python. Ниже приведен пример данных JSON:

Работа с Python JSON

Python поддерживает стандартную библиотеку marshal и pickle module, а JSON API ведет себя аналогично этой библиотеке. Python изначально поддерживает функции JSON.

Давайте посмотрим на эти функции:

В этом разделе мы изучим следующие методы:

Сериализация JSON

Объекты Python преобразуются в следующие объекты JSON. Список приведен ниже:

Объекты Python JSON
1. Dict Object
2. list, tuple Array
3. Str String
4. int, float Number
5. True true
6. False false
7. None null

Функция dump()

Рассмотрим простой пример сериализации:

В приведенной выше программе мы открыли файл с именем data.json в режиме записи. Мы использовали режим записи потому что если файл не существует, он будет создан. Метод json.dump() преобразует словарь в строку JSON.

Функция dumps()

Функция dumps() используется для хранения сериализованных данных в файле Python. Она принимает только один аргумент, который представляет собой данные Python для сериализации. Файловый аргумент не используется, потому что мы не записываем данные на диск. Рассмотрим следующий пример:

JSON поддерживает примитивные типы данных, такие как строки и числа, а также вложенные списки, кортежи и объекты.

Десериализация JSON

JSON Python
1. Object Dict
2. Array list
3. String str
4. Number (int) int
5. true True
6. false False
7. null None

В приведенной выше таблице показано обратное преобразование сериализованной таблицы, но технически это не идеальное преобразование данных JSON. Это означает, что если мы кодируем объект и снова декодируем его через некоторое время; мы не можем вернуть тот же объект.

Давайте возьмем реальный пример: один человек переводит что-то на китайский язык, а другой переводит обратно на английский, и это может быть неточно переведено. Рассмотрим простой пример:

Функция load()

Функция load() используется для десериализации данных JSON в объект Python из файла. Рассмотрим следующий пример:

В приведенной выше программе мы закодировали объект Python в файле с помощью функции dump(). После этого мы читаем файл JSON с помощью функции load(), где в качестве аргумента мы передали read_file.

Модуль json также предоставляет функцию loads(), которая используется для преобразования данных JSON в объект Python. Она очень похожа на функцию load(). Рассмотрим следующий пример:

Сравнение json.load() и json.loads()

Функция json.load() используется для загрузки файла JSON, тогда как функция json.loads() используется для загрузки строки.

Сравнение json.dump() и json.dumps()

Функция json.dump() используется, когда мы хотим сериализовать объекты Python в файл JSON, а функция json.dumps() используется для преобразования данных JSON в виде строки для анализа и печати.

Python Pretty Print JSON

Иногда нам нужно проанализировать и отладить большой объем данных JSON. Это можно сделать, передав дополнительные аргументы indent и sort_keys в методы json.dumps() и json.dump().

Примечание: обе функции dump() и dumps() принимают аргументы indent и short_keys.

Рассмотрим следующий пример:

Кодирование и декодирование


Сразу после появления, JSON быстро стал де факто стандартом обмена информации. Вероятно вы здесь из-за того, что вы хотите переместить данные из одного места в другое. Возможно вы получаете данные через API, или храните их в документной базе данных. Так или иначе, вы заинтересовались JSON, и вам нужно пользоваться им через Python.

Содержание

К счастью, это достаточно тривиальная задача, и как и с большинством тривиальных задач, Python делает все до омерзения простым.

Итак, используем ли мы JSON для хранения и обмена данными? Именно так. Это не более, чем стандартизированный формат, который используется сообществом для передачи данных. Помните, что JSON не является единственным доступным форматом для такой работы, XML и YAML наверное, единственные альтернативные способы, которые стоит упомянуть.

Подробнее про JSON

Не удивительно, что JavaScript Object Notation был вдохновен подмножеством языка программирования JavaScript, связанным с синтаксисом объектного литерала. У них есть отличный сайт, в котором все прекрасно объясняется. Не переживайте: JSON уже давно стал агностиком языка и существует как отдельный стандарт, по этому мы можем убрать JavaScript из этой дискуссии.

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

В конечном счете, большая часть сообщества приняла JSON благодаря его простоте как для людей, так и для машин.
Смотрите, это JSON!

Структура JSON

Готовьтесь. Я собираюсь показать реальный пример JSON— такой же, какой вы встретите в реальной жизни. Это нормально, подразумевается что JSON является читаемым для любого, кто пользовался С-языками, а Python – это С-язык, так что мы говорим о вас!

Как видите, JSON поддерживает примитивные типы, такие как строки python и числа, а также вложенные списки и объекты.

Погодите, это выглядит как словарь Python, верно? На данный момент это достаточно универсальная нотация объектов, и не думаю что UON может так же легко отскакивать от зубов. Кстати, предлагайте альтернативы в комментариях!

НУ что же, вы пережили первый контакт с диким JSON. Теперь вам нужно научиться приручать его!

Python поддерживает JSON

Python содержит встроенный модуль под названием json для кодирования и декодирования данных JSON.

Просто импортируйте модуль в начале вашего файла:

Небольшой словарь

Как правило, процесс кодирования JSON называется сериализация. Этот термин обозначает трансформацию данных в серию байтов (следовательно, серийных) для хранения или передачи по сети. Также вы, возможно, уже слышали о термине «маршалинг», но это уже совсем другая область.

Естественно, десериализация — является противоположным процессом декодирования данных, которые хранятся или направлены в стандарт JSON.

Звучит как много технических терминов. Определенно. Но в реальности, все, о чем мы сейчас говорим — это чтение и написание. Представьте это следующим образом: кодирование это запись данных на диск, в то время как декодирование — это чтение данных в памяти.

Сериализация JSON

Что происходит после того, как компьютер обрабатывает большие объемы информации? Ему нужно принять дамп данных. Соответственно, модуль json предоставляет метод dump() для записи данных в файлы. Также есть метод dumps() для записей в строку Python.

Простые объекты Python переводятся в JSON согласно с весьма интуитивной конверсией.

PythonJSON
dictobject
list, tuplearray
strstring
int, long, floatnumber
Truetrue
Falsefalse
Nonenull

Пример сериализации JSON Python

Представьте, что вы работаете с объектом Python в памяти, который выглядит следующим образом:

Сохранить эту информацию на диск — критично, так что ваша задача — записать на файл.

Используя контекстный менеджер Python, вы можете создать файл под названием data_file.json и открыть его в режиме write (файлы JSON имеют расширение .json).

Обратите внимание на то, что dump() принимает два позиционных аргумента: (1) объект данных, который сериализуется и (2), файловый объект, в который будут вписаны байты.

Или, если вы склонны продолжать использовать эти сериалзированные данные JSON в вашей программе, вы можете работать как со строкой.

Обратите внимание, что файловый объект является пустым, так как вы на самом деле не выполняете запись на диск. Кроме того, dumps() аналогичен dump().

Ура! У вас получился малыш JSON и вы можете выпустить его в реальный мир, чтобы он вырос большим и сильным.

Несколько полезных аргументов

Помните, что JSON создан таким образом, чтобы быть читаемым для людей. Но читаемого синтаксиса недостаточно, если все слеплено вместе. Кроме этого, ваш стиль программирования скорее всего отличается от моего, и вам будет проще читать код, если он отформатирован по вашему вкусу.

Обратите внимание: Методы dump() и dumps() пользуются одними и теми же аргументами ключевых слов.

Первая опция, которую большинство людей хотят поменять, это пробел. Вы можете использовать аргумент indent для определения размера отступа вложенных структур. Ощутите разницу лично, используя данные, упомянутые выше и выполните следующие команды в консоли:

Есть и другие аргументы, такие как sort_keys, но я не имею понятия, что он делает. Вы можете найти полный список в документации, если вам интересно.

Десериализация JSON

Отлично, похоже вам удалось поймать экземпляр дикого JSON! Теперь нам нужно предать ему форму. В модуле json вы найдете load() и loads() для превращения кодированных данных JSON в объекты Python.

Как и сериализация, есть простая таблица конверсии для десериализации, так что вы можете иметь представление о том, как все выглядит.

JSONPython
objectdict
arraylist
stringstr
number (int)int
number (real)float
trueTrue
falseFalse
nullNone

Технически, эта конверсия не является идеальной инверсией таблицы сериализации. По сути, это значит что если вы кодируете объект сейчас, а затем декодируете его в будущем, вы можете не получить тот же объект назад. Я представляю это как своего рода телепортацию: мои молекулы распадаются в точке А и собираются в точке Б. Буду ли я тем же самым человеком?

В реальности, это как попросить одного друга перевести что-нибудь на японский, а потом попросить другого друга перевести это обратно на русский. В любом случае, самым простым примером будет кодировать кортеж и получить назад список после декодирования, вот так:

В C++17 (нет-нет, Питон скоро будет, вы правильно зашли!) появляется новый синтаксис для оператора if , позволяющий объявлять переменные прямо в заголовке блока. Это довольно удобно, поскольку конструкции вида


довольно общеупотребительны. Код выше лёгким движением руки программиста (и тяжёлым движением руки комитета по стандартизации) превращается в:


Стало чуть-чуть лучше, хотя всё ещё не выглядит идеально. В Python нет и такого, но если вы ненавидите if в Python-коде так же сильно, как я, и хотите научиться быстро писать простые парсеры, то добро пожаловать под кат. В этой статье мы попытаемся написать короткий и изящный парсер для JSON на Python 2 (без каких-либо дополнительных модулей, конечно же).

Парсинг (по-русски «синтаксический анализ») — это бессмертная задача разобрать и преобразовать в осмысленные единицы нечто, написанное на некотором фиксированном языке, будь то язык программирования, язык разметки, язык структурированных запросов или главный язык жизни, Вселенной и всего такого. Типичная последовательность этапов решения задачи выглядит примерно так:

    Описать язык. Конечно, сначала надо определиться, какую именно задачу мы решаем. Обычно описание языка — это очередная вариация формы Бэкуса-Наура. (Вот, например, описание грамматики Python, использующееся при построении его парсера.) При этом устанавливаются как правила «построения предложений» в языке, так и правила определения валидных слов.

Модельная задача

Написание парсера проиллюстрируем на простом, но не до конца тривиальном примере — парсинге JSON. Грамматика выглядит примерно так:


Здесь нет правил для string и number — они, вместе со всеми строками в кавычках, будут нашими токенами.

Полноценный токенайзер мы писать не станем (это скучно и не совсем тема статьи) — будем работать с целой строкой и бить её на токены по мере необходимости. Напишем две первые функции:


(Я обещал без if'ов, но это последние, чесслово!)

Для всего остального напишем одну функцию, генерящую простенькие функции-парсеры:


Итого, по какому принципу мы строим наши функции:

  1. Они принимают строку, которую нужно парсить.
  2. Они возвращают пару (результат, оставшаяся_строка) при успехе (то есть когда требуемая конструкция нашлась в начале строки) и None при провале.
  3. Они отправляют в небытие все пробельные символы между токенами. (Не делайте так, если пишете парсер Питона!)

Парсим правило с ветвлением

Как должна выглядеть функция parse_value , соответствующая грамматике выше? Обычно как-то так:


Ну уж нет, эти if достали меня!

Давайте поменяем три функции выше самым неожиданным образом: заменим return на yield ! Теперь они возвращают генераторы — пустые, если парсинг не удался, и ровно с одним элементом, если удался. Да-да, мы разворачиваем на 90 градусов наш принцип номер 2: все наши функции мы будем теперь писать в таком стиле:


Во что же превратится наша parse_value ? На первый взгляд во что-то такое:


Но на второй взгляд мы увидим, что каждая опция может занимать всего одну строчку!


При этом эффективность остаётся на прежнем уровне — каждая функция начнёт выполняться (а стало быть, делать работу, проверяя регулярные выражения) только тогда, когда предыдущая не даст результата. return гарантирует, что лишняя работа не будет выполнена, если где-то в середине списка парсинг удался.

Парсим последовательности конструкций

Перейдём к следующему номеру нашей программы — функции parse_array . Выглядеть она должна как-то так:


Ни одного if , как и обещано, но что-то всё равно не так… Давайте напишем небольшую вспомогательную функцию, которая поможет нам соединять функции-парсеры в последовательности подобно тому, как chain помогла соединять их в режиме «или». Эта функция должна будет аккуратно брать все результаты и вернуть все первые элементы результатов (результаты анализа) и последний второй элемент (оставшуюся непроанализированной часть строки). Мой вариант выглядит так:


С этим мощным (пусть и страшноватым) инструментом наша функция перепишется в виде:


Ну а дописать функцию parse_comma_separated_values — раз плюнуть:


Приведёт ли такое решение к бесконечной рекурсии? Нет! Однажды функция parse_comma не найдёт очередной запятой, и до последующей parse_comma_separated_values выполнение уже не дойдёт.

Идём дальше! Объект:

Ну, что там дальше?

Собственно, всё! Остаётся добавить простую интерфейсную функцию:

130 строк. Попробуем запустить:

Заключение

Конечно, я рассмотрел далеко не все ситуации, которые могут возникнуть при написании парсеров. Иногда программисту может потребоваться ручное управление выполнением, а не запуск последовательности chain ов и sequence ов. К счастью, это не так неудобно в рассмотренном подходе, как может показаться. Так, если нужно попытаться распарсить необязательную конструкцию и сделать действие в зависимости от её наличия, можно написать:


Здесь мы пользуемся малопопулярной фишкой Питона — блоком else у циклов, который выполняется, если цикл дошёл до конца без break . Это выглядит не так привлекательно, как наш код в статье, но точно не хуже, чем те if , от которых мы столь изящно избавились.

Несмотря на неполноту и неакадемичность изложения, я надеюсь, что эта статья будет полезна начинающим программистам, а может, даже удивит новизной подхода программистов продвинутых. При этом я прекрасно отдаю себе отчёт, что это просто новая форма для старого доброго рекурсивного спуска; но если программирование — это искусство, разве не важна в нём форма если не наравне, то хотя бы в степени, близкой к содержанию.

Как обычно, не откладывая пишите в личку обо всех обнаруженных неточностях, орфографических, грамматических и фактических ошибках — иначе я сгорю от стыда!

Эта статья научит вас парсить данные из JSON. Также вы узнаете, как читать и записывать в файл данные JSON.

За последние 5-10 лет формат JSON был одним из самых популярных способов сериализации данных (если не самым популярным). Особенно в веб-разработке. С этим форматом вы столкнетесь при работе с REST API, конфигурациями приложений или базами данных.

Несомненно, знать принципы работы JSON — очень важно. В какой-то момент вы обязательно с ним встретитесь. Возможно, вы захотите узнать, как читать и записывать JSON в файл. Все эти действия — очень простые. В этом вы убедитесь, разобрав следующие примеры.

Запись JSON в файл

Самый простой способ записать JSON в файл — использовать словарь. Они могут хранить вложенные словари, массивы, булевы значения и другие типы данных вроде целых чисел и строк. Более детальный список поддерживаемых типов данных можно найти здесь.

Во встроенной библиотеке json есть «волшебный» метод, который позволяет конвертировать словари в сериализованную JSON-строку.

После импорта библиотеки json мы объявляем несколько словарей и наполняем их данными. Самая важная часть — в конце программы. Здесь мы используем оператор with , чтобы открыть файл. После этого мы используем метод json.dump , чтобы записать наши словари в файл.

Вторым аргументом может быть любой файлоподобный объект — даже если это не совсем файл. Например, сокет. Его можно открыть, закрыть и записать так же, как и файл. С подобным вариантом использования JSON вы точно столкнетесь — это важно запомнить.

Стоит упомянуть и о вариации метода json.dump — json.dumps . Этот метод позволяет вернуть JSON-строку, а не записывать ее в файл. Это может быть полезно, если вы хотите изменить JSON-строку. (например, зашифровать)

Чтение JSON из файла

Чтение JSON из файла такое же простое, как и запись. С помощью библиотеки json мы можем спарсить JSON-строку прямо из файла. В этом примере мы парсим данные и выводим их в консоль:

json.load — очень важный метод, запомните его. С его помощью происходит чтение файла, парс JSON-данных. После этого все данные записываются в словарь и возвращаются вам.

Как и у json.dump , у json.load есть дополнительный метод. Он позволяет работать со строками напрямую, ведь чаще всего у вас не будет файлоподобного объекта, содержащего JSON. Как вы уже догадались, называется он json.loads . Допустим, вы вызываете конечную точку REST с помощью GET, который возвращает строку. Ее мы и можем напрямую передать в json.loads .

Параметры

При сериализации данных в JSON могут возникнуть проблемы. Например, его будет не очень удобно читать, ведь удаляются все пробелы. В большинстве случаев этот вариант вполне хорош, но порой нужно внести небольшие изменения. К примеру, добавить пробелы, чтобы JSON было удобнее читать. У json.load и json.dump есть несколько параметров, которые дают необходимую гибкость. О некоторых из них мы и поговорим.

Pretty-Printing

Сделать JSON более удобочитаемым (pretty-printing) — очень просто. Нужно лишь передать целое число в параметр indent :

Это довольно полезно. Особенно если вам часто приходится читать JSON во время работы. Также вы можете использовать использовать команду json.tool прямо в командной строке. Если вы хотите удобочитаемый JSON, наберите в командной строке следующий код:

Сортировка

В JSON объект определяется следующим образом:

То есть, порядок не гарантируется. Но навести его реально. Сделать это можно с помощью передачи True в параметр sort_keys в методах json.dump или json.dumps .

ASCII-текст

По умолчанию json.dump проверяет, имеет ли ваш текст в словаре кодировку ASCII. Если присутствуют символы, отличные от ASCII, они автоматически экранируются. Это показано в следующем примере:

Но это не всегда приемлемо. Во многих случаях вы бы хотели сохранить символы Unicode нетронутыми. Для этого нужно передать в параметр ensure_ascii значение False .

Написание парсера JSON - один из простейших способов ознакомления с техниками парсинга. Форматирование предельно просто, оно построено на рекурсии. Поэтому оно немного сложнее чем, скажем, Braifuck. Также, вероятно, вы уже сталкивались с JSON.

Если вы просто хотите увидеть код - его можно найти на Github.

Что такое парсинг, и чем он не является

Обычно парсинг разбивается на два этапа: лексический анализ и синтаксический анализ. Лексический анализ разбивает исходные данные на простейшие элементы языка - токены (или лексемы). Синтаксический анализ собирает из лексем осмысленные (для этого языка) выражения.

Парсинг не проверяет семантическую правильность исходных данных. То есть мы не смотрим, объявлена ли переменная перед использованием, вызвана ли функция с правильными аргументами или объявлена ли переменная второй раз.

Интерфейс библиотеки

В данном случае мы реализуем лишь один метод from_string , который из строки вернёт словарь (dictionary). Пример теста:

Лексический анализ

На этапе лексического анализа мы разобьём исходную строку на лексемы. Во время анализа мы можем опустить комментарии, пробелы и отступы, чтобы упростить последующую работу синтаксического анализатора.

Для простейшего синтаксического анализа нам потребуется пройтись по всем символам в входных данных и разбить их на фундаментальные (не рекурсивные!) языковые конструкции, такие как: числа, строки и булевы значения. Строки должны быть частью процесса, так как мы не можем отбросить все пробелы, не убедившись, что это не часть строки.

В полноценном лексере мы бы следили за отступами и комментариями, которые были пропущены, номером строки и файлом, в котором мы находимся. Все эти данные будут полезны в процессе дебаггинга.

Последняя версия движка V8 JavaScript теперь может воспроизводить точный код вызванной функции. Это огромный прорыв для лексера языка с анонимными функциями и классами.

Реализация лексера JSON

Набросок лексера будет проходить циклом по входящей строке и выискивать паттерны строк, чисел, булевых выражений, null и синтаксиса JSON, такого как левые и правые скобки. Возвращать он будет каждый из элементов как список.

Задача здесь - выделить из ввода числа, строки, булевы значения и null. Если ни один из них не подходит, то проверяем символ на пробел (отступ) и синтаксическую часть JSON. Всё, кроме whitespaces, добавляется в список токенов, который возвращается функцией.

Теперь расширим логику, чтобы поддерживать все типы данных и добавим заготовки функций:

Определение строк

Давайте напишем функцию lex_string . В ней мы будем проверять, если дальше после указателя (нулевой символ строки и далее) идёт строка. Для этого мы в цикле будем искать открывающие и закрывающие кавычки:

Определение чисел

В процессе поиска чисел в функции lex_number мы будем итерировать по входящей строке до тех пор, пока не найдём знак, который не может являться частью числа. После обнаружения такого знака мы вернём float или int. В случае, если это не число, вернём None и исходную строку без изменений.

Определение булевых значений и null

Это самые простые типы данных для обнаружения:

Теперь лексер закончен. Полный исходный код можно найти в файле pj/lexer.py.

Синтаксический анализ

Задача синтаксического анализатора состоит в итерации по одномерному списку лексем и отыскивать в нём простейшие синтаксические конструкции языка Если в какой-то момент анализа, парсеру не удастся соотнести текущий набор токенов с синтаксическими конструкциями, парсер закончит свою работу и вернёт информацию об ошибке.

Реализация парсера JSON

Заготовка JSON парсера будет итерировать по токенам, которые вернул лексический анализатор, и соотносить их с объектами, списками или парами ключ-значение:

Логика будет выглядеть следующим образом:

Ключевым структурным отличием между лексером и парсером будет то, что лексер всегда возвращает одномерный список токенов, а парсер будет обрабатывать рекурсивные структуры (например, деревья).

Так как JSON - формат сериализации данных, а не язык, то парсер должен возвращать объекты языка Python, а не синтаксическое дерево, с которым можно было бы работать далее (или сгенерировать код в случае компилятора).

Главным преимуществом разделения лексического анализа от парсера является то, что оба фрагмента программы проще в реализации и выполняют только одну функцию.

Парсинг массивов

Массив всегда начинается с левой квадратной скобки. Поэтому парсинг массивов сводится к поиску элементов массива, запятых между ними или же закрывающих скобок:

Парсинг объектов

Парсинг объектов заключается в поиске пар ключ-значение, где ключ от значения отдёлен двоеточием, а пары разделены запятыми. Поиск продолжается до окончания объекта:

Парсер готов. Полный код можно увидеть в pj/parser.py.

Собираем библиотеку

Осталось объединить лексический и синтаксический анализаторы в общую библиотеку, удовлетворяющую интерфейсу, описанному в начале статьи:

Библиотека готова. Дополнительные вещи, вроде тестов, можно найти в репозитории Github.

Дополнение: парсинг в один этап

Некоторые парсеры не разбивают процесс на лексический и синтаксический анализ. Для определенных языков это может упростить парсинг. Для других, таких как Common Lisp, это позволяет динамически расширять лексер и парсер через макросы.

Данная библиотека написана для того, чтобы дать общее понимание процесса парсинга языков для широких масс. Многие из описанных в статье принципов используются для более сложных вещей, например, написания программируемых калькуляторов или работы с визуальными языками.

Читайте также: