Перейти к содержимому
Теория · Модули, файлы и генераторы · урок 5 из 12

Формат JSON

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

С данными программы происходит то же самое. Метод write записывает в файл только строки, а данные программы — это словари, списки, числа. Их нужно превратить в строку, а потом собрать обратно. Такое превращение называют сериализацией.

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

Python
import json

data = {"name": "Ann", "langs": ["python", "go"], "admin": False}
text = json.dumps(data)
print(text)  # {"name": "Ann", "langs": ["python", "go"], "admin": false}
back = json.loads(text)
print(back["langs"][0], back == data)  # python True

Давайте разберём пример по строкам:

  1. data — обычный словарь со строкой, списком и False.
  2. json.dumps(data) упаковывает словарь в строку text.
  3. print(text) показывает, как выглядит JSON: почти как словарь Python, но False превратился в false.
  4. json.loads(text) разбирает строку и строит из неё новые объекты Python.
  5. Последняя строка печатает python True: собранный словарь равен исходному.

Как пишется JSON

У JSON свои правила записи, и они чуть строже, чем у Python:

  • строки стоят только в двойных кавычках;
  • True и False пишутся как true и false, а None — как null;
  • словарь в JSON называется объектом, а список — массивом.

Аргумент indent=2 в json.dumps разбивает текст на строки с отступом в два пробела. Такой файл удобно читать и править вручную.

Что меняется по дороге

Здесь аналогия с переездом заканчивается: шкаф собирают ровно таким, каким он был, а данные иногда приезжают другими. JSON знает только объекты, массивы, строки, числа, true, false и null. Поэтому кортеж записывается массивом и возвращается списком, а ключи объекта всегда строки — число 1 в ключе станет строкой "1":

Python
import json

data = {1: "one", "pair": (1, 2)}
back = json.loads(json.dumps(data))
print(back)  # {'1': 'one', 'pair': [1, 2]}
print([1, 2] == (1, 2), back == data)  # False False

Строки, числа, списки и словари со строковыми ключами возвращаются такими же, какими ушли. Список никогда не равен кортежу, даже с теми же элементами, поэтому back и data уже не равны.

Множество JSON не принимает вовсе: json.dumps({1, 2}) вызывает TypeError: Object of type set is not JSON serializable. Перед упаковкой его превращают в список, например через sorted.

JSON и файлы

Для файлов есть пара функций без буквы s на конце:

  • json.dump(data, f) пишет данные в открытый файл;
  • json.load(f) читает их из файла.

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

Частая ошибка

Строку JSON написали с одинарными кавычками, как в Python:

Python
import json

settings = json.loads("{'theme': 'dark'}")  # ✗
TEXT
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

Конец сообщения указывает место ошибки: символ с индексом 1, где JSON ждал двойную кавычку. JSON принимает строки только в двойных кавычках: '{"theme": "dark"}'. Класс json.JSONDecodeError наследует от ValueError, поэтому неверный JSON ловит и except ValueError.

Главное

  • json.dumps превращает данные в строку JSON, а json.loads собирает из строки новые объекты.
  • В JSON строки только в двойных кавычках, а True, False и None пишутся как true, false и null.
  • Кортеж возвращается списком, ключ-число — строкой, а множество в JSON не записать.
  • Неверный JSON вызывает json.JSONDecodeError, потомка ValueError.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом