HimeraSearchDB
Carding_EbayThief
triada
CrackerTuch
JustinSun

НОВОСТИ JSON Schema. Быть или не быть?

BDFpromo
Оффлайн

BDFpromo

.
.
Регистрация
23.09.18
Сообщения
12.347
Реакции
176
Репутация
0
Архитектура: искусство делать излишнее необходимым.

Фредерик Кислер​

Ни для кого давно уже не секрет, что для любого web-сервиса на протоколе SOAP с сообщениями в формате XML верным и проверенным временем решением является предварительная разработка (xsd-схемы), описывающей типы данных и структуру XML сообщений. При этом подходе у разработчиков существует явное преимущество: у них есть строгие стандартизированные правила по структуре сообщений, которые заданы в схеме, число правил конечно, и они позволяют автоматизировать проверку любого нового сообщения в формате XML.

Но также известно, что язык XML потеснился языком разметки JSON (JavaScript Object Notation) в виду его большей тяжеловесности, а также в виду распространения архитектурного стиля REST (REpresentational State Transfer) разработки программного обеспечения для распределенных систем. Хотя сам REST-стиль не требует использования JSON (он вообще, можно сказать, ничего не требует, а «рекомендует»), но как показывает практика, чаще при разработке REST API все-таки используется JSON для описания тела сообщений.

Так вот, практика разработки REST API с JSON–сообщениями прочно вошла в жизнь ИТ в России (и не только у нас), хотя опыт при этом по описанию структуры сообщений в виде XML Schema, значительно упростивший жизнь разработчиков web-служб в свое время, упорно игнорируется в случае c JSON–сообщениями. Но не всеми, что не может не радовать.

Когда разработчики, знакомые с XML Schema, столкнулись с необходимостью каждый раз заново изобретать велосипед с разбором документов и переизобретать логику валидации, сформировался аналогичный . Он доступен по адресу , а также ряд документов по истории изменений и примеров использования. И несмотря на то что он публикуется в статусе «draft», его давно уже поддерживают все популярные платформы разработки и библиотеки на разных языках.

Сама предоставляет меньше возможностей по структуризации сообщений, чем . То, что легко можно описать через XML Schema, не всегда будет тривиальной задачей повторить с помощью JSON Schema, если вообще это будет возможно. Но здесь же я бы данный факт стала рассматривать и как преимущество. Почему?

Известно, что чем проще и линейнее алгоритм работы системы, тем она и надежнее, что чем проще структура документа, тем легче он для восприятия и т.д.
Не могу удержаться, чтобы не процитировать: «Всё гениальное просто, и всё простое гениально». И если не удается с помощью схемы описать сложную структуру документа и множество допустимых вариантов, то, возможно, стоит посмотреть в сторону упрощения самой структуры и логики формирования этого документа?

Предисловие


Итак, о чем же эта статья?
Я бы хотела привлечь больше внимания к преимуществам описания передаваемых JSON сообщений схемой JSON Schema. Несмотря на то, что «на входе» разработка REST API без какой-либо JSON-схемы всегда проще и быстрее, но с ростом системы, ее отсутствие так или иначе приводит к удорожанию сопровождения и поддержки системы. Также любая предварительная проработка структуры сообщений способствует более качественной организации обмена сообщениями, без лишнего дублирования при обмене данными и общими правилами их обработки.

Также в целях распространения информации в русскоязычном сообществе о возможностях JSON Schema и правилах работы с ней я поделюсь своим некоторым опытом на конкретных примерах в рамках данной статьи.

Постановка задачи


Перед тем как приступить к изучению JSON и JSON Schema, опишу задачу, на которой мы будем рассматривать все примеры далее.
Рассмотрим управления в организации. Предполагаем, что справочную информацию по существующей ролевой модели мы должны будем передавать в зависимые системы в сообщениях в формате JSON посредством вызова REST-сервиса.

Описание задачи:
В организации есть сотрудники, им часто приходится работать одновременно в нескольких системах. При этом уровень доступа (полномочий) к тем или иным компонентам системы (ресурсам) для разных сотрудников в зависимости от их роли в организации может отличаться, и должен контролироваться при авторизации пользователя в системе.

Например, бухгалтер (роль) будет иметь доступ на чтение и редактирование (операции/полномочии) к расчетным листкам (ресурс) по заработной плате всех сотрудников, а у аналитика (роль), к примеру, будет доступ на чтение (операция/полномочии) только по своему расчетному листку (ресурс).

Необходимо спроектировать и описать ролевую модель управления в организации. Доступные роли, набор возможных полномочий и ресурсов в системе необходимо передавать другим системам по запросу.
tr1mrejzcrb_dksubi8edawdssm.png

Рисунок 1. Представление компонентов ролевой модели

Способы описания и реализации ролевой модели могут отличаться, но независимо от реализации чаще всего в ролевой модели в таком случае мы можем выделить следующие базовые составляющие:
  1. Роль (например, менеджер, бухгалтер и т.д.).
  2. Ресурс (например, документ, объект недвижимости и т.д.).
  3. Операция/полномочии (например, прочесть, распечатать, создать и т.д.).

При описании ролевого доступа (как один из возможных вариантов) прибегают к созданию доступа на основе выделенных сущностей, например:
Таблица 1. Дискретная матрица доступов.
Ресурс: documents Ресурс: objects

Роль: manager

read, print

read, create

Роль: accountant

read, create

read

Далее в статье мы ознакомимся сначала с теоретической составляющей текстового формата обмена данными JSON и правилами их структурирования с помощью JSON Schema, а в качестве примеров буду приводить в рамках нашей поставленной задачи.

JavaScript Object Notation (JSON)


JSON (англ. JavaScript Object Notation) — текстовый формат обмена данными, основанный на JavaScript.

Теория


Язык разметки JSON задает ограниченный набор типов данных. Для пары {“ключ”: значение} для «ключа» всегда используют тип string, для «значения» применимы типы: string, number, object (тип JSON), array, boolean (true или false) и null.

x6q_sgnunckncpntu5xyf7zeu1i.png

Рисунок 2. Типы данных JSON

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

  1. Данные записываются в виде пар {“ключ”: значение}.
  2. Данные разделяются запятыми.
  3. В фигурных скобках записываются объекты.
  4. В квадратных скобках записываются массивы.
  5. Наименования «ключей» регистрозависимы.

7rxlho08got9yund-ef8z-pqsn0.png

Рисунок 3. Синтаксис JSON

Практика


Рассмотрим пример справочника ролей, который мы будем передавать в сервисе:
8kvmtcqmy-gmt-d_aenedkkohwo.png

Рисунок 4. Описание справочника ролей в формате json

Из примера видно, что даже несмотря на столь небольшое число базовых типов, при их комбинации мы можем создавать более сложные структуры сообщений при необходимости. Здесь, в частности, я описываю справочник ролей через объект массивов, содержащих другие объекты (на рисунке 4 выделены двумя прямоугольниками).
В табличном виде с помощью средств визуализации json-сообщений справочник может быть представлен следующим образом:

yqc8rrdhcdlr3l4qstqrqicsrt4.png

Рисунок 5. Визуализации справочника ролей в формате JSON

Справочник, условно говоря, представляет собой 3 «таблицы» для задания ролей в группе администраторов, бухгалтеров и рабочих. Состав «атрибутов» может быть расширен, при необходимости.
Визуальное представление, на мой взгляд, упрощает восприятие текстового описания. Аналогичную структуру зададим и для двух других справочников. Приведу ниже пример только табличного представления для справочника полномочий (операций) и ресурсов.

iyvupbcqihvtdwyt4rtlyd0q4do.png

Рисунок 6. Визуализации справочника полномочий в формате JSON

xfulrsikpxf6wawcwtkww5phiew.png

Рисунок 7. Визуализации справочника ресурсов в формате JSON

Исходные сообщения в текстовом формате JSON для справочника , и можно скачать/просмотреть по .
Теперь перейдем к самому интересному: к изучению JSON Schema и созданию схемы под наши справочники!

JSON Schema


Теория


Поскольку схема json написана в формате JSON, она поддерживает все типы JSON плюс дополнение: тип integer, который является подтипом типа number. Сама схема является JSON-объектом и предназначена для описания данных в формате JSON. Ниже приводится схема типов данных, используемых при создании самой схемы:

b6ztbhcx0shqsj6psmuvm_8r2a0.png

Рисунок 8. Типы данных JSON Schema

Как видно из рисунка, для схемы используются все те же типы данных, а также все те же принципы синтаксиса, что и для обычного документа JSON, приведенные на рисунке 3.

Теперь рассмотрим самое важное — правила, используемые в схеме для задания ограничений и структурирования JSON-сообщений.

JSON Schema позволяет:
  1. Ограничить тип данных для элементов документа JSON.
  2. В зависимости от типа проверяемых данных, также могут быть применимы дополнительные правила — «keywords», начиная с корня схемы документа и спускаясь к их дочерним элементам.

Некоторые «ключевые слова» являются чисто описательными, как например: «title», «description» и др., которые просто описывают предназначение схемы. Другие предназначены для идентификации документа: «$schema». Это ключевое слово используется для указания желаемой версии схемы. Значение этого ключевого слова должно быть строкой, представляющей URI, например: "$schema": « ».

Здесь очень важно обратить внимание, что не все версии могут поддерживаться вашим инструментом работы со схемой. Но 4-й драфт поддерживают практически все. О последних изменениях (JSON Schema 2019-09 Release Notes) для разных версий можно познакомиться по ссылке .
Остальные ключевые слова используются непосредственно для проверки документа JSON. Их мы сейчас и рассмотрим.
Таблица 2. Анализ структуры JSON Schema. Ключевые слова и их примеры использования.
ТипKeyword(s) Пример/описание

«Keywords» для описания схемы

"$schema"


"$schema":

Используется для задания версии драфта схемы.

"$id"


"$id": " "

Используется для указания уникального идентификатора документа или его подсхем.

"title"
"description"
"examples"
"comment"


{
"title": "JSON schema for dictionary",
"description": "Справочники ролей",
"examples": ["user", "manager"],
"comment": "не поленитесь прочесть статью до конца))"
}


Общие «Validation keywords», независимые от типа данных элемента

"enum"


{"enum": [ "administrator", "superuser" ]}

Выполняется проверка на совпадение с хотя бы 1 значением.

"const"


{"const": "user" }

Выполняется проверка на точное соответствие заданному значению.

"type"


{"type": ["number", "string", "null", "boolean"]}
{"type": "array"}

Указывает тип данных, который будет использоваться схемой. Это ключевое слово не является обязательным, и значением ключевого слова может быть строка, представляющая допустимый тип данных, или массив строк, представляющих допустимые типы данных.

Keywords, зависимые от того типа данных, с которым они используются

"type": "string"

minLength
maxLength
pattern
contentEncoding
contentMediaType


{
"type": "string",
"minLength": 3,
"maxLength": 10,
"pattern": "^test\\/[a-z-]+$",
"contentEncoding": "base64",
"contentMediaType": "application/json"
}

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

"type": "number" или "type": "integer"

minimum
exclusiveMinimum
maximum
exclusiveMaximum
multipleOf


{
"type": "number",
"minimum": 1.5,
"exclusiveMinimum": true,
"maximum": 12.3,
"exclusiveMaximum": true,
"multipleOf": 0.5
}

Выполняется проверка на соответствие числового выражения заданным параметрам.

"type": "object"

properties
required
dependencies
minProperties
maxProperties
propertyNames
patternProperties
additionalProperties



"employees": {
"description": "Сотрудники",
"type": "array",
"uniqueItems": true,
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["employee"],
"enumNames": ["Сотрудник"]
},
"enabled": {
"type": "boolean",
"default": true
}
},
"additionalProperties": false
}
}

Объект проверяется как по наименованию ключа, так и по ограничениям на значение (в примере приведены не все ключевые слова).

"type": "array"

minItems
maxItems
uniqueItems
contains
items
additionalItems



"employees": {
"description": "Сотрудники",
"type": "array",
"uniqueItems": true,
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": ["employee"],
"enumNames": ["Сотрудник"]
},
"enabled": {
"type": "boolean",
"default": true
}
},
"additionalProperties": false
}
}

Массив проверяется как по наименованию ключа, так и по ограничениям на значение (в примере приведены не все ключевые слова).

"type": "boolean"
Ключевые слова не используются


{"type": "boolean"}

Тип boolean используется для проверки только логических значений (true или false).

"type": "null"
Ключевые слова не используются


{"type": "null"}

Тип null используется для проверки «отсутствия» значения.

"type": "____"
"format": "____"


{
"type": "string",
"format": "date"
}

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

"type": "____"
"default": "____"


{
"enabled": {
"type": "boolean",
"default": true
}

Значение по умолчанию для элемента.
Это ключевое слово не является обязательным, и значение этого ключевого слова может быть любым.

Ключевые слова для наложения условий на проверки

"not"
"if-then-else"


{
"not": {
"type": "string"
}
}

Ключевые слова поддерживаются любым типом, являются необязательными.

Ключевые слова, объединяющие проверки для разных частей схемы

"anyOf"
"oneOf"
"allOf"


{
"type": "string",
"anyOf": [
{"const": "user"},
{"const": "manager" }
]
}

Ключевые слова поддерживаются любым типом, являются необязательными.

Ключевые слова, обеспечивающие ссылочность и связность схем

"$id" и "$ref"

Схема RolesDictionaryDef.json:

{
"$id":
}

Ссылаемся на схему 1 с помощью $ref (приведена часть схемы со ссылкой):


"items": {
"type": "object",
"minLength": 1,
"properties": {
"name": {
"description": "Наименование группы словарей",
"type": "string"
},
"description": {
"description": "Описание словарей",
"type": "string"
},
"dictionaryGroup": {
"$ref": "RolesDictionaryDef.json#/definitions/roles"
}
},
"additionalProperties": false
}

Мы рассматриваем 2 разных схемы JSON. Чтобы повторно использовать схему, мы делаем ссылку на нее с помощью ключевого слова $ref в другой схеме.

"$ref" и "definitions"

Через "$ref": "#/definitions/roles" ссылаемся на часть текущего документа, описанного после ключа «definitions»:

{
"$schema": " ",
"description": "Справочники ролей",
"type": "object",
"properties": {
"dictionaries": {
"description": "Справочники",
"type": "array",
"maxItems": 1,
"items": {
"type": "object",
"minLength": 1,
"properties": {
"name": {
"description": "Наименование словаря",
"type": "string",
"enum": [
"Roles Dictionary"
]
}
"dictionaryGroups": {
"$ref": "#/definitions/roles",
"description": "Справочные данные"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false,
"definitions": {
"roles": {
"description": "Роли",
"type": "object",
"properties": {
"administrators": {
"description": "Администраторы",
"type": "array",
"uniqueItems": true,
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"enum": [
"administrator",
"superuser"
],
"enumNames": [
"Администратор",
"Пользователь-админ с ограниченным доступом"
]
},
"enabled": {
"type": "boolean",
"default": true
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}
},
"$id": " "
}

Для ссылок на проверки, относящихся только к текущему документу схемы. Реализуется с помощью ключевого слова $ref и ключевого слова definitions.

"$ref"
Абсолютные и относительные указатели.


"$ref": " #/definitions/roles"
"$ref": "RolesDictionaryDef.json#/definitions/roles"

Абсолютный указатель используется для поиска, начиная с корня документа.
Относительный используется для поиска значений, начиная с текущего местоположения.


Мы рассмотрели ключевые слова схемы JSON, позволяющие описать нам будущую структуру наших сообщений в формате JSON.


Практика


При рассмотрении примеров завершенных JSON-схем мы поступим аналогично примерам работы с самими сообщениями в формате JSON. Т.е. мы будем использовать визуальное представление в древовидном и табличном виде для наших схем справочников ролей, ресурсов и полномочий (операций), а с текстом схем предлагаю ознакомиться заинтересовавшихся читателей самостоятельно .

Ниже приводится схема для справочника ролей.
ni-j1jbw9jt6fu4dojo8xrpquu4.png

Рисунок 9. Пример JSON Schema для справочника ролей

Как мы видим на рисунке, схема представляет собой JSON-объект и описывает наше сообщение для передачи справочника ролей в формате JSON, которое приводилось на рисунке 4. На текущем примере представлено как с помощью JSON схемы может быть описан объект массивов, состоящий из объектов.

Схемы двух других справочников (полномочий и ресурсов) по своей структуре идентичны со схемой для справочника ролей, поэтому не буду их здесь приводить, а приведу схему, объединяющую в себе все 3 справочника.
К сожалению, схема всего справочника при раскрытии не поместится на экране, поэтому рассмотрим ее часть.

cvvmhsq_zvedimld_f5ibaw-ubk.png

Рисунок 10. Пример JSON Schema справочника, объединяющего в себе справочник ролей, полномочий и ресурсов

На рисунке мы видим, что часть объектов массива справочников подключена с использованием ключевого слова «anyOf».
Также, возможно, нагляднее будет табличное представление справочника.
Рассмотрим еще одну важную особенность нашей схемы:

zrvjdcwdpfqk_oen45vhoh8ilnq.png

Рисунок 11. Пример JSON Schema справочника, объединяющего в себе справочник ролей, полномочий и ресурсов в табличном представлении

Из рисунка мы видим, что объединяющий справочник не дублирует в себе код из ранее разработанных справочников ролей, полномочий и ресурсов, а использует ключевое слово "$ref".

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

На этом мой обзор JSON и JSON Schema я завершаю. Надеюсь, что приведенный здесь материал и рассмотренные примеры, окажутся полезными при изучении возможностей JSON Sсhema.

Вместо заключения


Думаю, пора подводить итоги.
Так что же применение JSON Schema в итоге нам может дать?

  1. Может упростить жизнь разработчикам и улучшить код по валидации JSON -сообщений.
    Иными словами, это упрощение поддержки и интеграции ПО.
  2. Позволит разрабатывать сервисы, прорабатывая форматы и состав данных с «заделом» на будущее развитие системы.
  3. Применить проверку документов в документо-ориентированных, объектно-ориентированных БД.
  4. JSON-Schema может помочь сэкономить время на тестировании и документировании API.
  5. Упрощение поддержки обратной совместимости API.
  6. Позволит управлять потоками данных.


Каждый из нас сам решает, «Быть или не быть JSON Schema» в наших IT -проектах. Выше я привела список того, что я считаю ключевым преимуществом применения схем, и ради чего уже стоит задуматься о ее применении в проектах.

Возможно, читатели захотят помочь мне продолжить этот список?
Я буду признательна :)

Также приведу список ссылок, на мой взгляд, полезных для работы с JSON и JSON Schema,
  1. .
  2. .
  3. .
  4. .
  5. .


И ссылку на git-репозиторий, где можно ознакомиться с исходными файлами, приводимыми для ознакомления в данной статье: .

Системный архитектор,
© Ирина Блажина
 
Сверху Снизу