Типы JSON и Python: таблица соответствий
Шесть пар перевода между JSON и Python: object — dict, array — list, true — True, null — None. И два типа, которые формат не понимает вовсе.
Редакция Питоники
Первые два урока показали превращение словаря туда и обратно. Но словари в реальных данных наполнены всем сразу: строками, числами, флагами, пустыми значениями. Как каждый из этих типов переживает дорогу в JSON и назад? У формата свой небольшой словарь типов — всего шесть позиций, — и модуль json переводит между двумя языками автоматически. Задача урока: увидеть всю таблицу перевода целиком и запомнить два типа, которые из Python в JSON не проедут.
Таблица соответствий
Вот полный перевод. Слева — типы JSON, как они выглядят в тексте, в центре — что вернёт Python после json.loads, справа — пример.
| Тип JSON | Python | Пример |
|---|---|---|
| object — {"a": 1} | dict | {'a': 1} |
| array — [1, 2] | list | [1, 2] |
| string — "чай" | str | 'чай' |
| number — 42 или 4.5 | int или float | 42 / 4.5 |
| true / false | True / False (bool) | флаги и переключатели |
| null | None | пустое значение |
Пять пунктов из шести очевидны, два стоят проговорить. Number — один тип JSON на оба питоновских: целое число вернётся int, число с точкой — float. Boolean в JSON всегда строчные: true и false — это значения, а не строки, кавычек у них нет.
Все типы в одном dumps
Соберём товар, в котором живут почти все типы сразу, и посмотрим на его json-вид.
import json
item = {
"title": "Кружка",
"price": 490.5,
"count": 2,
"in_stock": True,
"discount": False,
"note": None,
}
print(json.dumps(item))
{"title": "\u041a\u0440\u0443\u0436\u043a\u0430", "price": 490.5, "count": 2, "in_stock": true, "discount": false, "note": null}Читай вывод по колонкам: True превратился в true, False — в false, None — в null, число с точкой осталось с точкой. Кириллица снова уехала в \u-коды — с урока 1 мы знаем, что это режим перевозки по умолчанию, а не порча данных. Модуль json сделал весь перевод сам: ни одного ручного преобразования в коде.
Обратная сторона: что вернёт loads
Теперь дорога назад. Подадим loads ту же смесь типов текстом и спросим типы у каждого значения.
import json
text = '{"count": 2, "price": 490.5, "in_stock": true, "note": null}'
data = json.loads(text)
print(type(data["count"]))
print(type(data["price"]))
print(type(data["in_stock"]))
print(type(data["note"]))
<class 'int'> <class 'float'> <class 'bool'> <class 'NoneType'>
Четыре строки — четыре питоновских типа: int, float, bool и NoneType, то есть тип значения None. Таблица работает в обе стороны: то, что dumps записал true, loads вернул как True. Для программиста это значит, что после чтения данных никаких проверок «а что это вообще такое» не нужно — типы предсказуемы.
Типы — это кирпичики, и собираться они могут как угодно: в списке лежат словари, в словаре — списки, внутри значений — те же типы снова. Перевод работает на всю конструкцию сразу, на любую глубину, поэтому таблица одна на весь раздел. Как собирать и разбирать такие комбинации — темы урока 7, там же пригодится и полное имя каждого типа из сегодняшнего вывода.
Числа: int и float
Числа заслуживают отдельного взгляда: целые проходят без сюрпризов, а вот дробные иногда возвращаются из текста с хвостом.
import json
print(json.dumps(42))
print(json.dumps(3.14))
print(json.dumps(0.1 + 0.2))
42 3.14 0.30000000000000004
Отдельная ловушка — числа в кавычках. В JSON "42" — это не число, а строка из двух символов: после loads тип будет str, и арифметика с ним упадёт. API грешат этим для телефонов, артикулов и индексов — значений, где ведущий ноль и длина важнее величины. Так что тип значения определяют не «числовость» содержания, а кавычки в тексте: есть кавычки — жди str.
Кортеж: проедет, но изменится
А теперь типы, которых в JSON нет. Первый — кортеж. dumps не падает: кортеж похож на список, и модуль записывает его массивом. Но при чтении назад массив станет списком — и это уже другой тип.
import json
point = (55.75, 37.61)
text = json.dumps(point)
print(text)
back = json.loads(text)
print(type(point), type(back))
[55.75, 37.61] <class 'tuple'> <class 'list'>
Кортеж с координатами уехал в JSON массивом — квадратные скобки в тексте. Назад вернулся список. Кортеж после полного круга превращается в список: JSON хранит структуру данных, а не питоновский тип. Про исходный тип он ничего не помнит. Для координат или пар «ключ-значение» это обычно не страшно — индексы и len работают одинаково. Но если код полагается на неизменяемость кортежа или использует его как ключ словаря, разрыв вылезет.
import json
data = {(1, 2): "точка"}
print(json.dumps(data))
TypeError: keys must be str, int, float, bool or None, not tuple
Множество: не переводится вовсе
Второй «непереводимый» тип — set. У JSON нет множества: есть массив, но он хранит порядок и допускает повторы, а множество — нет. Притвориться списком модуль json отказывается: слишком молчаливая подмена — порядок элементов и дубли пропали бы без предупреждения.
import json
tags = {"чай", "кофе"}
print(json.dumps(tags))
TypeError: Object of type set is not JSON serializable
Ошибка Object of type set is not JSON serializable — фирменная фраза модуля json: «тип не сериализуется». Решение простое: преврати множество в список руками — json.dumps(list(tags)) — и порядок с дублями перестанут быть проблемой, потому что ты их проконтролировал сам, например отсортировав: sorted(tags). Это же сообщение ждёт тебя, если в данных затесался datetime — о превращении таких объектов подумают уроки про API, а пока держи в голове правило: JSON понимает только шесть типов из таблицы.
null и None: пустота без кавычек
null в JSON и None в Python — значение «здесь ничего нет». Оно пишется без кавычек, и его легко отличить от строки "null", которая после loads останется обычным текстом.
import json
data = json.loads('null')
print(data, data is None)
None True
Проверка is None — питоновский способ спросить про пустоту, и она сработала. В живых данных null встречается постоянно: у товара нет скидки, у пользователя не заполнен телефон. После loads такие поля станут None, и код проверяет их привычным if.
Практическое следствие: перед обращением к необязательному полю его проверяют. Схема простая: достали значение, спросили if note is None, и только потом использовали. Без проверки None выстрелит позже, в самом неожиданном месте — например, при сложении с числом. В следующем уроке появится и метод .get с умолчанием, но проверка на None работает всегда и везде.
Первая программа: круг со всеми типами
Проверим таблицу целиком: соберём словарь со всеми переводимыми типами и прогоним полный круг из урока 2. Всё, что входит в таблицу, обязано вернуться равным самому себе.
import json
cart = {"count": 2, "price": 490.5, "in_stock": True, "note": None}
back = json.loads(json.dumps(cart))
print(back == cart)
print(type(back["note"]))
True <class 'NoneType'>
Равенство подтвердило: int, float, bool и None пережили дорогу без изменений. Этот трюк — «прогни данные через dumps-loads и сравни» — годится как быстрая проверка своих структур: если круг ломается, значит где-то затесался тип вне таблицы. Приём пригодится и при чтении файлов: json.load из следующего урока вернёт те же типы, что и loads.
Что дальше
Таблица соответствий — карта всего раздела: что бы ни пришло из API и что бы ты ни сохранял в файл, типы двигаются по этим шести рельсам. Впереди: файлы JSON — dump и load для данных, которые слишком большие, чтобы жить в строке; кириллица — параметр, который вернёт русские буквы в вывод; и красивая печать для чтения сложных структур глазами. Напоминание про то, как выглядела первая выгрузка, — в уроке 1.
Шесть типов JSON — шесть пар перевода: object — dict, array — list, string — str, number — int и float, true и false — bool, null — None. Всё остальное в формат не влезает.
Сначала предскажи ответ в голове — это главный навык программиста.
import json
print(json.dumps({"on": True, "off": False, "x": None}))
import json
t = ("a", "b")
back = json.loads(json.dumps(t))
print(type(back), len(back))
import json
data = json.loads('{"price": 9.99}')
print(type(data["price"]))
1. Во что json.loads превратит значение null?
2. Что произойдёт с кортежем (1, 2) после json.loads(json.dumps((1, 2)))?
3. Что означает ошибка Object of type set is not JSON serializable?
4. Какие питоновские типы соответствуют одному типу number в JSON?
5. Питоновское True в json-тексте выглядит как…
Проверь таблицу соответствий на товаре: собери словарь item с ключами title («Стол»), price (7990.5), in_stock (True) и note (None). Прогони полный круг json.loads(json.dumps(item)) и выведи, равен ли результат исходному словарю, и тип значения note.
Какие типы данных есть в JSON и во что они превращаются в Python?
Шесть типов: object — в dict, array — в list, string — в str, number — в int или float, true/false — в True/False (bool), null — в None. Перевод в обе стороны делает модуль json автоматически: dumps пишет питоновские значения на языке формата, loads читает их обратно.
Можно ли сохранить кортеж в JSON?
Как значение — можно, но тип не сохранится: dumps запишет кортеж массивом, а при чтении вернётся список. У JSON нет своего кортежа. Как ключ словаря кортеж не работает вовсе: dumps упадёт с keys must be str, int, float, bool or None, not tuple. Если нужна неизменяемость — храни список и не меняй, или пересобирай данные после чтения в tuple сам.
Почему Python пишет Object of type set is not JSON serializable?
В JSON нет множества: массив хранит порядок и повторы, а set — нет, и молчаливая подмена была бы опасна. Преврати множество в список руками: json.dumps(list(tags)) или sorted(tags) для предсказуемого порядка. То же сообщение появляется для datetime и собственных классов — их сериализуют строкой или словарём заранее.
Чем null в JSON отличается от пустой строки и нуля?
null — это «значения нет вообще»: после loads это None, и проверка data["note"] is None вернёт True. Пустая строка "" — строка без символов, 0 — число, оба вполне себе значения. API используют null для незаполненных полей: у товара нет скидки, у пользователя нет телефона — это не ноль, а именно отсутствие.
Понравился урок? Сошлитесь на него
«Кортеж после полного круга превращается в список: JSON хранит структуру данных, а не питоновский тип.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что читать дальше
json · Урок 1
Что такое JSON и зачем он Python: первый json.dumps
Первое превращение словаря в json-строку одной командой: import json, json.dumps и честный взгляд на кракозябры в выводе — всё исполняется прямо на странице.
json · Урок 2
json.loads: чтение JSON из строки
Обратная дорога: текст JSON становится словарём Python одной командой json.loads — и по ключам можно ходить, считать и менять значения.
json · Урок 4
Файлы JSON: json.dump и json.load
Данные, которые переживают скрипт: json.dump пишет словарь в файл, json.load читает обратно, а round-trip подтверждает — сохранил, прочитал, совпало.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
sqlite3 · Урок 2
Типы данных SQLite и CREATE TABLE: строим схему базы
Пять типов SQLite, ограничения NOT NULL, UNIQUE и DEFAULT, PRIMARY KEY с автонумерацией и ALTER TABLE — проектируем схему таблицы товаров по-взрослому.
sqlite типы данныхnot null unique default
openpyxl · Урок 3
Ячейки в openpyxl: адреса, значения и типы данных
Два способа попасть в ячейку: по адресу и по координатам. Числа против строк, ловушка «2026 как текст», очистка через None и чтение значений обратно.
openpyxl типы данныхopenpyxl записать число
json · Урок 10
Обход данных: циклы по JSON-спискам
for по списку словарей, извлечение полей, суммирование выручки и list comprehension — превращаем выгрузку заказов в отчёт.
python цикл по jsonpython перебрать json список