Django-Admin.ru

JSON в Python: модуль json

Как работать с JSON в Python: json.dumps и json.loads, запись и чтение файла через dump и load, кириллица и ensure_ascii, indent, сериализация datetime.

Редакция Django-Admin.ru
· обновлено · 9 мин чтения

Содержание · 10 разделов
  1. Четыре функции модуля json
  2. json.dumps: объект Python в строку JSON
  3. ensure_ascii=False: кириллица без экранирования
  4. indent, sort_keys и separators: форматирование
  5. json.loads: строка JSON в объект Python
  6. Соответствие типов Python и JSON
  7. Запись и чтение JSON-файла: dump и load
  8. Сериализация datetime и других типов
  9. Свой JSONEncoder и dataclass
  10. Проверка и форматирование JSON из командной строки
  11. Частые ошибки
  12. JSONDecodeError: Expecting property name enclosed in double quotes
  13. JSONDecodeError: Expecting value: line 1 column 1 (char 0)
  14. Перепутали load и loads
  15. Кракозябры или UnicodeDecodeError при чтении файла
  16. NaN в результате
  17. Частые вопросы
  18. Что значит ошибка Unexpected UTF-8 BOM?
  19. Как прочитать файл, где на каждой строке свой JSON?
  20. Как обращаться к полям JSON через точку, а не по ключу?
  21. Как разобрать строку в одинарных кавычках, если исправить источник нельзя?
  22. Чем json отличается от pickle?
  23. Итог

Для работы с JSON в Python есть встроенный модуль json — ничего устанавливать не нужно. Короткий ответ: json.dumps(obj) превращает словарь или список в строку JSON, json.loads(text) — строку обратно в объекты Python, а json.dump() и json.load() делают то же самое с файлами. Для читаемой кириллицы передавайте ensure_ascii=False.

В статье — четыре основные функции модуля, соответствие типов Python и JSON, форматирование, запись и чтение файлов, сериализация datetime и разбор типичных ошибок. Примеры проверены на Python 3.12, вывод показан комментарием # →.

Четыре функции модуля json

ФункцияЧто делаетРаботает с
json.dumps(obj)объект Python → строка JSONстрокой
json.loads(text)строка JSON → объект Pythonстрокой
json.dump(obj, file)объект Python → запись JSON в файлфайлом
json.load(file)чтение JSON из файла → объект Pythonфайлом

Запомнить просто: буква s на конце означает string — функция работает со строкой.

json.dumps: объект Python в строку JSON

import json

user = {"name": "Анна", "age": 30, "admin": True, "tags": ["dev", "ops"], "phone": None}

text = json.dumps(user)
print(text)
# → {"name": "\u0410\u043d\u043d\u0430", "age": 30, "admin": true, "tags": ["dev", "ops"], "phone": null}
print(type(text))  # → <class 'str'>

True стал true, None — null, а кириллица превратилась в последовательности вида \u0410. Это корректный JSON, и любой парсер прочитает его правильно, но человеку такой текст неудобен.

ensure_ascii=False: кириллица без экранирования

По умолчанию ensure_ascii=True, и все не-ASCII символы экранируются. Отключите это, чтобы кириллица осталась читаемой:

print(json.dumps(user, ensure_ascii=False))
# → {"name": "Анна", "age": 30, "admin": true, "tags": ["dev", "ops"], "phone": null}

indent, sort_keys и separators: форматирование

print(json.dumps(user, ensure_ascii=False, indent=2))
{
  "name": "Анна",
  "age": 30,
  "admin": true,
  "tags": [
    "dev",
    "ops"
  ],
  "phone": null
}
print(json.dumps({"b": 1, "a": 2}, sort_keys=True))  # → {"a": 2, "b": 1}
print(json.dumps({"a": 1, "b": [1, 2]}, separators=(",", ":")))  # → {"a":1,"b":[1,2]}

indent задаёт отступ для «красивого» вывода, sort_keys=True сортирует ключи (удобно для сравнения файлов в git), а separators=(",", ":") убирает пробелы и делает JSON максимально компактным для передачи по сети.

json.loads: строка JSON в объект Python

data = json.loads('{"name": "Анна", "age": 30, "admin": true, "phone": null}')
print(data)        # → {'name': 'Анна', 'age': 30, 'admin': True, 'phone': None}
print(type(data))  # → <class 'dict'>
print(data["age"] + 1)  # → 31

Верхним уровнем JSON может быть не только объект: json.loads("[1, 2.5, \"x\", false]") вернёт список [1, 2.5, 'x', False], а json.loads("42") — число 42.

Соответствие типов Python и JSON

PythonJSONОбратно в Python
dictobjectdict
list, tuplearraylist
strstringstr
int, floatnumberint / float
True / Falsetrue/falseTrue / False
NonenullNone

Из таблицы следуют две неочевидные вещи: кортеж после «круга» через JSON возвращается списком, а ключи словаря в JSON всегда строки — числовой ключ станет строковым:

print(json.loads(json.dumps({1: "a", "t": (1, 2)})))  # → {'1': 'a', 't': [1, 2]}

Подробнее о словарях и их ключах — в статье о словарях в Python.

Запись и чтение JSON-файла: dump и load

json.dump() пишет JSON прямо в открытый файл, json.load() читает из него. Всегда указывайте encoding="utf-8". Без него Python до версии 3.14 включительно берёт кодировку системы: на Windows с русской локалью это cp1251, и файл в UTF-8 читается с ошибками. С Python 3.15 по умолчанию включён режим UTF-8 (PEP 686), но явная кодировка делает код независимым от версии и настроек.

import json

config = {"debug": True, "db": {"host": "localhost", "port": 5432}, "title": "Мой сайт"}

with open("config.json", "w", encoding="utf-8") as f:
    json.dump(config, f, ensure_ascii=False, indent=2)

with open("config.json", encoding="utf-8") as f:
    loaded = json.load(f)

print(loaded["db"]["port"], loaded["title"])  # → 5432 Мой сайт

Содержимое config.json:

{
  "debug": true,
  "db": {
    "host": "localhost",
    "port": 5432
  },
  "title": "Мой сайт"
}

Конструкция with сама закроет файл даже при ошибке. Ответы веб-API обычно тоже приходят в JSON: в библиотеке requests для их разбора есть готовый метод response.json() — подробнее в статье о библиотеке requests.

Сериализация datetime и других типов

Модуль json умеет сериализовать только типы из таблицы выше. Дата, Decimal, множество или объект класса вызовут ошибку:

from datetime import datetime, timezone

order = {"id": 7, "created": datetime(2026, 10, 9, 14, 30, tzinfo=timezone.utc)}
json.dumps(order)
# → TypeError: Object of type datetime is not JSON serializable

Быстрое решение — default=str: функция вызывается для каждого неподдерживаемого объекта.

print(json.dumps(order, default=str))
# → {"id": 7, "created": "2026-10-09 14:30:00+00:00"}

Надёжнее написать свою функцию и выводить даты в формате ISO 8601 через isoformat() — его понимают JavaScript, базы данных и большинство API:

from datetime import date, datetime, timezone
from decimal import Decimal

def to_json(obj):
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    if isinstance(obj, Decimal):
        return str(obj)
    if isinstance(obj, set):
        return sorted(obj)
    raise TypeError(f"Тип {type(obj).__name__} не сериализуется в JSON")

order = {
    "id": 7,
    "created": datetime(2026, 10, 9, 14, 30, tzinfo=timezone.utc),
    "total": Decimal("1999.90"),
    "tags": {"new", "paid"},
}
text = json.dumps(order, default=to_json)
print(text)
# → {"id": 7, "created": "2026-10-09T14:30:00+00:00", "total": "1999.90", "tags": ["new", "paid"]}

Обратно JSON возвращает строку, а не дату. Преобразуйте её явно через datetime.fromisoformat():

data = json.loads(text)
created = datetime.fromisoformat(data["created"])
print(repr(created))
# → datetime.datetime(2026, 10, 9, 14, 30, tzinfo=datetime.timezone.utc)

Если даты в JSON узнаются по имени ключа, преобразование можно автоматизировать параметром object_hook — функция получает каждый разобранный словарь:

def parse_dates(d):
    for key, value in d.items():
        if key.endswith("_at"):
            d[key] = datetime.fromisoformat(value)
    return d

print(json.loads('{"id": 1, "created_at": "2026-10-09T14:30:00+00:00"}', object_hook=parse_dates))
# → {'id': 1, 'created_at': datetime.datetime(2026, 10, 9, 14, 30, tzinfo=datetime.timezone.utc)}

О форматах дат, часовых поясах и isoformat() подробно рассказано в статье о datetime в Python.

Свой JSONEncoder и dataclass

Ту же логику можно оформить классом-наследником json.JSONEncoder и передавать через cls=. Для неизвестных типов вызывайте super().default(obj) — он выбросит стандартный TypeError:

class AppEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (datetime, date)):
            return obj.isoformat()
        return super().default(obj)

print(json.dumps({"day": date(2026, 10, 9)}, cls=AppEncoder))  # → {"day": "2026-10-09"}

Объекты dataclass сначала превратите в словарь функцией dataclasses.asdict(). Если нужна полноценная валидация входящего JSON по схеме, посмотрите на Pydantic: его модели умеют и разбирать JSON, и сериализовать даты.

Проверка и форматирование JSON из командной строки

Модуль json можно запустить как утилиту: она проверит синтаксис файла и выведет его с отступами.

python -m json.tool config.json
python -m json.tool --no-ensure-ascii --sort-keys config.json

На Windows вместо python может понадобиться py. Если JSON некорректен, утилита покажет ошибку с номером строки и столбца.

Частые ошибки

JSONDecodeError: Expecting property name enclosed in double quotes

В JSON строки и ключи пишутся только в двойных кавычках, а запятая после последнего элемента запрещена:

json.loads("{'name': 'Анна'}")
# → json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
json.loads('{"a": 1,}')
# → json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 9 (char 8)

В Python 3.13 и новее для лишней запятой сообщение точнее: Illegal trailing comma before end of object: line 1 column 8 (char 7).

Частая причина — попытка разобрать результат str(dict) или print(dict): такой текст записан в синтаксисе Python, и парсер JSON его не примет. Для сериализации всегда используйте json.dumps().

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Парсер получил пустую строку или текст, который вообще не начинается как JSON: пустой файл, HTML-страницу с ошибкой вместо ответа API, слово None. Выведите исходный текст и проверьте, что там действительно JSON. JSONDecodeError — подкласс ValueError, его удобно ловить через try/except.

Перепутали load и loads

json.load() ждёт открытый файл, а не имя файла: json.load("config.json") выбросит AttributeError: 'str' object has no attribute 'read'. Откройте файл через open() или прочитайте строку и вызовите json.loads().

Кракозябры или UnicodeDecodeError при чтении файла

Файл открыт без encoding="utf-8", и Python прочитал его в кодировке системы — на Windows до Python 3.15 это обычно cp1251 или cp1252. Указывайте кодировку явно и при записи, и при чтении.

NaN в результате

json.dumps(float("nan")) вернёт NaN — Python это допускает, но по стандарту JSON такого значения нет, и строгие парсеры (например, в браузере) его не примут. Передайте allow_nan=False, чтобы получить ValueError заранее.

Частые вопросы

Что значит ошибка Unexpected UTF-8 BOM?

Файл сохранён в кодировке «UTF-8 с BOM» — так сохраняют файлы Блокнот в старых версиях Windows и Windows PowerShell 5.1. В начале файла стоит служебный символ, который json не принимает: JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig). Откройте файл с кодировкой utf-8-sig, она пропускает BOM:

with open("data.json", encoding="utf-8-sig") as f:
    data = json.load(f)

Как прочитать файл, где на каждой строке свой JSON?

Это формат JSON Lines (.jsonl), в нём хранят логи и выгрузки. json.load() на таком файле упадёт с Extra data, поэтому разбирайте строки по одной:

with open("events.jsonl", encoding="utf-8") as f:
    events = [json.loads(line) for line in f if line.strip()]

print(events)  # → [{'id': 1, 'type': 'login'}, {'id': 2, 'type': 'logout'}]

Как обращаться к полям JSON через точку, а не по ключу?

Передайте в object_hook класс SimpleNamespace — каждый словарь станет объектом с атрибутами:

from types import SimpleNamespace

cfg = json.loads('{"db": {"host": "localhost", "port": 5432}}',
                 object_hook=lambda d: SimpleNamespace(**d))
print(cfg.db.port)  # → 5432

Для данных с проверкой типов лучше подходят модели Pydantic.

Как разобрать строку в одинарных кавычках, если исправить источник нельзя?

Если строка — это напечатанный словарь Python, используйте ast.literal_eval(): он безопасно разбирает литералы Python (True, None, одинарные кавычки) и не выполняет код. ast.literal_eval("{'name': 'Анна', 'active': True}") вернёт словарь. Функцию eval() для этого не применяйте.

Чем json отличается от pickle?

json пишет текст, который читают любые языки и сервисы, но поддерживает только базовые типы. pickle сохраняет почти любой объект Python в двоичном виде, но формат понятен только Python, а загрузка pickle из недоверенного источника позволяет выполнить произвольный код. Для обмена данными и конфигов выбирайте JSON.

Итог

Для JSON в Python достаточно четырёх функций: dumps и loads для строк, dump и load для файлов. Для кириллицы добавляйте ensure_ascii=False, для читаемости — indent=2, при работе с файлами — encoding="utf-8", а даты сериализуйте через default и isoformat(). Другие модули стандартной библиотеки и конструкции языка собраны в шпаргалке по синтаксису Python.

  • #Python
  • #JSON
  • #json
  • #Сериализация
  • #Стандартная библиотека
↑ ↓ выборEnter открытьEsc закрыть