Полиморфные JSON-объекты: Лучший подход к API
Узнайте, почему полиморфные JSON-объекты предпочтительнее плоских с null, и как реализовать ответы, улучшая клиентский опыт и поддержку для масштабируемых систем.
Нормально ли отправлять JSON‑ответ, содержащий список объектов, свойства которых зависят от типа сущности? Например:
{
"entity_type": "company",
"registered_date": "2020-01-01",
"reference_number": "ABC1234567",
"legal_form": "partnership"
},
{
"entity_type": "person",
"date_of_birth": "1990-01-01",
"reference_number": "12345",
"age": 35
}
Считается ли это хорошим дизайном API, или лучше вернуть один плоский объект со всеми возможными полями, оставляя нерелевантные как null?
Полиморфные объекты в ответах JSON‑API действительно являются распространённой и принятым практикой, особенно когда речь идёт о сущностях с фундаментально разными свойствами. В вашем примере показан устоявшийся шаблон, где объекты имеют общие поля, но также и тип‑специфические свойства. Такой подход обычно предпочтительнее, чем возвращать один плоский объект с множеством null‑значений, поскольку он обеспечивает более чистую и осмысленную структуру данных, которую легче обрабатывать на стороне клиента.
Содержание
- Понимание полиморфных JSON‑объектов
- Плоские объекты с
null: альтернативный подход - Сравнение обоих подходов
- Лучшие практики реализации
- Стандарты отрасли и документация
- Выводы и рекомендации
Понимание полиморфных JSON‑объектов
Полиморфные JSON‑объекты, как ваш пример с сущностями «компания» и «человек», являются естественным продолжением концепций объектно‑ориентированного программирования в JSON‑API. Такой подход позволяет разным типам объектов совместно использовать общий интерфейс, при этом сохраняя свои уникальные свойства.
Почему полиморфные объекты распространены:
- В @balanced есть конечная точка, которая возвращает полиморфные типы Credit, Debit, Hold, Refund и Reversal в порядке создания GitHub json‑api issue
- Этот шаблон позволяет API эффективно обрабатывать коллекции связанных, но отличных сущностей
- Клиент может корректно обрабатывать различные типы сущностей, опираясь на поле
entity_type
Ключевые характеристики полиморфного JSON:
- Каждый объект содержит идентификатор типа (ваше поле
entity_type) - Общие свойства разделяются между типами (например,
reference_number) - Тип‑специфические свойства включаются только для соответствующих объектов
- Структура остаётся предсказуемой и валидируемой
«Полиморфизм и наследование – это ключевые паттерны, которые улучшают дизайн вашего API и избавляют от дублирования кода» – Bump.sh API documentation guide
Плоские объекты с null: альтернативный подход
Альтернативный подход подразумевает возврат единой, унифицированной структуры объекта со всеми возможными полями, при этом не относящиеся к конкретному типу поля устанавливаются в null или полностью опускаются.
Рассмотрение плоских объектов:
- Reddit‑обсуждение указывает, что вложенные объекты дают лучший контекст для соответствующих свойств r/AskProgramming
- Сжатие JSON создаёт компромисс между компактностью и производительностью чтения Stack Overflow
- Ведутся дискуссии о том, должны ли JSON‑объекты явно включать
null‑значения Stack Overflow
Потенциальные проблемы плоских объектов:
- Неоднозначность: отсутствие свойств создаёт неясность, является ли поле
nullили просто отсутствует - Путаница типов: свойства, не применимые к определённым типам сущностей, становятся бессмысленными
- Сложность схемы: схема становится громоздкой из-за полей, которые могут никогда не использоваться
- Сложность на стороне клиента: клиентам приходится постоянно проверять
null‑значения и обрабатывать условную логику
Сравнение обоих подходов
Давайте рассмотрим ключевые различия между полиморфным и плоским подходами:
| Аспект | Полиморфные объекты | Плоские объекты с null |
|---|---|---|
| Читаемость | Высокая – каждый объект содержит только релевантные данные | Низкая – множество null/неактуальных свойств |
| Обработка клиентом | Простая – обрабатывается по типу сущности | Сложная – условная логика для каждого свойства |
| Документация API | Чёткое разделение ответственности | Единая, но потенциально запутанная схема |
| Эффективность данных | Эффективная – передаётся только необходимая информация | Потенциально неэффективная – много null‑значений |
| Валидация | Тип‑специфические схемы можно валидировать | Одна схема с множеством опциональных полей |
| Расширяемость | Лёгко добавлять новые типы сущностей | Труднее – затрагивает всех существующих клиентов |
Примеры из реального мира:
Обсуждение на Stack Overflow о моделировании RESTful API с наследованием демонстрирует именно этот паттерн в OpenAPI/Swagger:
requestBody:
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Fish'
discriminator:
propertyName: animal_type
Это показывает, что полиморфизм не только теоретический, но и активно поддерживается современными стандартами документации API.
Лучшие практики реализации
Для полиморфных объектов:
- Используйте единый идентификатор типа: ваше поле
entity_typeидеально подходит для этого - Тщательно документируйте схему: применяйте ключевые слова
oneOfиdiscriminatorв OpenAPI 3.0 - Обеспечьте обратную совместимость: при добавлении новых типов существующие клиенты должны продолжать работать
- Проводите валидацию с обеих сторон: убедитесь в серверной валидации и корректной обработке типов на клиенте
Исследовательские выводы:
- «Используйте явное поле в объекте, чтобы указать его тип. Большинство библиотек для маппинга JSON в объекты можно настроить так, чтобы они создавали объекты соответствующих подклассов» – Software Engineering Stack Exchange
Обработка полиморфных ответов:
- Salesforce Stack Exchange предлагает, что для полиморфных ответов может потребоваться парсер, чтобы эффективно обрабатывать разные типы данных Salesforce Stack Exchange
Учитывайте десериализацию:
- В .NET «Эта конфигурация позволяет полиморфную сериализацию для WeatherForecastBase, особенно когда тип во время выполнения – WeatherForecastWithCity» – Microsoft Learn
Стандарты отрасли и документация
Поддержка OpenAPI/Swagger:
- «OpenAPI 3.0 (ранее Swagger) действительно поддерживает полиморфизм в JSON» – Stack Overflow
- Решение для представления полиморфных объектов в JSON Schema – «переопределить свойство
typeв схемах then» – Stack Overflow
Стандарт JSON API:
- Сообщество JSON API обсуждает именно этот сценарий: «Допустим, у меня есть ресурс Transportation на /transportations, а Transportation может иметь различные типы» – JSON API discuss
Опыт разработки:
- Как отметил один Java‑разработчик, «десериализация ответа сервиса оказалась в коде, который ничем не читаем, потому что нет прямой связи с классами через геттеры/сеттеры/конструкторы» при использовании плоского JSON для иерархических данных Software Engineering Stack Exchange
Выводы и рекомендации
На основании исследовательских выводов полиморфные JSON‑объекты не только являются нормальной практикой, но и считаются лучшим подходом для API, работающих с сущностями разного типа.
Ключевые выводы:
- Полиморфные объекты – стандарт: этот паттерн широко используется и поддерживается основными стандартами API, такими как OpenAPI 3.0
- Лучше для клиентского опыта: клиенты получают только релевантные данные и могут корректно обрабатывать разные типы сущностей
- Более поддерживаемый: добавление новых типов не ломает существующий клиентский код
- Согласие отрасли: множество источников рекомендуют полиморфизм вместо плоских объектов с
null
Рекомендации:
- Продолжайте использовать полиморфные объекты: ваш текущий подход соответствует лучшим практикам
- Тщательно документируйте: применяйте возможности OpenAPI 3.0, такие как
oneOfиdiscriminator, чтобы чётко определить полиморфное поведение - Учитывайте потребности клиентов: убедитесь, что ваши клиентские библиотеки могут эффективно обрабатывать полиморфные ответы
- Планируйте расширяемость: проектируйте API так, чтобы легко добавлять новые типы сущностей в будущем
Исследования ясно показывают, что полиморфизм в JSON‑API – «ключевой паттерн, который улучшает дизайн вашего API и избавляет от дублирования кода», а не редкое исключение. Ваш пример демонстрирует этот паттерн на практике, делая его как нормальным, так и рекомендуемым для хорошего дизайна API.