Лекция
Сериализация: JSON, DTO и модели домена
Лекция о сериализации в формате JSON, адаптации данных для передачи и моделях домена.
- сериализация
- JSON
- DTO
- модели домена
- обмен данными
- десериализация
- передача данных
Для звонков по России
Личный кабинет
Лекция
Лекция о сериализации в формате JSON, адаптации данных для передачи и моделях домена.
Подскажем по теме, разберём задание и поможем довести работу до результата.
Сериализация переводит «живой» объект из памяти в строку или поток байтов для передачи по сети или записи в файл. Это позволяет сохранить состояние данных, чтобы позже воссоздать их в другой системе.
В оперативной памяти (Heap) объект — это сложная структура с указателями и адресами, понятными только конкретной программе. Сетевой протокол не видит этих связей и не может передать объект целиком, так как тот жестко привязан к контексту среды выполнения.
Чтобы переслать информацию, нужно отделить состояние от поведения. Методы и логика остаются внутри приложения, а значения полей упаковываются в универсальный формат, доступный любому получателю.
Возьмём пример: объект — это корабль в порту с командой, двигателем и грузом. Чтобы отправить товары в другой город по суше, вы не перевозите всё судно. Вы выгружаете содержимое в стандартные контейнеры. JSON — это такой контейнер: он унифицирован, легко встает на любой «транспорт» (например, HTTP-запрос) и не зависит от устройства самого «корабля».
| Характеристика | Объект в памяти (Heap) | JSON-данные (в сети) |
|---|---|---|
| Формат | Бинарный, специфичный для языка | Текстовый (UTF-8) |
| Связи | Ссылки и указатели на адреса | Вложенный текст |
| Компоненты | Данные и методы | Только данные (состояние) |
Диаграмма загружается…
Процесс воссоздания данных из «контейнера» обратно в программную структуру на стороне получателя называется десериализацией. Благодаря этой паре процессов системы на разных языках могут обмениваться информацией без потери смысла.
JSON стал эталоном в веб-разработке, так как его одинаково легко читают и человек, и машина. В какой формат превратится ваш объект — зависит от выбранной библиотеки, но принцип «упаковки» состояния остается неизменным.
JSON (JavaScript Object Notation) — это формат обмена данными, основанный на парах «ключ — значение». Он преобразует объекты из памяти программы в текстовую строку, которую легко передать по сети и прочитать на любом языке.
Несмотря на упоминание JavaScript в названии, формат универсален. Это технологический стандарт: объект, созданный в Java, без искажений восстанавливается в среде Python, Go или C#. Пока данные находятся в оперативной памяти, они привязаны к правилам конкретной платформы, но JSON превращает их в нейтральный текст, преодолевая границы программных сред.
Структура JSON наглядно описывает объекты и коллекции через две базовые конструкции:
{ }): набор пар «название : значение». Соответствует полям класса или типу Map.[ ]): упорядоченный список элементов, аналог List.Изучим пример описания книги:
{
"title": "Clean Code",
"pages": 464,
"isAvailable": true,
"tags": ["programming", "architecture"],
"author": {
"name": "Robert Martin"
}
}
Здесь ключ "title" — это строковое поле, число 464 — числовой тип, а список tags — коллекция строк. Вложенный блок author доказывает, что формат успешно передает сложные иерархии и графы объектов, а не только плоские таблицы.
В отличие от закрытых бинарных протоколов, JSON использует открытый текст в кодировке Unicode. Такой подход дает три ключевых преимущества:
Диаграмма загружается…
Текстовая схема разрывает жесткую зависимость между отправителем и получателем. Как строительный чертеж понятен инженеру в любой стране, структура JSON описывает состояние данных, оставляя выбор инструментов обработки на усмотрение принимающей стороны. Это позволяет строить масштабируемые системы, где компоненты общаются на одном «языке», сохраняя полную независимость кода.
Достаточно ли просто передать текст, чтобы система заработала корректно, или существуют скрытые правила типизации, которые стоит учитывать?
Data Transfer Object (DTO) — это объект для перевозки данных между частями системы или по сети в формате JSON. Он работает как почтовый конверт: хранит информацию, но не содержит логики.
В архитектуре приложений часто конфликтуют два подхода. Один из них — Модель Домена (Domain Model), ядро программы. Она живет долго, инкапсулирует правила и строго следит за целостностью данных: нельзя просто переписать поле, не пройдя проверку бизнес-логики. Другой подход требует передать состояние этой модели клиенту, например мобильному приложению. Пересылать «живой» объект с его внутренними связями и методами неэффективно. Эту задачу берет на себя DTO.
Возьмём пример со сложным смесителем, оснащённым фильтрами и датчиками — это Модель Домена. DTO в этой схеме напоминает обычный стакан. Ему не нужны встроенные приборы, его роль — просто донести воду от крана до стола.
При проектировании таких объектов разработчики осознанно отступают от классического ООП:
Обратимся к, как одна и та же сущность «Пользователь» выглядит в разных ролях.
Диаграмма загружается…
Доменная модель хранит хэш пароля и методы верификации. В DTO мы убираем секретные данные и оставляем только то, что нужно отобразить в профиле.
В Python для таких структур подходят dataclass. Они лаконичны и подчеркивают, что объект — это лишь набор атрибутов.
from dataclasses import dataclass
# Модель домена (Domain Model)
class Product:
def __init__(self, name, price, stock):
self._name = name
self._price = price
self._stock = stock
def apply_discount(self, percent):
# Бизнес-логика: проверка правил
if 0 < percent < 50:
self._price -= self._price * (percent / 100)
# Объект передачи данных (DTO)
@dataclass
class ProductDTO:
name: str
price: float
is_available: bool
# Пример маппинга
product = Product("Ноутбук", 100000, 5)
product.apply_discount(10)
# Преобразование Домена в DTO для JSON
dto = ProductDTO(
name=product._name,
price=product._price,
is_available=product._stock > 0
)
Использование DTO защищает внутреннюю логику. Если вы измените способ хранения данных в Product (например, переименуете _price в _base_cost), структура JSON останется прежней. Внешние потребители API не заметят рефакторинга.
Такой подход делает обмен информацией стабильным контрактом. Помните: DTO — это данные без обязательств. Какую структуру вы бы выбрали для передачи сложного финансового отчёта?
Маппинг — это сопоставление структуры текстовых данных (JSON) с полями объекта в памяти программы. Библиотеки-мапперы вроде Jackson, GSON или Marshmallow анализируют ключи в строке и распределяют их значения по атрибутам создаваемого экземпляра.
Во время обработки строки библиотека выполняет три этапа. Сначала происходит парсинг — разбор текста на составные части (токены). После этого инструмент задействует рефлексию, чтобы исследовать структуру целевого класса и найти подходящие поля. Завершается процесс инстанцированием: созданием объекта и заполнением его данными.
Диаграмма загружается…
Различие в стандартах именования — основная сложность при передаче данных. В веб-среде и API преобладает формат snake_case (слова через подчеркивание), тогда как в объектно-ориентированной разработке принят camelCase. Без дополнительной настройки маппер не распознает ключи и оставит поля объекта пустыми.
Чтобы избежать потери данных, разработчики используют аннотации или глобальные настройки конфигурации. Они создают карту соответствия вручную или заставляют библиотеку автоматически переводить названия из одного стиля в другой.
# Пример использования библиотеки для маппинга
from pydantic import BaseModel, Field
class UserDTO(BaseModel):
# Явное указание соответствия ключа JSON и поля объекта
user_id: int = Field(alias="userId")
full_name: str = Field(alias="full_name")
# JSON с ключами в разных стилях
json_data = '{"userId": 101, "full_name": "Ivan Ivanov"}'
# Процесс маппинга (десериализация)
user = UserDTO.model_validate_json(json_data)
print(user.user_id) # Выведет: 101
Трудности с null-значениями возникают, когда в JSON нет ключа, обязательного для модели, или объект содержит конфиденциальную информацию (например, пароль), которую нельзя сериализовать. Настройка маппера позволяет игнорировать лишние поля или подставлять значения по умолчанию, сохраняя работоспособность приложения.
Отдельный вызов — циклические ссылки. Допустим, объект «Отдел» ссылается на список «Сотрудников», а каждый «Сотрудник» хранит ссылку обратно на «Отдел». Попытка прямой конвертации вызовет бесконечный цикл упаковки данных. Эту проблему решает использование DTO: такая модель разрывает связь, заменяя вложенный объект его идентификатором (ID).
Как обеспечить безопасность и неизменность этих данных при передаче через внешние интерфейсы? Грамотная настройка маппинга — это первый шаг к защите внутренней логики системы от некорректных входных сигналов.
Безопасность при сериализации — это контроль над тем, какие данные покидают контур приложения, а какие остаются скрытыми. Ошибки на этом этапе приводят к утечке паролей, компрометации бизнес-логики или несанкционированному доступу к функциям системы.
Объекты в памяти часто содержат информацию, не предназначенную для клиента. Использование «сырых» сущностей базы данных вместо подготовленных DTO (Data Transfer Object) создает риск уязвимости Mass Assignment. В этом случае злоумышленник может перезаписать скрытые поля — например, статус администратора или баланс счета — просто добавив их в присылаемый JSON-запрос.
| Категория данных | Почему опасна передача целиком |
|---|---|
| Секреты | Хеши паролей и токены должны находиться только во внутренней памяти приложения. |
| Технические ID | Внутренние первичные ключи БД раскрывают структуру таблиц, упрощая подготовку SQL-инъекций. |
| Служебные поля | Версии строк и флаги «удалено» (soft-delete) — это детали реализации, а не бизнес-данные. |
Чтобы тонко настроить маппинг, применяют аннотации. Они явно указывают сериализатору, как обрабатывать конкретное свойство, не меняя физическую структуру кода.
class UserDto {
String username;
// Скрывает поле при превращении объекта в JSON
@JsonIgnore
String rawPassword;
// Переименовывает ключ для соответствия API-контракту
@JsonProperty("registration_date")
String createdAt;
}
Разделение данных на доменную модель и DTO реализует принцип единственной ответственности (SRP). Модель отвечает за бизнес-логику, а DTO — за формат общения с внешним миром. Такая изоляция позволяет менять структуру таблиц в базе данных, не нарушая работу фронтенда: достаточно обновить правила преобразования полей.
Диаграмма загружается…
Всегда запопробуем себе вопрос: «Что произойдет, если клиент пришлет это поле обратно с измененным значением?». Если ответ сулит проблемы с безопасностью, поле не должно попадать в формат обмена данными.
Вопросы для самопроверки:
@JsonIgnore для полей с паролями?