JSON и Python говорят на одних формах
JSON — формат по умолчанию для API, конфигов и обмена данными в вебе. К счастью для питонистов, JSON-объект ложится прямо на Python-словарь, JSON-массив — на список, JSON-строка — на str. Это близкое совпадение — причина того, что чтение и запись JSON в Python — операция в две строки.
Модуль стандартной библиотеки json обрабатывает оба направления.
Парсинг JSON-строки
json.loads(text) — «load string» — берёт строку в формате JSON и возвращает соответствующий Python-объект:
Результат — обычный словарь. К ключам обращаешься как к любому словарю — никакой специальной обёртки JSON, никакого .parse() на результате.
Если строка невалидна, json.loads поднимает json.JSONDecodeError. Сообщение включает строку и колонку проблемы — обычно этого хватает, чтобы найти пропущенную запятую или неэкранированную кавычку.
Запись JSON в строку
json.dumps(data) — «dump string» — делает обратное: берёт Python-объект и возвращает JSON-строку:
Python-овские True, False и None автоматически превращаются в JSON-ные true, false, null. Числа и строки проходят без изменений. Списки становятся массивами, словари — объектами.
Красивое форматирование
По умолчанию json.dumps выдаёт компактный вывод — ок для транспорта, тяжело людям. Передай indent=2 ради чего-то читабельного:
Ещё пара опций dumps, которые стоит знать:
sort_keys=True— отсортировать ключи объектов по алфавиту. Удобно для детерминированного вывода (конфиги, фикстуры тестов, diff-ы).ensure_ascii=False— писать не-ASCII символы (é, ü, 中) как есть, а не через\u-эскейпы. Обычно правильный выбор для UTF-8 файлов.separators=(",", ":")— самый плотный возможный вывод. В сочетании сensure_ascii=Falseдаёт самый компактный UTF-8 JSON.
Чтение JSON из файла
json.load(file) — без s — читает прямо из файлового объекта. Типичный паттерн — вместе с with open:
import json
with open("config.json") as f:
config = json.load(f)
print(config["version"])
Не нужно сначала читать файл в строку — json.load стримит через файловый объект.
Запись JSON в файл
json.dump(data, file) — файловый напарник:
import json
data = {"created": "2026-01-01", "items": [1, 2, 3]}
with open("state.json", "w") as f:
json.dump(data, f, indent=2)
Опция indent работает и тут. Открой получившийся файл — увидишь аккуратный JSON-документ.
Чтение ответа JSON API
Большинство HTTP-библиотек отдают байты или текст; ты вызываешь json.loads, чтобы превратить их в используемые данные:
import json
import urllib.request
with urllib.request.urlopen("https://api.example.com/users/1") as response:
text = response.read().decode("utf-8")
user = json.loads(text)
print(user["name"])
Библиотека requests (о ней отдельно) пропускает шаг: у объекта ответа есть метод .json(), вызывающий json.loads за тебя.
Что JSON может и не может представить
У JSON типовая система уже, чем у Python. Маппинг в обе стороны:
| Python | JSON |
|---|---|
dict | object |
list, tuple | array |
str | string |
int, float | number |
True | true |
False | false |
None | null |
Кортежи туда-обратно превращаются в списки — кортежность теряется. Множества, пользовательские классы и объекты datetime сериализовать по умолчанию нельзя — получишь TypeError: Object of type X is not JSON serializable.
Работа с datetime и своими объектами
Два частых подхода.
Сначала преобразуй в JSON-безопасную структуру. Делаем трансформацию в своём коде и уже сериализуем обычный словарь:
Передай функцию default=. json.dumps вызывает её для каждого значения, которое не умеет сериализовать:
Когда данные читаешь обратно, ISO-строки в datetime конвертируешь сам — JSON не помнит, чем они были исходно.
Раунд-трип словаря через JSON
Быстрая проверка, что словари переживают преобразование:
Значения равны, но это разные объекты. Часто именно это и нужно: json.dumps + json.loads — дешёвый способ сделать глубокую копию любой JSON-совместимой структуры.
Реалистичный пример
Короткий скрипт, который грузит JSON-конфиг, обновляет поле и сохраняет обратно:
import json
from pathlib import Path
config_path = Path("settings.json")
# Load (with a default if the file doesn't exist).
if config_path.exists():
config = json.loads(config_path.read_text())
else:
config = {"theme": "dark", "last_opened": None}
# Update.
config["last_opened"] = "2026-01-15"
# Save, pretty-printed.
config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False))
Полный цикл «прочитать, изменить, записать» для JSON в десятке строк.
Несколько привычек
- Всегда
with open(...)для файлов. JSON — это просто текст; все правила работы с файлами применяются. - Предпочитай
indent=2для файлов, которые читают люди — конфиги, фикстуры, экспорты. Пропускай для сетевого трафика, где важна компактность. - Ставь
ensure_ascii=Falseдля UTF-8 вывода, чтобы имена с акцентами или нелатинскими символами оставались читаемыми. - Валидируй через
try/except json.JSONDecodeError, когда парсишь данные, которые не сам произвёл.
Дальше
JSON обрабатывает данные ключ-значение. Следующий инструмент в той же главе — CSV, встроенная поддержка табличного формата, стоящего за большинством экспортов из таблиц, что ты встретишь.
Часто задаваемые вопросы
Как распарсить JSON в Python?
Используй json.loads(text) для JSON-строки или json.load(file) для файлового объекта. Оба возвращают Python-словарь (или список — смотря что в JSON). Пример: data = json.loads('{"name": "Rosa"}') — data['name'] даст 'Rosa'.
Как преобразовать словарь Python в JSON?
json.dumps(my_dict) возвращает JSON-строку. json.dump(my_dict, file) пишет прямо в файл. Для красивого вывода передай indent=2: json.dumps(data, indent=2).
В чём разница между json.loads и json.load?
loads (с s) принимает строку. load (без s) принимает файловый объект. Аналогично для dumps vs dump. Буква s — от string, так проще запомнить.
Как обрабатывать даты и свои объекты в JSON?
В JSON нет типа для даты, так что передавай даты строками ISO-8601 и парсь их обратно вручную. Для своих классов либо передавай функцию default= в json.dumps, возвращающую JSON-безопасное представление, либо сам конвертируй в dict до сериализации.