Python JSON
Узнайте, как использовать встроенный модуль json в Python для кодирования, декодирования, чтения и записи данных JSON с примерами и обработкой ошибок.
Встроенный модуль json в Python позволяет преобразовывать объекты Python в JSON-текст и обратно, как правило, с помощью всего двух функций. В этой главе рассматривается всё необходимое: сопоставление типов данных, dumps/loads для строк, dump/load для файлов, форматированный вывод, обработка ошибок и пользовательская сериализация.
Что такое JSON?
JSON (JavaScript Object Notation) — это лёгкий текстовый формат обмена данными. Он удобен для чтения, не зависит от языка программирования и является стандартным форматом для большинства веб-API.
Документ JSON строится из двух структур:
- Объекты — неупорядоченные наборы пар ключ/значение, заключённые в
{}. Ключи всегда являются строками в двойных кавычках. - Массивы — упорядоченные последовательности значений, заключённые в
[].
Пример JSON-документа:
{
"name": "John Doe",
"age": 30,
"city": "New York",
"hobbies": ["reading", "traveling", "photography"],
"active": true,
"score": null
}Сопоставление типов Python и JSON
При кодировании или декодировании JSON модуль json в Python преобразует типы согласно следующей таблице:
| Тип Python | Тип JSON | Тип Python (после декодирования) |
|---|---|---|
dict | object {} | dict |
list, tuple | array [] | list |
str | string "" | str |
int, float | number | int или float |
True / False | true / false | bool |
None | null | None |
Обратите внимание: кортежи становятся JSON-массивами и возвращаются как списки Python после декодирования.
Кодирование объектов Python в JSON-строки
json.dumps() (dump-string) сериализует объект Python в строку в формате JSON.
Базовое кодирование
Вывод:
{"name": "John Doe", "age": 30, "city": "New York", "hobbies": ["reading", "traveling", "photography"]}Форматированный вывод с indent и sort_keys
По умолчанию json.dumps() производит компактную однострочную строку. Передайте indent, чтобы получить удобочитаемый вывод, и sort_keys=True, чтобы отсортировать ключи словаря в алфавитном порядке:
import json
person = {
"name": "John Doe",
"age": 30,
"city": "New York",
"hobbies": ["reading", "traveling", "photography"]
}
print(json.dumps(person, indent=2, sort_keys=True))Вывод:
{
"age": 30,
"city": "New York",
"hobbies": [
"reading",
"traveling",
"photography"
],
"name": "John Doe"
}Используйте indent=2 или indent=4 при записи конфигурационных файлов или отладке ответов API — это делает вложенные структуры удобными для чтения.
Декодирование JSON-строк в объекты Python
json.loads() (load-string) разбирает JSON-строку и возвращает эквивалентный объект Python.
Базовое декодирование
Вывод:
{'name': 'John Doe', 'age': 30, 'city': 'New York', 'hobbies': ['reading', 'traveling', 'photography']}
<class 'dict'>
John DoeДекодированное значение является обычным словарём Python, поэтому вы можете обращаться к его ключам через [] или .get(), итерировать с помощью for и т. д.
Обработка некорректного JSON
Если входные данные не являются корректным JSON, json.loads() вызывает исключение json.JSONDecodeError. Всегда перехватывайте его при работе с данными из внешних источников:
import json
raw = '{"name": "Alice", "age":}' # invalid — missing value
try:
data = json.loads(raw)
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e}")Вывод:
Invalid JSON: Expecting value: line 1 column 24 (char 23)Смотрите главу Python try/except для полного руководства по обработке исключений.
Работа с вложенным JSON
Объекты JSON могут содержать другие объекты и массивы любой глубины вложенности. Обращайтесь к вложенным значениям с помощью цепочки операторов []:
Вывод:
John
travelingДля глубоко вложенных структур рекомендуется использовать .get() со значением по умолчанию, чтобы избежать KeyError при отсутствующих ключах:
city = person.get("address", {}).get("city", "unknown")В главе о вложенных словарях рассматриваются паттерны работы с глубоко вложенными словарями Python.
Чтение и запись JSON-файлов
json.dump() записывает данные в файловый объект, а json.load() читает из него. Это файловые аналоги функций dumps/loads.
Запись JSON в файл
import json
data = {"name": "Alice", "scores": [95, 87, 92]}
with open("data.json", "w") as f:
json.dump(data, f, indent=2)Это создаёт data.json с форматированным содержимым. Использование with open(...) гарантирует автоматическое закрытие файла — подробности смотрите в главе Работа с файлами в Python.
Чтение JSON из файла
import json
with open("data.json") as f:
data = json.load(f)
print(data)
print(data["scores"])Вывод (при условии, что файл был записан выше):
{'name': 'Alice', 'scores': [95, 87, 92]}
[95, 87, 92]Полный пример с записью и чтением
import json
# Write
config = {"host": "localhost", "port": 5432, "debug": False}
with open("config.json", "w") as f:
json.dump(config, f, indent=2)
# Read back
with open("config.json") as f:
loaded = json.load(f)
print(loaded["port"]) # 5432
print(type(loaded["port"])) # <class 'int'>Получение JSON из веб-API
В большинстве проектов вам придётся декодировать JSON, поступающий из HTTP-ответов. Популярная библиотека requests упрощает эту задачу:
import requests
response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
response.raise_for_status() # raises an error for 4xx/5xx responses
data = response.json() # equivalent to json.loads(response.text)
print(data["title"])
print(data["completed"])response.json() внутренне вызывает json.loads(). Всегда вызывайте raise_for_status() перед разбором, чтобы неудачный запрос не вернул тело ошибки в тихом режиме.
Пользовательская сериализация с default
Модуль json по умолчанию не может кодировать произвольные объекты Python (например, datetime.date). Передайте вызываемый объект default или пользовательский подкласс JSONEncoder для их обработки:
import json
import datetime
class DateEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.date):
return obj.isoformat()
return super().default(obj)
event = {"name": "Conference", "date": datetime.date(2024, 1, 15)}
print(json.dumps(event, cls=DateEncoder))Вывод:
{"name": "Conference", "date": "2024-01-15"}Объект date преобразуется в строку ISO 8601 перед сериализацией.
Пользовательская десериализация с object_hook
object_hook вызывается для каждого JSON-объекта (dict) в процессе декодирования. Вы можете использовать его для преобразования данных на лету — например, для конвертации строковых значений в правильные типы Python:
import json
def as_record(d):
"""Convert age field from string to int if present."""
if "age" in d:
d["age"] = int(d["age"])
return d
raw = '{"name": "Bob", "age": "25"}'
person = json.loads(raw, object_hook=as_record)
print(person)
print(type(person["age"])) # <class 'int'>Вывод:
{'name': 'Bob', 'age': 25}
<class 'int'>Краткий справочник
| Функция | Направление | Источник/назначение |
|---|---|---|
json.dumps(obj) | Python → JSON | возвращает str |
json.loads(s) | JSON → Python | читает из str |
json.dump(obj, f) | Python → JSON | записывает в файл |
json.load(f) | JSON → Python | читает из файла |
Ключевые необязательные параметры:
indent=2— форматированный вывод с отступом в 2 пробелаsort_keys=True— сортировка ключей словаря в алфавитном порядкеcls=MyEncoder— использование пользовательского подклассаJSONEncoderobject_hook=fn— преобразование каждого декодированного object dict