Работа с JSON и XML в Python

На этом языке общается половина интернета

Работа с JSON и XML в Python

Сегодня разбираем простые форматы для работы с текстовой информацией — JSON и XML. Посмотрим, как вообще работать с таким текстом, где это нужно, какие ещё есть похожие форматы и как работать с JSON в языке программирования Python.

Что такое JSON, XML и YAML

JSON, XML и YAML — это три похожих формата для работы с текстом. С их помощью в программах можно хранить и обмениваться информацией между собой.

Чтобы не запутаться, кратко разберём каждый — как выглядят, как в общем работают и когда используются. 

JSON

Текстовый формат для обмена данными. Расшифровывается как JavaScript Object Notation.

Чаще всего используется:

  • В веб-разработке для передачи данных между сервером и клиентом — например, для обмена списком товаров и их характеристик в интернет-магазине.
  • При работе с API, когда программа запрашивает или отправляет данные в API.
  • Для настройки приложений. Примеры таких файлов — settings.json и extensions.json в редакторе кода Visual Studio Code. Чтобы добавить какое-то расширение или настройку, их можно вписать в нужный файл.

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

XML

Тоже текстовый формат. Используется реже JSON из-за своей сложности.

У XML нет предопределённых типов, как в JSON, вместо этого всё нужно задавать самостоятельно. Это убирает ограничения на количество типов, но замедляет работу программ и читаемость для человека.

Где может применяться:

  • В сложных программах, где нужно задавать свои типы данных и их проверку.
  • В компаниях с легаси-кодом, которым сложно, дорого или просто невыгодно переезжать на другие технологии.

YAML

Формат, который чаще применяется для хранения настроек в виде текста.

Примеры использования:

  • В файлах-манифестах Kubernetes.
  • Для настройки серверов и развёртывания приложений в файлах Docker.
  • При создании автотестов, например для автоматического деплоя в GitHub Actions.

Что удобнее и как это выглядит

Для примера возьмём профиль человека и посмотрим, как он будет выглядеть в трёх разных форматах. Эта информация может отправиться на сервер с клиента — или наоборот.

JSON поддерживает всего несколько типов данных. Но их достаточно в большинстве случаев, а программа работает быстрее. Получается хорошо читаемый формат, с которым удобно работать и человеку, и машине:

{
  "name": "Alice",
  "age": 30,
  "is_active": true,
  "skills": ["Python", "SQL", "Docker"],
  "address": {
    "city": "Moscow",
    "country": "Russia"
  }
}

XML разрешает работать с любыми типами данных, если их предварительно настроить. Это может быть полезно в сложных структурах, но скорость работы с таким файлом снижается, и человеку читать XML тоже сложнее:

<user>
  <name>Alice</name>
  <age>30</age>
  <is_active>true</is_active>
  <skills>
    <skill>Python</skill>
    <skill>SQL</skill>
    <skill>Docker</skill>
  </skills>
  <address>
    <city>Moscow</city>
    <country>Russia</country>
  </address>
</user>

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

Выглядит YAML так:

name: Alice
age: 30
is_active: true
skills:
  - Python
  - SQL
  - Docker
address:
  city: Moscow
  country: Russia

Библиотеки Python для работы с JSON и XML

Python — универсальный и самый популярный язык сегодня, который применяется в разных сферах. Во многих из них нужен обмен текстовыми данными, и в Python JSON и XML поддерживаются по умолчанию.

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

Чтобы дальше было проще, вот основные команды для работы с обоими форматами в стандартных модулях. Основные операции и синтаксис в остальных библиотеках будут похожими или вообще такими же.

Команды для работы с JSON:

  • Чтение JSON-данных в Python-программе, если сами данные в виде строки: json.loads(json_str). В скобках передаём JSON-строку, которую надо прочитать.
  • Чтение JSON-данных в Python-программе, если данные в виде файла:  json.load(file). В скобках передаём JSON-файл, который надо прочитать.
  • Запись Python-данных в JSON-строку для отправки или чтения программой: json.dumps(data). В скобках передаём Python-объекты, которые переводим в JSON-формат.
  • Запись Python-данных в JSON-файл: json.dump(data, file). В скобках передаём Python-объекты для записи и JSON-файл, в который они будут записаны.

Команды для работы с XML:

  • Чтение XML-данных в Python-программе, если сами данные в виде строки: ET.fromstring(xml_str). В скобках передаём XML-строку, которую будем читать.
  • Чтение XML-данных в Python-программе, если данные в виде файла:  ET.parse("file.xml"). В скобках передаём JSON-файл, который надо прочитать.
  • Запись Python-данных в XML-строку: ET.tostring(xml_tree). В скобках — XML-строка, куда будут записаны созданные в Python данные.
  • Запись Python-данных в XML-файл: tree.write("file.xml"). В скобках передаём название файла, куда будет записана предварительно созданная XML-структура.

Ещё три термина, которые нам понадобятся.

Сериализация — запись данных нашей программы в JSON-строку или файл.

Десериализация — обратный процесс, когда мы считываем JSON-данные.

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

Как устанавливаются библиотеки

Для работы с XML и JSON Python по умолчанию умеет работать со стандартными библиотеками xml.etree.ElementTree и json. Чтобы подключить эти модули, их достаточно просто добавить в начало скрипта командой import:

import json

import xml.etree.ElementTree as ET

Остальные сначала нужно установить командой pip install в терминале среды разработки IDE или терминале командной строки — в зависимости от того, где вы работаете.

В терминале командной строки это выглядит так:

Работа с JSON и XML в Python

А в терминале на вкладке IDE — так:

Работа с JSON и XML в Python

Библиотеки для работы с JSON

К описанию библиотек мы добавили примеры в коде, чтобы было понятно, как происходит:

  • извлечение JSON-данных;
  • извлечение XML-данных;
  • генерация JSON;
  • генерация XML.

json

Встроенная библиотека для работы с JSON-форматом. Подходит для большинства несложных задач. Но она не самая быстрая, потому что написана тоже на Python, а он может работать медленнее по сравнению с некоторыми другими языками.

Подходит для чтения API-запросов или сохранения настроек программы в файл.

Пример с генерацией JSON:

# импортируем библиотеку
import json

# создаём словарь с данными
data = {
   "name": "Анна",
   "age": 28,
   "courses": ["Python to json", "Data Science"],
   "objects": ["module", "convert"],

}

# преобразуем в JSON-строку с отступами
# и возможностью чтения кириллицы
json_str = json.dumps(data, indent=4, ensure_ascii=False)
# выводим на экран
print(json_str)

Результат:

{
"name": "Анна",
"age": 28,
"courses": [
"Python to json",
"Data Science"
],
"objects": [
"module",
"convert"
]
}

Пример с чтением JSON:

# импортируем библиотеку
import json

# создаём JSON-строку для примера
json_data = '''
{
  "name": "Анна",
  "age": 28,
  "courses": ["JSON to Python", "Data Science"]
}
'''

# преобразуем JSON-строку в словарь
data = json.loads(json_data)
# выводим на экран
print(data["name"])

Запускаем код:

Анна

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

SimpleJSON

Поддерживает дополнительные возможности, например работу с точным числовым форматом decimal. Этот вариант будет полезен в работе с финансами и другими сферами, где важна точность.

Сначала библиотеку нужно установить командой pip install simplejson.

Пример c генерацией JSON:

# импортируем библиотеку
import simplejson as json
from decimal import Decimal

# cоздаём данные с decimal
data = {"price": Decimal("19.99")}

# сериализуем с поддержкой decimal
json_str = json.dumps(data, use_decimal=True)
# выводим на экран
print(json_str)

Что получаем в консоли:

{"price": 19.99}

Пример с чтением JSON, в котором мы проверим, удалось ли получить нужный тип данных:

# импортируем библиотеку
import simplejson as json

# создаём decimal-строку в JSON
json_data = '{"price": 19.99}'

# загрузка и преобразование чисел в decimal
data = json.loads(json_data, use_decimal=True)
# выводим на экран
print(type(data["price"]))

Запускаем код и проверяем результат:

<class 'decimal.Decimal'>

ujson

Быстрая библиотека для работы с JSON, оптимизированная для обработки больших файлов. Например, логов или таблиц баз данных.

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

Пример c генерацией JSON:

# импортируем библиотеку
import ujson

# создаём словарь
data = {"users": [{"id": 1}, {"id": 2}]}

# быстрая сериализация
json_bytes = ujson.dumps(data)
# выводим на экран
print(json_bytes)

Проверяем, что получилось:

{"users":[{"id":1},{"id":2}]}

Пример с чтением:

# импортируем библиотеку
import ujson

# создаём JSON-данные
json_data = '{"users": [{"id": 1}, {"id": 2}]}'

# быстрое чтение
data = ujson.loads(json_data)
# выводим на экран
print(data["users"][0]["id"])

Запускаем код:

1

ijson

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

Сначала создадим JSON-файл для наглядного примера:

# импортируем библиотеку
import json

# создаём большой JSON-файл для демонстрации
big_data = {"users": [{"id": i, "name": f"User_{i}"} for i in range(1000)]}

# записываем данные в файл
with open('big_data.json', 'w') as f:
   json.dump(big_data, f)

Теперь откроем его и выведем на экран только первых трёх пользователей:

# импортируем библиотеку
import ijson

# открываем файл для чтения в бинарном режиме
with open('big_data.json', 'rb') as f:
   # итеративно обрабатываем элементы массива users
   for i, user in enumerate(ijson.items(f, 'users.item')):
       # выводим каждого пользователя по отдельности
       print(f"Обработка пользователя: {user['name']}")
       # выводим только первых трёх пользователей
       if i >= 2:
           break

Посмотрим, что получилось:

Обработка пользователя: User_0
Обработка пользователя: User_1
Обработка пользователя: User_2

jsonschema

Даёт возможность задавать правила для валидации данных. Пригодится, когда структура информации должна соответствовать шаблону.

Создадим тестовые JSON-данные:

# импортируем библиотеку
import json

# данные, которые должны соответствовать схеме
valid_data = {
   "name": "Иван",
   "age": 30,
   "email": "ivan@example.com"
}

# сохраняем данные в файл
with open('user.json', 'w') as f:
   json.dump(valid_data, f)

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

# импортируем библиотеки
from jsonschema import validate, ValidationError
import json

# задаём схему валидации с проверками типов
schema = {
   "type": "object",
   "properties": {
       "name": {"type": "string"},
       "age": {"type": "number"},
       "email": {"type": "string", "format": "email"}
   },
   "required": ["name", "email"]
}

# открываем файл
with open('user.json') as f:
   # пытаемся проверить содержимое на
   # соответствие заданной выше схеме
   try:
       # если всё правильно, говорим, что данные прошли проверку
       validate(instance:=json.load(f), schema)
       print(f"✅ Валидно! Имя: {instance['name']}")
   # если проверка не удалась, выносим это в исключение...
   except Exception as e:
       # ...и выдаём сообщение об ошибке
       print(f"❌ Ошибка: {type(e).__name__}: {e}")

Запускаем проверку:

✅ Валидно! Имя: Иван

Библиотеки для работы с XML

Обе технологии имеют похожие операции для работы: чтение, запись и валидация. Но при этом XML более сложный, и при работе с библиотеками это тоже проявляется.

Для работы с XML нужно разобрать понятие DOM-дерева и его узлов. DOM расшифровывается как Document Object Model. Это модель представления XML-файла, где каждый узел — один из элементов:

Работа с JSON и XML в Python
Источник: learn.microsoft.com

xml.etree.ElementTree

Стандартная встроенная в Python библиотека, подходит для всех простых базовых операций.

Пример создания XML-файла:

# импортируем библиотеки
import xml.etree.ElementTree as ET

# создаём корневой элемент
root = ET.Element("catalog")

# добавляем дочерние элементы
book = ET.SubElement(root, "book", attrib={"id": "1"})
title = ET.SubElement(book, "title")
title.text = "Питон для тех, что читает КОД"

# создаём дерево и записываем в файл
tree = ET.ElementTree(root)
tree.write("catalog.xml", encoding="utf-8", xml_declaration=True)

Теперь прочтём созданный файл и выведем на экран элемент-узел title.

# импортируем библиотеки
import xml.etree.ElementTree as ET

# загружаем XML из файла
tree = ET.parse("catalog.xml")
root = tree.getroot()

# находим все элементы title
for book in root.findall("book"):
   # выводим результат на экран
   print(book.find("title").text)

Запускаем код и выводим результат:

Питон для тех, что читает КОД

lxml

Быстрая и мощная библиотека с поддержкой технологий запросов быстрого поиска и преобразования XML в другие форматы. Полезна для сложной обработки XML и просто для хорошей производительности.

Пример генерации XML:

# импортируем библиотеку
from lxml import etree

# создаём корневой элемент с пространством имён
root = etree.Element("{http://example.com}root")

# добавляем дочерние элементы
child = etree.SubElement(root, "child")
child.text = "Пятый элемент"

# записываем в файл с форматированием
tree = etree.ElementTree(root)
tree.write("lxml_output.xml", pretty_print=True, encoding="utf-8")

Теперь найдём созданный элемент child.text с помощью технологии быстрых запросов XPath. Для этого нужно задать путь до нужного узла с использованием метода .xpath:

# импортируем библиотеку
from lxml import etree

# загружаем созданный XML
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse("lxml_output.xml", parser)

# используем XPath для поиска
results = tree.xpath("//child/text()")
# выводим на экран
print(results)

Смотрим, что выводится на экран:

['Пятый элемент']

xmltodict

Простая библиотека для работы с XML-данными как со словарями Python. Подойдёт, если XML достаточно простой или если в программе нужно быстро преобразовывать формат XML в объект Python.

Для примера генерации файла создадим словарь и переведём его в XML:

# импортируем библиотеку
import xmltodict

# создаём словарь с данными
data = {
   "catalog": {
       "book": {
           "@id": "1",
           "title": "Python",
           "price": "999"
       }
   }
}

# преобразуем словарь в XML
xml_str = xmltodict.unparse(data, pretty=True)
# создаём файл для записи
with open("xmltodict_output.xml", "w") as f:
   f.write(xml_str)

При запуске кода в корневом каталоге проекта появится файл xmltodict_output.xml.

Теперь прочтём этот XML и выведем на экран одно из значений:

# импортируем библиотеку
import xmltodict

# читаем XML и преобразуем в словарь
with open("xmltodict_output.xml") as f:
   data = xmltodict.parse(f.read())

# выводим на экран значение по ключу title
print(data["catalog"]["book"]["title"])

В консоли получаем результат:

Python

BeautifulSoup

Популярный инструмент, когда нужно использовать универсальный и гибкий парсинг форматов XML и HTML.

Создать файл можно так:

# импортируем библиотеку
from bs4 import BeautifulSoup

# создаём XML документ
soup = BeautifulSoup(features="xml")
catalog = soup.new_tag("catalog")
book = soup.new_tag("book", id="1")
title = soup.new_tag("title")
title.string = "Парсинг, XML и всё остальное"
book.append(title)
catalog.append(book)

# записываем в файл
with open("bs4_output.xml", "w") as f:
   f.write(str(catalog))

Теперь загрузим этот файл и найдём номер элемента и текст по тегу title:

# импортируем библиотеку
from bs4 import BeautifulSoup

# загружаем и парсим XML
with open("bs4_output.xml") as f:
    soup = BeautifulSoup(f, "xml")

# находим элементы
for book in soup.find_all("book"):
    print(book["id"], book.title.text)  # 1 Python

Результат в консоли:

1 Парсинг, XML и всё остальное

Примеры валидации данных в JSON и XML

Для дополнительной надёжности в программы добавляют функцию валидации данных — когда информацию перед использованием проверяют на соответствие правилам. 

Когда мы говорили про JSON-библиотеки, то разобрали пример с jsonschema с проверкой типа данных: возраст должен быть числом, имя — строкой, а адрес электронной почты должен соответствовать шаблону проверки.

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

Примеры валидации JSON в Cerberus

Cerberus — библиотека для установки правил проверки JSON.

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

Для примера установим такую проверку: тип данных для значения по ключу name должен быть строкой и иметь минимальную длину в два символа:

# импортируем библиотеку
from cerberus import Validator

# устанавливаем правила валидации
using = {'name': {'type': 'string', 'minlength': 2}}

# создаём данные для проверки
dictionary = {'name': 'Alex'}

# загружаем схему в переменную
v = Validator(using)
# проверяем данные на соответствие правилам
if v.validate(dictionary):
  print("✓ Данные валидны")
else:
  print("✗ Ошибки:", v.errors)

Проверяем результат:

✓ Данные валидны

Примеры валидации XML в xmlschema

xmlschema — библиотека для валидации XML по XSD-схемам. 

XSD — структура в коде, которая описывает допустимые правила и границы данных программы. То есть даёт возможность разработчикам настраивать такие же проверки, как Cerberus в JSON, но для XML.

Создание такой схемы выглядит так:

# импортируем библиотеку
from xmlschema import XMLSchema

# создаём схему XSD
xsd_schema = """<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xs:element name="note">
   <xs:complexType>
     <xs:sequence>
       <xs:element name="to" type="xs:string"/>
       <xs:element name="from" type="xs:string"/>
     </xs:sequence>
   </xs:complexType>
 </xs:element>
</xs:schema>"""

# сохраняем схему в файл
with open("note_schema.xsd", "w") as f:
   f.write(xsd_schema)

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

# создаём тестовый XML файл для проверки
with open("note.xml", "w") as f:
   f.write("""<?xml version="1.0"?>
<note>
 <to>Alice</to>
 <from>Bob</from>
</note>""")

# загружаем схему
schema = XMLSchema("note_schema.xsd")

# проверяем XML файл
if schema.is_valid("note.xml"):
   print("XML соответствует схеме")
else:
   print("Ошибки валидации:", list(schema.iter_errors("note.xml")))

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

XML соответствует схеме

Вам слово

Приходите к нам в соцсети поделиться своим мнением об этих форматах и почитать, что пишут другие. А ещё там выходит дополнительный контент, которого нет на сайте — шпаргалки, опросы и разная дурка. В общем, вот тележка, вот ВК — велком!

Обложка:

Алексей Сухов

Корректор:

Александр Зубов

Вёрстка:

Егор Степанов

Соцсети:

Юлия Зубарева

Вам может быть интересно
easy