JSON в Python: чтение и запись файлов, json.load/json.dump — практическое руководство
JSON — дефолтный формат обмена данными между сервисами и фронтендом. В Python для работы с ним есть стандартный модуль json: лёгкий, быстрый и удобный. В этом руководстве вы узнаете, как читать и записывать JSON-файлы, работать со строками, управлять форматированием, обрабатывать большие логи в формате JSON Lines и сериализовать нестандартные типы данных.
Что такое JSON и где он используется
JSON (JavaScript Object Notation) — текстовый формат представления структурированных данных. Его используют API, мобильные приложения, сервисы логирования и аналитики. Ключевые преимущества: человекочитаемость, компактность и поддержка во всех языках программирования, включая Python.
Быстрый старт: чтение и запись файлов JSON (json.load, json.dump)
Самые частые операции — загрузить JSON из файла в Python-объекты (dict, list) и сохранить обратно.
import json
from pathlib import Path
path = Path("data/users.json")
# Чтение JSON из файла
with path.open(encoding="utf-8") as f:
users = json.load(f) # list[dict] или dict — зависит от структуры файла
# Изменение данных в памяти
users.append({"id": 3, "name": "Ирина", "active": True})
# Запись JSON в файл (красиво отформатированный, с кириллицей)
with path.open("w", encoding="utf-8") as f:
json.dump(users, f, ensure_ascii=False, indent=2)
Обратите внимание на параметры:
- encoding="utf-8" — избегает проблем с кодировкой;
- ensure_ascii=False — сохраняет кириллицу, а не \uXXXX-последовательности;
- indent=2 — читабельный формат для диффов и ревью.
Строки: json.loads и json.dumps
Когда данные приходят по сети или из БД как текст, используйте функции для строк:
import json
from decimal import Decimal
text = '{"price": 12.50, "tags": ["sale", "promo"]}'
# Преобразуем строку JSON в объекты Python
# parse_float=Decimal — чтобы не потерять точность в деньгах
data = json.loads(text, parse_float=Decimal)
# Обратно в строку (компактный вид без лишних пробелов)
packed = json.dumps(data, ensure_ascii=False, separators=(",", ":"))
print(packed) # {"price":12.50,"tags":["sale","promo"]}
Форматирование: компактный vs читаемый JSON
Для хранения и передачи по сети чаще нужен компактный JSON, а для ревью — читаемый.
import json
# Компактный вариант (минимум байт)
compact = json.dumps({"a": 1, "b": [1, 2, 3]}, ensure_ascii=False, separators=(",", ":"))
# Красивый вывод (ключи отсортированы для стабильных диффов)
pretty = json.dumps({"b": [1, 2, 3], "a": 1}, ensure_ascii=False, indent=2, sort_keys=True)
print(compact)
print(pretty)
- separators=(",", ":") — убирает пробелы после запятых и двоеточий;
- sort_keys=True — одинаковый порядок ключей облегчает сравнение файлов в git.
Большие данные: JSON Lines (NDJSON)
Для логов и стриминга часто используют формат JSON Lines (по одной JSON-записи на строку). Его удобно писать и читать построчно, не загружая все данные в память.
import json
# Запись NDJSON
with open("events.jsonl", "w", encoding="utf-8") as f:
for i in range(3):
row = {"event": "click", "i": i}
f.write(json.dumps(row, ensure_ascii=False) + "\n")
# Чтение NDJSON (строка за строкой)
events = []
with open("events.jsonl", encoding="utf-8") as f:
for line in f:
if line.strip(): # пропускаем пустые строки
events.append(json.loads(line))
print(events)
Плюсы NDJSON: потоковая обработка, меньшая пиковая память, лёгкая конкатенация файлов. Минус: это не единый JSON-объект, а набор отдельных записей по строкам.
Кастомная сериализация: datetime, Decimal и другие типы
Стандартный json умеет сериализовать только базовые типы (dict, list, str, int, float, bool, None). Для нестандартных добавляем default-функцию.
import json
from datetime import datetime
from decimal import Decimal
def to_json(obj):
if isinstance(obj, datetime):
return obj.isoformat() # "2025-01-15T10:30:00"
if isinstance(obj, Decimal):
return float(obj) # или str(obj), если нужна точность при передаче
raise TypeError(f"Type {type(obj)} is not JSON serializable")
order = {
"id": 101,
"total": Decimal("19.90"),
"created_at": datetime(2025, 1, 15, 10, 30),
}
text = json.dumps(order, default=to_json, ensure_ascii=False, indent=2)
print(text)
А чтобы преобразовывать значения при загрузке, используйте object_hook — она вызывается для каждого dict.
import json
from datetime import datetime
# Предположим, у нас есть JSON из примера выше в переменной text
def parse_datetime(d):
for k, v in list(d.items()):
if k.endswith("_at") and isinstance(v, str):
try:
d[k] = datetime.fromisoformat(v)
except ValueError:
pass
return d
restored = json.loads(text, object_hook=parse_datetime)
print(type(restored["created_at"])) # <class 'datetime.datetime'>
Безопасность и надёжность
- Никогда не используйте eval для разбора JSON — только json.loads/json.load.
- Проверяйте размеры входных данных, если читаете из внешних источников (ограничивайте upload size, применяйте таймауты на уровне транспорта).
- Указывайте encoding="utf-8" при работе с файлами — меньше сюрпризов на проде.
- Логируйте ошибки парсинга с деталями позиции — это ускоряет отладку.
Частые ошибки и как их избежать
- Одинарные кавычки и лишние запятые — это не JSON. Разрешены только двойные кавычки, запятая после последнего элемента запрещена.
- Путаете функции: dump/load — для файлов, dumps/loads — для строк.
- Теряете кириллицу, видите \u041F... — включите ensure_ascii=False и правильную кодировку файла.
- Денежные суммы на float — лучше используйте Decimal (parse_float=Decimal) или храните в копейках как int.
import json
bad = "{'a':1,}" # одинарные кавычки и лишняя запятая — это НЕ JSON
try:
json.loads(bad)
except json.JSONDecodeError as e:
print(f"Ошибка JSON: {e}")
Мини-практика: маленький конвертер с фильтрацией
Пример: читаем массив объектов из файла, фильтруем активных пользователей и сохраняем компактный JSON для API.
import json
from pathlib import Path
src = Path("data/users.json")
dst = Path("build/active_users.json")
dst.parent.mkdir(parents=True, exist_ok=True)
with src.open(encoding="utf-8") as f:
users = json.load(f)
active = [u for u in users if u.get("active") is True]
with dst.open("w", encoding="utf-8") as f:
json.dump(active, f, ensure_ascii=False, separators=(",", ":"))
print(f"Сохранено: {dst} — {len(active)} записей")
Чеклист лучших практик JSON в Python
- Для файлов: json.load/json.dump и encoding="utf-8".
- Для строк: json.loads/json.dumps.
- Для продакшена: compact JSON (separators), для ревью — indent и sort_keys.
- Русский текст: ensure_ascii=False.
- Деньги: Decimal или int-центы; при сериализации используйте default.
- Большие потоки: JSON Lines (строка = запись).
- Пользовательские типы: default и object_hook.
- Валидируйте входные данные и обрабатывайте JSONDecodeError.
Куда двигаться дальше
Освоив базовые операции с JSON, вы готовы к API-клиентам, интеграциям и обработке логов. Если хотите системно прокачать Python с практикой и проектами — присмотритесь к курсу: Прокачать Python с нуля до гуру на реальных задачах →.
Теперь у вас под рукой удобный рецепт: как читать и записывать JSON в Python, красиво форматировать данные, работать с большими файлами и сериализовать сложные типы. Пользуйтесь и экономьте часы на рутине!
-
Создано 16.02.2026 17:01:14
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.