База, которую нужно знать про JSON Schema

от автора

Привет, Хабр!

Сегодня мы рассмотрим одну из тем, которая, как ни странно, остаётся недооценённой — JSON Schema. Если ты аналитик (или хочешь им быть) и в твоей работе часто мелькают JSON-файлы, то наверняка знаешь, как сложно порой бывает держать всё это под контролем. В этой статье мы разберём всё, что тебе нужно знать про JSON Schema.

JSON Schema: что это и зачем тебе нужно?

Итак, начнем с основ. JSON Schema — это спецификация для описания структуры JSON-документов. Представь себе это как строгий контракт между клиентом и сервером. Как только ты начинаешь разрабатывать API, в один момент наступает полный бардак, если нет четких правил, что можно, а что нельзя отправлять в запросах или получать в ответах.

JSON Schema позволяет описать:

  • Формат данных (числа, строки, объекты, массивы).

  • Допустимые значения (минимумы, максимумы).

  • Обязательные поля и необязательные.

  • И много других классных вещей, которые спасут тебе кучу времени на дебаг.

Простой JSON Schema

Сразу к практике. Допустим, нужно описать JSON для пользователя. В JSON будут имя, возраст и email. Начнем с простого:

{   "$schema": "http://json-schema.org/draft-07/schema#",   "type": "object",   "properties": {     "name": {       "type": "string"     },     "age": {       "type": "integer",       "minimum": 18     },     "email": {       "type": "string",       "format": "email"     }   },   "required": ["name", "age", "email"] }

Что тут происходит?

  1. $schema — это версия спецификации, которой ты следуешь (мы используем draft-07).

  2. type — указывает на тип данных. В нашем случае это объект.

  3. properties — это описание полей, где указываются их типы и дополнительные ограничения. Например, возраст (age) должен быть целым числом и минимум 18.

  4. required — обязательные поля, без которых наш пользователь не имеет права на существование.

Да, в этом примере мы проверяем, чтобы возраст не был меньше 18 лет. Это выглядит довольно тривиально, но под капотом JSON Schema спрятано много возможностей, о которых сейчас и поговорим.

Сложные схемы

Теперь усложним задачу. Допустим, нужно описать не просто пользователя, а массив пользователей. Каждый пользователь имеет айдишник, имя, возраст и список адресов. JSON Schema тут тоже спасает:

{   "$schema": "http://json-schema.org/draft-07/schema#",   "type": "array",   "items": {     "type": "object",     "properties": {       "id": {         "type": "string"       },       "name": {         "type": "string"       },       "age": {         "type": "integer",         "minimum": 18       },       "addresses": {         "type": "array",         "items": {           "type": "object",           "properties": {             "street": {               "type": "string"             },             "city": {               "type": "string"             },             "postalCode": {               "type": "string",               "pattern": "^[0-9]{5}$"             }           },           "required": ["street", "city", "postalCode"]         }       }     },     "required": ["id", "name", "age"]   } }

Здесь происходит куча всего:

  1. Мы описали массив type: array, где каждый элемент — объект пользователя.

  2. У пользователя есть список адресов, который тоже является массивом.

  3. В каждом адресе у нас есть регулярка на проверку индекса (почтового кода). Здесь мы добавляем немного сладости — регулярные выражения (в нашем случае индекс должен быть строго пятизначным числом).

Да, на первый взгляд всё просто, но когда нужно валидировать реальные данные (сотни пользователей, куча полей, различные страны), такие мелочи спасают API от краха.

AnyOf, AllOf, OneOf

JSON Schema — это не только про строгие рамки. Часто бывает так, что ты не знаешь точно, какой тип данных ожидается. Например, можно получать объект или строку в зависимости от контекста. Тут помогают такие штуки как anyOf, oneOf и allOf.

AnyOf: как говорить «или то, или другое»

Если нужно, чтобы одно из нескольких условий было выполнено, используй anyOf. Например, хочется, чтобы поле могло быть либо числом, либо строкой:

{   "type": "object",   "properties": {     "price": {       "anyOf": [         { "type": "number" },         { "type": "string" }       ]     }   } }

Поле price может быть как числом, так и строкой. Очень полезно, когда есть API, где данные могут поступать в разных форматах, но хочется оставить некоторую свободу.

OneOf: строго одно

С oneOf всё жестче — валидируется только одно из условий:

{   "type": "object",   "properties": {     "discount": {       "oneOf": [         { "type": "number", "minimum": 0, "maximum": 100 },         { "type": "boolean" }       ]     }   } }

Тут поле discount может быть либо процентом скидки (от 0 до 100), либо булевым значением (например, скидка включена или выключена).

AllOf: нужно всё сразу

С allOf идет требование, чтобы выполнялись все условия. Полезно, если есть сложная структура и данные должны соответствовать нескольким критериям:

{   "type": "object",   "properties": {     "product": {       "allOf": [         { "type": "string" },         { "minLength": 3 },         { "pattern": "^[A-Z].*$" }       ]     }   } }

Поле product должно быть строкой, длиной не меньше 3 символов и начинаться с заглавной буквы. Да, вот такие простые требования иногда бывают в реальной жизни.

Как валидировать JSON с помощью JSON Schema

Теперь важный момент — как использовать все эти описания на практике? Ведь написать красивую схему — это только полдела. Нужно её еще и применить для валидации данных.

Например, в Python это делается при помощи библиотеки jsonschema. Установить её можно просто через pip:

pip install jsonschema

Пример валидации:

import jsonschema from jsonschema import validate  # Определяем схему schema = {     "type": "object",     "properties": {         "name": {"type": "string"},         "age": {"type": "integer", "minimum": 18},     },     "required": ["name", "age"] }  # Пример данных data = {     "name": "Nikolay Sobolev",     "age": 5 }  # Валидация данных try:     validate(instance=data, schema=schema)     print("Данные валидны") except jsonschema.exceptions.ValidationError as err:     print(f"Ошибка валидации: {err.message}")

Здесь описали простую схему для пользователя и проверили, соответствуют ли данные этой схеме. Если что-то не так — скрипт выведет сообщение об ошибке.

Кейс применения JSON Schema

Итак, представим себе, что мы работаем в компании, которая производит корма для котиков, и тебе нужно создать API для обработки заказов. Конечно, данные, которые поступают в систему, должны быть строго структурированы.. Допустим, есть большое количество информации о самих кормах: вкус, размер упаковки, срок годности, рекомендации для различных возрастных групп котиков.

Нужно описать JSON-схему, которая будет валидацией для информации о корме. Например, есть такие параметры:

  1. Название продукта — строка, обязательное поле.

  2. Описание — строка, необязательное поле.

  3. Вкус — список вкусов (например, курица, рыба, говядина), обязательное поле.

  4. Вес упаковки — число в граммах, минимальное значение — 50 г, максимальное — 10 000 г.

  5. Цена — число, обязательное поле, минимальное значение — 0.

  6. Срок годности — строка в формате даты.

  7. Рекомендованный возраст котика — целое число, минимальный возраст — 1 год, максимальный — 25 лет.

  8. Рейтинг корма — число с плавающей точкой от 1.0 до 5.0.

  9. Наличие в магазинах — массив с названиями городов.

  10. Акции — объект, который может содержать поле «скидка», но она не должна превышать 50%.

Сздадим JSON Schema для валидации этих данных:

{   "$schema": "http://json-schema.org/draft-07/schema#",   "type": "object",   "properties": {     "productName": {       "type": "string"     },     "description": {       "type": "string"     },     "flavors": {       "type": "array",       "items": {         "type": "string"       },       "minItems": 1     },     "weight": {       "type": "integer",       "minimum": 50,       "maximum": 10000     },     "price": {       "type": "number",       "minimum": 0     },     "expirationDate": {       "type": "string",       "format": "date"     },     "recommendedAge": {       "type": "integer",       "minimum": 1,       "maximum": 25     },     "rating": {       "type": "number",       "minimum": 1.0,       "maximum": 5.0     },     "availability": {       "type": "array",       "items": {         "type": "string"       }     },     "promotion": {       "type": "object",       "properties": {         "discount": {           "type": "number",           "maximum": 50         }       }     }   },   "required": ["productName", "flavors", "weight", "price"] }

Что тут происходит:

  1. Название продукта (productName) — обязательное поле, должно быть строкой.

  2. Описание (description) — опциональное поле, тоже строка.

  3. Вкус (flavors) — массив строк, где минимум один элемент, обязателен для заполнения. Вкусы могут быть любыми, но хотя бы один должен быть указан.

  4. Вес упаковки (weight) — целое число, ограничено значениями от 50 до 10 000 грамм. Если кто-то случайно введёт вес пачки в миллиграммах, JSON Schema сразу это отловит.

  5. Цена (price) — обязателен, минимальное значение — 0 (потому что корм нельзя продавать за отрицательные деньги, хотя если бы это было возможно, все были бы рады).

  6. Срок годности (expirationDate) — строка в формате даты, очень важно следить за свежестью продукта.

  7. Рекомендованный возраст (recommendedAge) — целое число, потому что котики тоже стареют, и младенцы должны получать специальные корма, как и пенсионеры.

  8. Рейтинг корма (rating) — число с плавающей точкой от 1.0 до 5.0, потому что так принято в маркетинге, где всё должно быть по шкале.

  9. Наличие в магазинах (availability) — массив строк с названиями городов, где доступен продукт.

  10. Акции (promotion) — объект с полем «discount», где скидка не должна превышать 50%.

Теперь глянем как валидировать входящие данные для корма через Python. Для этого используем библиотеку jsonschema.

Пример данных для проверки:

import jsonschema from jsonschema import validate  # JSON Schema для данных корма schema = {     "$schema": "http://json-schema.org/draft-07/schema#",     "type": "object",     "properties": {         "productName": {             "type": "string"         },         "description": {             "type": "string"         },         "flavors": {             "type": "array",             "items": {                 "type": "string"             },             "minItems": 1         },         "weight": {             "type": "integer",             "minimum": 50,             "maximum": 10000         },         "price": {             "type": "number",             "minimum": 0         },         "expirationDate": {             "type": "string",             "format": "date"         },         "recommendedAge": {             "type": "integer",             "minimum": 1,             "maximum": 25         },         "rating": {             "type": "number",             "minimum": 1.0,             "maximum": 5.0         },         "availability": {             "type": "array",             "items": {                 "type": "string"             }         },         "promotion": {             "type": "object",             "properties": {                 "discount": {                     "type": "number",                     "maximum": 50                 }             }         }     },     "required": ["productName", "flavors", "weight", "price"] }  # Пример данных о корме data = {     "productName": "Kitty Feast Deluxe",     "description": "Лучший корм для твоего котика",     "flavors": ["курица", "говядина"],     "weight": 500,     "price": 15.99,     "expirationDate": "2024-12-31",     "recommendedAge": 3,     "rating": 4.7,     "availability": ["Москва", "Санкт-Петербург"],     "promotion": {         "discount": 20     } }  # Валидация данных try:     validate(instance=data, schema=schema)     print("Данные валидны") except jsonschema.exceptions.ValidationError as err:     print(f"Ошибка валидации: {err.message}")

Если всё верно, вывод программы скажет «Данные валидны«.

Вот так легко и просто можно контролировать входящие данные о продуктах.


Заключение

Будь то простая проверка типов данных или сложные условия — JSON Schema делает твою жизнь проще, а API — стабильнее.

Всегда помни — лучше потратить 10 минут на написание схемы, чем потом ночами разбираться, почему все полетело к чертям из-за какого-то лишнего пробела или неверного типа.

Всех заинтересованных приглашаю на бесплатные вебинары курса Системный аналитик. Advanced:

  • Эксклюзивные стратегии для системных аналитиков: как успешно пройти техническое собеседование. Зарегистрироваться

  • Как переехать с монолита на микросервис: подходы, проблемы и пути их решения. Зарегистрироваться


ссылка на оригинал статьи https://habr.com/ru/articles/851512/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *