Понимание схемы JSON (JSON Schema), часть 2

09.11.21

Интеграция - WEB-интеграция

Перевод книги https://json-schema.org/understanding-json-schema/

Продолжение публикации //infostart.ru/1c/articles/1543922/

Описание схемы JSON

Общие ключевые слова

В этой главе перечислены некоторые различные свойства, доступные для всех типов JSON.

 

Аннотации

Схема JSON включает несколько ключевых слов, которые не используются строго для проверки, но используются для описания частей схемы. Ни одно из этих ключевых слов "annotation" не обязательны, но они рекомендуются для хорошей практики и могут сделать вашу схему "самодокументируемой".

Ключевые слова "title" и "description" должны быть строками. "title" предпочтительно будет коротким, в то время как "description" предоставит более подробное объяснение назначения данных, описываемых схемой.

Ключевое слово "default" задает значение по умолчанию. Это значение не используется для заполнения пропущенных значений в процессе проверки. Средства, не требующие проверки, такие как генераторы документации или генераторы форм, могут использовать это значение, чтобы давать пользователям подсказки о том, как использовать значение. Однако "default" обычно используется для выражения того, что если значение отсутствует, то значение семантически такое же, как если бы значение присутствовало вместе со значением "default". Значение "default" должно соответствовать схеме, в которой оно находится, но это не обязательно.

Ключевое слово "examples" - это место для предоставления массива примеров, которые проверяются на соответствие схеме. Это не используется для проверки, но может помочь объяснить читателю эффект и назначение схемы. Каждая запись должна проверяться на соответствие схеме, в которой она находится, но это не обязательно. Нет необходимости дублировать значение "default" в массиве "examples", так как "default" будет рассматриваться как другой пример.

Логические ключевые слова "readOnly" и "writeOnly" обычно используются в контексте API. "readOnly" указывает, что значение не должно изменяться. Его можно использовать для указания на то, что запрос PUT, изменяющий значение, приведет к ответу "400 Bad Request". "writeOnly" указывает, что значение может быть установлено, но останется скрытым. Он может использоваться для указания на то, что вы можете установить значение с помощью запроса PUT, но оно не будет включено при получении этой записи с помощью запроса GET.

Ключевое слово "deprecated" - это логическое значение, указывающее, что значение экземпляра, к которому применяется ключевое слово, не должно использоваться и может быть удалено в будущем.

{
  "title": "Match anything",
  "description": "This is a schema that matches anything.",
  "default": "Default value",
  "examples": [
    "Anything",
    4035
  ],
  "deprecated": true,
  "readOnly": true,
  "writeOnly": false
}

 

Комментарии

Ключевое слово $comment предназначено исключительно для добавления комментариев в схему. Его значение всегда должно быть строкой. В отличие от аннотаций "title", "description" и "examples", реализациям схемы JSON не разрешается придавать ему какое-либо значение или поведение, и он может быть удален в любое время. Поэтому он полезен для того, чтобы оставлять заметки будущим редакторам схемы JSON, но не должен использоваться для общения с пользователями схемы.

 

Перечисления

Ключевое слово "enum" используется для ограничения значения фиксированным набором значений. Оно должно быть массивом по крайней мере с одним элементом, где каждый элемент уникален.

{
  "enum": ["red", "amber", "green"]
}
"red"
"blue"

Вы можете использовать "enum" даже без типа, чтобы принимать значения разных типов. Давайте расширим пример, чтобы использовать "null" его для обозначения "выключено", а также добавим 42, просто для удовольствия.

{
  "enum": ["red", "amber", "green", null, 42]
}
"red"
null
42
0

 

Константы

Ключевое слово "const" используется для ограничения значения одним значением.

Например, если вы поддерживаете экспорта доставку только в Соединенные Штаты:

{
  "properties": {
    "country": {
      "const": "United States of America"
    }
  }
}
{ "country": "United States of America" }
{ "country": "Canada" }

 

Медиа: данные, не относящиеся к JSON в виде строки

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

 

Тип медиа контента

Ключевое слово "contentMediaType" определяет тип MIME содержимого строки, как описано в RFC 2046. Существует список типов MIME, официально зарегистрированных IANA, но набор поддерживаемых типов будет зависеть от приложения и операционной системы. Mozilla Developer Network также поддерживает более короткий список типов MIME, которые важны для Интернета

 

Кодирование содержимого

Ключевое слово "contentEncoding" указывает кодировку, используемую для хранения содержимого, как указано в RFC 2054, часть 6.1 и RFC 4648.

Допустимыми значениями являются . Если не указано, кодировка совпадает с кодировкой содержащего JSON-документа.

Не вдаваясь в детали низкого уровня каждой из этих кодировок "7bit", "8bit", "binary", "quoted-printable", "base16", "base32", и "base64", на самом деле есть только два варианта, полезных для современного использования:

  • Если содержимое закодировано в той же кодировке, что и заключающий документ JSON (который для практических целей почти всегда является UTF-8), оставьте "contentEncoding" неопределенным и включите содержимое в строку как есть. Это включает типы контента на основе текста, такие как "text/html" или "application/xml".
  • Если содержимое представляет собой двоичные данные, установите "contentEncoding" значение "base64" и закодируйте содержимое с помощью Base64. Это будет включать в себя множество типов изображений, таких как "image/png" или типов аудио, таких как "audio/mpeg".

 

Примеры

Следующая схема указывает, что строка содержит HTML-документ, закодированный с использованием той же кодировки, что и окружающий документ:

{
  "type": "string",
  "contentMediaType": "text/html"
}
"<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\"><head></head></html>"

Следующая схема указывает, что строка содержит изображение PNG, закодированное с использованием Base64:

{
  "type": "string",
  "contentEncoding": "base64",
  "contentMediaType": "image/png"
}
"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA..."

 

Состав схемы

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

Эти ключевые слова соответствуют хорошо известным понятиям булевой алгебры, таким как И, ИЛИ, Исключающее ИЛИ и НЕ. Вы часто можете использовать эти ключевые слова для выражения сложных ограничений, которые иначе не могут быть выражены с помощью стандартных ключевых слов схемы JSON.

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

  • allOf: (И) Должно быть действительным для всех подсхем
  • anyOf: (ИЛИ) Должно быть действительным в отношении любой из подсхем
  • oneOf: (Исключающее ИЛИ) Должно быть действительным только для одной из подсхем

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

Кроме того, существует:

  • not: (НЕ) Не должно быть действительным для данной схемы

 

allOf (И)

Для проверки на "allOf" приведенные данные должны быть действительны для всех заданных подсхем.

{
  "allOf": [
    { "type": "string" },
    { "maxLength": 5 }
  ]
}
"short"
"too long"

Примечание: allOf нельзя использовать для "расширения" схемы, чтобы добавить к ней больше деталей в смысле объектно-ориентированного наследования. Экземпляры должны независимо быть действительны для "всех" схем в allOf. Дополнительную информацию смотрите в разделе о независимости подсхем.

 

anyOf: (ИЛИ)

Для проверки "anyOf" данные должны быть действительны в отношении любой (одной или нескольких) из указанных подсхем.

{
  "anyOf": [
    { "type": "string", "maxLength": 5 },
    { "type": "number", "minimum": 0 }
  ]
}
"short"
"too long"
12
-5

 

oneOf (Исключающее ИЛИ)

Для проверки "anyOf" данные должны быть действительны только в отношении одной из заданных подсхем.

{
  "oneOf": [
    { "type": "number", "multipleOf": 5 },
    { "type": "number", "multipleOf": 3 }
  ]
}
10
9

Не кратно ни 5, ни 3.

2

Отклоняется число, кратное как 5, так и 3.

15

 

not (НЕ)

Ключевое слово "not" объявляет, что экземпляр валиден, если он не соответствует данной подсхеме.

Например, следующая схема проверяет все, что не является строкой:

{ "not": { "type": "string" } }
42
{ "key": "value" }
"I am a string"

 

Свойства композиции схемы

Независимость подсхемы

Важно отметить, что схемы, перечисленные в массиве allOf, anyOf или oneOf, ничего не знают друг о друге. Например, предположим, что у вас была схема для адреса в "$defs" разделе и вы хотите "расширить" ее, включив в нее тип адреса:

{
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city": { "type": "string" },
        "state": { "type": "string" }
      },
      "required": ["street_address", "city", "state"]
    }
  },

  "allOf": [
    { "$ref": "#/$defs/address" },
    {
      "properties": {
        "type": { "enum": [ "residential", "business" ] }
      }
    }
  ]
}
{
   "street_address": "1600 Pennsylvania Avenue NW",
   "city": "Washington",
   "state": "DC",
   "type": "business"
}

Это работает, но что, если бы мы хотели ограничить схему, чтобы не было разрешено никаких дополнительных свойств? Можно попробовать добавить выделенную строку ниже ("additionalProperties": false):

{
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city": { "type": "string" },
        "state": { "type": "string" }
      },
      "required": ["street_address", "city", "state"]
    }
  },

  "allOf": [
    { "$ref": "#/$defs/address" },
    {
      "properties": {
        "type": { "enum": [ "residential", "business" ] }
      }
    }
  ],

  "additionalProperties": false
}
{
   "street_address": "1600 Pennsylvania Avenue NW",
   "city": "Washington",
   "state": "DC",
   "type": "business"
}

К сожалению, теперь схема отвергнет все. Это происходит потому что "additionalProperties" ничего не знает о свойствах, объявленных в подсхемах внутри массива allOf.

Для многих это один из самых больших сюрпризов операций объединения в схеме JSON: она не ведет себя как наследование в объектно-ориентированном языке. Есть некоторые предложения по решению этой проблемы в следующей версии спецификации схемы JSON.

 

Нелогичные Схемы

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

{
  "allOf": [
    { "type": "string" },
    { "type": "number" }
  ]
}
"No way"
-1

 

Оптимизация схем

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

{
  "oneOf": [
    { "type": "number", "multipleOf": 5 },
    { "type": "number", "multipleOf": 3 }
  ]
}
{
   "type": "number",
   "oneOf": [
     { "multipleOf": 5 },
     { "multipleOf": 3 }
   ]
 }

 

Зависимые требования

Ключевое слово "dependentRequired" условно требует, чтобы определенные свойства присутствовали, если данное свойство присутствует в объекте. Например, предположим, что у нас есть схема, представляющая клиента. Если у вас есть номер их кредитной карты, вы также хотите убедиться, что у вас есть платежный адрес. Если у вас нет номера их кредитной карты, платежный адрес не потребуется. Мы представляем эту зависимость одного свойства от другого с помощью ключевого слова "dependentRequired". Значение ключевого слова "dependentRequired" - это объект. Каждая запись в объекте сопоставляется с именем свойства, p, к массиву строк со списком свойств, которые требуются, если присутствует p.

В следующем примере всякий раз, когда имеется свойство "credit_card", свойство "billing_address" также должно присутствовать:

{
  "type": "object",

  "properties": {
    "name": { "type": "string" },
    "credit_card": { "type": "number" },
    "billing_address": { "type": "string" }
  },

  "required": ["name"],

  "dependentRequired": {
    "credit_card": ["billing_address"]
  }
}
{
  "name": "John Doe",
  "credit_card": 5555555555555555,
  "billing_address": "555 Debtor's Lane"
}

В этом экземпляре есть "credit_card", но в нем отсутствует "billing_address"

{
  "name": "John Doe",
  "credit_card": 5555555555555555
}

Это нормально, так как у нас нет ни "credit_card", ни "billing_address"

{
  "name": "John Doe"
}

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

{
  "name": "John Doe",
  "billing_address": "555 Debtor's Lane"
}

Чтобы исправить последнюю проблему выше (то, что зависимости не являются двунаправленными), вы, конечно, можете явно определить двунаправленные зависимости:

{
  "type": "object",

  "properties": {
    "name": { "type": "string" },
    "credit_card": { "type": "number" },
    "billing_address": { "type": "string" }
  },

  "required": ["name"],

  "dependentRequired": {
    "credit_card": ["billing_address"],
    "billing_address": ["credit_card"]
  }
}

В этом экземпляре есть "credit_card", но в нем отсутствует "billing_address"

{
  "name": "John Doe",
  "credit_card": 5555555555555555
}

У этого есть "billing_address", но отсутствует "credit_card"

{
  "name": "John Doe",
  "billing_address": "555 Debtor's Lane"
}

 

Зависимые схемы

Ключевое слово "dependenciesSchemas" условно применяет подсхему, когда присутствует данное свойство. Эта схема применяется таким же образом, как и все схемы. Ничто не объединяется и не расширяется. Обе схемы применяются независимо.

Например, вот еще один способ написать вышесказанное:

{
  "type": "object",

  "properties": {
    "name": { "type": "string" },
    "credit_card": { "type": "number" }
  },

  "required": ["name"],

  "dependentSchemas": {
    "credit_card": {
      "properties": {
        "billing_address": { "type": "string" }
      },
      "required": ["billing_address"]
    }
  }
}
{
  "name": "John Doe",
  "credit_card": 5555555555555555,
  "billing_address": "555 Debtor's Lane"
}

В этом экземпляре есть "credit_card", но в нем отсутствует "billing_address"

{
  "name": "John Doe",
  "credit_card": 5555555555555555
}

У этого есть "billing_address" но отсутствует "credit_card". Это проходит, потому что здесь "billing_address" выглядит как дополнительное свойство:

{
  "name": "John Doe",
  "billing_address": "555 Debtor's Lane"
}

 

Если-Тогда-Иначе

Ключевые слова "if" "then" "else" позволяют применять подсхему, основанную на результатах другой схемы, аналогично конструкциям if/then/else которые вы, вероятно, видели в традиционных языках программирования.

Если "if" допустимо, "then" также должно быть допустимым (и "else" игнорируется). Если "if" недопустимо, "else" также должно быть допустимым (и  "then" игнорируется).

Если "then" или "else" не определено, "if" ведет себя так, как если бы они имели значение "Истина".

Если "then" и/или "else" отображаются в схеме без "if", "then" и "else" игнорируются.

Мы можем представить это в виде таблицы истинности, показывающей комбинации, когда "if", "then", и "else" являются допустимыми, и результирующую действительность всей схемы:

if then else Результат
T
T
n/a
T
T F n/a F
F n/a T T
F n/a F F
n/a n/a n/a T

 

Например, предположим, вы хотели написать схему для обработки адресов в Соединенных Штатах и Канаде. Эти страны имеют разные форматы почтовых индексов, и мы хотим выбрать, какой формат для проверки в зависимости от страны. Если адрес находится в Соединенных Штатах, поле "postal_code" представляет собой "почтовый индекс": пять цифровых цифр, за которыми следует необязательный четырехзначный суффикс. Если адрес находится в Канаде, поле "postal_code" представляет собой шестизначную буквенно-цифровую строку, в которой чередуются буквы и цифры.

{
  "type": "object",
  "properties": {
    "street_address": {
      "type": "string"
    },
    "country": {
      "default": "United States of America",
      "enum": ["United States of America", "Canada"]
    }
  },
  "if": {
    "properties": { "country": { "const": "United States of America" } }
  },
  "then": {
    "properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
  },
  "else": {
    "properties": { "postal_code": { "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" } }
  }
}
{
  "street_address": "1600 Pennsylvania Avenue NW",
  "country": "United States of America",
  "postal_code": "20500"
}
{
  "street_address": "1600 Pennsylvania Avenue NW",
  "postal_code": "20500"
}
{
  "street_address": "24 Sussex Drive",
  "country": "Canada",
  "postal_code": "K1M 1M4"
}
{
  "street_address": "24 Sussex Drive",
  "country": "Canada",
  "postal_code": "10000"
}
{
  "street_address": "1600 Pennsylvania Avenue NW",
  "postal_code": "K1M 1M4"
}

Примечание: В этом примере "country" не является обязательным свойством. Поскольку схема "if" также не требует свойства "country", она пройдет, и будет применена схема "then". Поэтому, если свойство "country" не определено, поведение по умолчанию заключается в проверке "postal_code" как почтового индекса США. Ключевое слово "default” не имеет эффекта, но его полезно включить для читателей схемы, чтобы легче распознавать поведение по умолчанию.

К сожалению, описанный выше подход не распространяется более чем на две страны. Однако вы можете обернуть пары "if" и "then" внутри "allOf", чтобы создать что-то, что будет масштабироваться. В этом примере мы будем использовать почтовые индексы США и Канады, но также добавим почтовые индексы Нидерландов, состоящие из 4 цифр, за которыми следуют две буквы. Читателю остается в качестве упражнения распространить это на остальные почтовые индексы мира.

{
  "type": "object",
  "properties": {
    "street_address": {
      "type": "string"
    },
    "country": {
      "default": "United States of America",
      "enum": ["United States of America", "Canada", "Netherlands"]
    }
  },
  "allOf": [
    {
      "if": {
        "properties": { "country": { "const": "United States of America" } }
      },
      "then": {
        "properties": { "postal_code": { "pattern": "[0-9]{5}(-[0-9]{4})?" } }
      }
    },
    {
      "if": {
        "properties": { "country": { "const": "Canada" } },
        "required": ["country"]
      },
      "then": {
        "properties": { "postal_code": { "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]" } }
      }
    },
    {
      "if": {
        "properties": { "country": { "const": "Netherlands" } },
        "required": ["country"]
      },
      "then": {
        "properties": { "postal_code": { "pattern": "[0-9]{4} [A-Z]{2}" } }
      }
    }
  ]
}
{
  "street_address": "1600 Pennsylvania Avenue NW",
  "country": "United States of America",
  "postal_code": "20500"
}
{
  "street_address": "1600 Pennsylvania Avenue NW",
  "postal_code": "20500"
}
{
  "street_address": "24 Sussex Drive",
  "country": "Canada",
  "postal_code": "K1M 1M4"
}
{
  "street_address": "Adriaan Goekooplaan",
  "country": "Netherlands",
  "postal_code": "2517 JX"
}
{
  "street_address": "24 Sussex Drive",
  "country": "Canada",
  "postal_code": "10000"
}
{
  "street_address": "1600 Pennsylvania Avenue NW",
  "postal_code": "K1M 1M4"
}

Примечание: Ключевое слово "required" необходимо в схемах "if", иначе все они будут применяться, если "country" не определена. Если оставить "required" в схеме "Соединенные Штаты Америки" "if", то она фактически будет использоваться по умолчанию, если "country" не определена.

Примечание: Даже если "country" была обязательным полем, все равно рекомендуется иметь ключевое слово "required" в каждой схеме "if". Результат проверки будет таким же, потому что "required" завершится ошибкой, но не включение его добавит шума в результаты ошибок, поскольку он проверит "postal_code" по всем трем схемам "then", что приведет к неуместным ошибкам.

 

Импликация

Wikipedia: бинарная логическая связка, по своему применению приближенная к союзам «если…, то…»

До версии 7 вы можете выразить условие "если-то", используя ключевые слова композиции схемы и концепцию булевой алгебры, называемую "импликация". "A -> B" (произносится, A подразумевает B) означает, что если A истинно, то B также должно быть истинным. Это может быть выражено как "!A || B" то, что может быть выражено в виде схемы JSON.

{
  "type": "object",
  "properties": {
    "restaurantType": { "enum": ["fast-food", "sit-down"] },
    "total": { "type": "number" },
    "tip": { "type": "number" }
  },
  "anyOf": [
    {
      "not": {
        "properties": { "restaurantType": { "const": "sit-down" } },
        "required": ["restaurantType"]
      }
    },
    { "required": ["tip"] }
  ]
}
{
  "restaurantType": "sit-down",
  "total": 16.99,
  "tip": 3.4
}
{
  "restaurantType": "sit-down",
  "total": 16.99
}
{
  "restaurantType": "fast-food",
  "total": 6.99
}
{ "total": 5.25 }

 

Варианты импликации могут использоваться для выражения тех же самых вещей, которые вы можете выразить с помощью ключевых слов "if/then/else". ""if/then" может быть выражено как "A -> B", "if/else" может быть выражено как "!A -> B" и "if/then/else" может быть выражено как "A -> B AND !A -> C".

Примечание: Поскольку этот шаблон не очень интуитивно понятен, рекомендуется поместить ваши условные "$defs" обозначения с описательным именем и используя "$ref" включить его в вашу схему

"allOf": [{ "$ref": "#/$defs/sit-down-restaurant-implies-tip-is-required" }]

 

Объявления диалекта

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

 

$schema

Ключевое слово "$schema" используется для объявления того, для какого диалекта схемы JSON была написана схема. Значение ключевого слова "$schema" также является идентификатором схемы, который можно использовать для проверки правильности схемы в соответствии с определениями диалекта "$schema". Схема, описывающая другую схему, называется "мета-схемой".

"$schema" применяется ко всему документу и должен находиться на корневом уровне. Это не относится к документам с внешними ссылками ("$ref", "$dynamicRef"). Эти схемы должны быть объявлены своими собственными "$schema".

Если "$schema" не используется, реализация может позволить вам указать значение извне или может сделать предположения о том, какая версия спецификации должна использоваться для оценки схемы. Рекомендуется, чтобы все схемы JSON имели ключевое слово "$schema" для передачи читателям и инструментам, для которых предназначена версия спецификации. Поэтому в большинстве случаев вы захотите, чтобы это было в корне вашей схемы:

"$schema": "https://json-schema.org/draft/2020-12/schema"

 

Словари

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

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

Подробнее про использование https://github.com/json-schema-org/json-schema-vocabularies

 

Инструкции

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

Ключевые слова метаданных являются наиболее совместимыми, поскольку они не влияют на проверку. Например, вы можете добавить ключевое слово "units". Это всегда будет работать так, как и ожидалось, с совместимым валидатором.

{
  "type": "number",
  "units": "kg"
}
42
"42"

Следующими лучшими кандидатами для пользовательских ключевых слов являются ключевые слова, которые не применяют другие схемы и не изменяют поведение существующих ключевых слов. Ключевое слово "isEven" является примером. В контекстах, где некоторая проверка лучше, чем отсутствие проверки, например, проверка HTML-формы в браузере, эта схема будет работать так хорошо, как можно ожидать. По-прежнему потребуется полная проверка, и для этого следует использовать валидатор, который понимает пользовательское ключевое слово.

{
  "type": "integer",
  "isEven": true
}
2

Это проходит, потому что валидатор не понимает "isEven"

3

Схема не полностью нарушена, потому что она не понимает "isEven"

"3"

Наименее совместимый тип пользовательского ключевого слова-это тот, который применяет другие схемы или изменяет поведение существующих ключевых слов. Примером может быть что-то вроде "requiredProperties" того, что объявляет свойства и делает их обязательными. Этот пример показывает, как схема становится почти полностью бесполезной при оценке с помощью валидатора, который не понимает пользовательское ключевое слово. Это не обязательно означает, что "requiredProperties" это плохая идея для ключевого слова, это просто неправильный выбор, если схему, возможно, потребуется использовать в контексте, который не понимает пользовательские ключевые слова.

{
  "type": "object",
  "requiredProperties": {
    "foo": { "type": "string" }
  }
}
{ "foo": "bar" }

Это проходит, потому что "requiredProperties" не понятен валидатору

{}

Это проходит, потому что "requiredProperties" не понятен валидатору

{ "foo": 42 }

 

Структурирование комплексной схемы

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

 

Идентификация схемы

Как и любой другой код, схемы легче поддерживать, если их можно разбить на логические блоки, которые при необходимости ссылаются друг на друга. Чтобы ссылаться на схему, нам нужен способ ее идентификации. Документы схемы идентифицируются с помощью неродственных URI.

Документы схемы не обязательно должны иметь идентификатор, но он вам понадобится, если вы хотите ссылаться на одну схему из другой. В этой документации мы будем ссылаться на схемы без идентификатора как на "анонимные схемы".

В следующих разделах мы увидим, как определяется "идентификатор" схемы.

Примечание: Терминология URI иногда может быть неинтуитивной. В этом документе используются следующие определения.

  • [1] URI или не относительный URI: полный URI, содержащий схему (https). Он может содержать фрагмент URI (#foo). Иногда в этом документе будет использоваться "не относительный URI", чтобы было более ясно, что относительные URI недопустимы.
  • [2] относительная ссылка: частичный URI, который не содержит схемы (https). Он может содержать фрагмент (#foo).
  • [3] URI-ссылка: Относительная ссылка или не относительный URI. Он может содержать фрагмент URI (#foo).
  • [4] абсолютный URI Полный URI, содержащий схему (https), но не фрагмент URI (#foo).

Примечание: Несмотря на то, что схемы идентифицируются URI, эти идентификаторы не обязательно могут быть адресованы по сети. Это просто идентификаторы. Как правило, реализации не выполняют HTTP-запросы (https://) или чтение из файловой системы (file://) для извлечения схем. Вместо этого они предоставляют способ загрузки схем во внутреннюю базу данных схем. Когда на схему ссылается идентификатор URI, схема извлекается из внутренней базы данных схем.

 

Базовый URI

Использование не относительных URI может быть громоздким, поэтому любые URI, используемые в схеме JSON, могут быть ссылками на URI, которые разрешаются в соответствии с базовым URI схемы, что приводит к не относительному URI. В этом разделе описывается, как определяется базовый URI схемы.

Примечание: Определение базового URI и относительное разрешение ссылки определяются RFC-3986. Если вы знакомы с тем, как это работает в HTML, этот раздел должен показаться вам очень знакомым.

 

URI для поиска

URI, используемый для извлечения схемы, известен как "URI извлечения". Часто можно передать анонимную схему реализации, и в этом случае у этой схемы не будет URI для поиска.

Предположим, что на схему ссылаются с помощью URI "https://example.com/schemas/address" и извлекается следующая схема.

{
  "type": "object",
  "properties": {
    "street_address": { "type": "string" },
    "city": { "type": "string" },
    "state": { "type": "string" }
  },
  "required": ["street_address", "city", "state"]
}

Базовый URI для этой схемы совпадает с URI поиска, "https://example.com/schemas/address".

 

$id

Вы можете задать базовый URI, используя "$id" ключевое слово в корне схемы. Значение "$id" - это ссылка на URI без фрагмента, которая сопоставляется с URI поиска. Полученный URI является базовым URI для схемы.

Примечание: Это аналогично "<base>" тегу в HTML.

Примечание: Когда  "$id" ключевое слово появляется в подсхеме, оно означает что-то немного другое. Дополнительные сведения см. в разделе Комплектация.

Давайте предположим, что URI "https://example.com/schema/address" и "https://example.com/schema/billing-address" оба идентифицируют следующую схему.

{
  "$id": "/schemas/address",

  "type": "object",
  "properties": {
    "street_address": { "type": "string" },
    "city": { "type": "string" },
    "state": { "type": "string" }
  },
  "required": ["street_address", "city", "state"]
}

Независимо от того, какой из двух URI используется для извлечения этой схемы , будет использоваться базовый URI "https://example.com/schemas/address", который является результатом "$id" разрешения ссылки на URI в соответствии с URI для извлечения.

Однако использование относительной ссылки при настройке базового URI может быть проблематичным. Например, мы не могли использовать эту схему в качестве анонимной схемы, потому что не было бы URI для поиска, и вы не можете разрешить относительную ссылку ни на что. По этой и другим причинам рекомендуется всегда использовать абсолютный URI при объявлении базового URI с "$id" помощью .

Базовый URI следующей схемы всегда будет "https://example.com/schemas/address" независимо от того, каким был URI для поиска или использовался ли он в качестве анонимной схемы.

{
  "$id": "https://example.com/schemas/address",

  "type": "object",
  "properties": {
    "street_address": { "type": "string" },
    "city": { "type": "string" },
    "state": { "type": "string" }
  },
  "required": ["street_address", "city", "state"]
}

 

Указатель JSON

В дополнение к идентификации документа схемы вы также можете идентифицировать подсхемы. Наиболее распространенный способ сделать это-использовать указатель JSON во фрагменте URI, который указывает "/properties/street_address" на подсхему.

Указатель JSON описывает разделенный косой чертой путь для обхода ключей в объектах документа. Следовательно, означает:

  1. найдите значение ключа "properties"
  2. внутри этого объекта найдите значение ключа "street_address"

URI "https://example.com/schemas/address#/properties/street_address" идентифицирует выделенную подсхему в следующей схеме.

{
  "$id": "https://example.com/schemas/address",

  "type": "object",
  "properties": {
    "street_address":
      {
        "$anchor": "#street_address",
        "type": "string"
      },
    "city": { "type": "string" },
    "state": { "type": "string" }
  },
  "required": ["street_address", "city", "state"]
}

 

$anchor

Менее распространенным способом идентификации подсхемы является создание именованной привязки в схеме с использованием "$anchor" ключевого слова и использование этого имени во фрагменте URI. Якоря должны начинаться с буквы, за которой следует любое количество букв, цифр, "-", "_", ":", или ".".

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

URI "https://example.com/schemas/address#street_address" идентифицирует подсхему в выделенной части следующей схемы.

{
  "$id": "https://example.com/schemas/address",

  "type": "object",
  "properties": {
    "street_address":
      {
        "$anchor": "#street_address",
        "type": "string"
      },
    "city": { "type": "string" },
    "state": { "type": "string" }
  },
  "required": ["street_address", "city", "state"]
}

 

$ref

Схема может ссылаться на другую схему с помощью ключевого слова "$ref". Значение "$ref" - это ссылка на URI,которая сопоставляется с базовым URI схемы. При оценке a "$ref" реализация использует разрешенный идентификатор для извлечения схемы , на которую ссылается, и применяет эту схему к экземпляру.

В этом примере предположим, что мы хотим определить запись клиента, в которой у каждого клиента может быть как адрес доставки, так и адрес выставления счета. Адреса всегда одни и те же - у них есть адрес улицы, города и штата, поэтому мы не хотим дублировать эту часть схемы везде, где мы хотим сохранить адрес. Это не только сделает схему более подробной, но и затруднит ее обновление в будущем. Если бы наша воображаемая компания в будущем начала заниматься международным бизнесом, и мы хотели бы добавить поле страны ко всем адресам, было бы лучше сделать это в одном месте, а не везде, где используются адреса.

{
  "$id": "https://example.com/schemas/customer",

  "type": "object",
  "properties": {
    "first_name": { "type": "string" },
    "last_name": { "type": "string" },
    "shipping_address": { "$ref": "/schemas/address" },
    "billing_address": { "$ref": "/schemas/address" }
  },
  "required": ["first_name", "last_name", "shipping_address", "billing_address"]
}

URI-ссылки в "$ref" разрешении сопоставляются с базовым URI схемы (https://example.com/schemas/customer), что приводит к "https://example.com/schemas/address". Реализация извлекает эту схему и использует ее для оценки свойств "shipping_address" и "billing_address".

Примечание: При использовании "$ref" в анонимной схеме относительные ссылки могут быть неразрешимы. Давайте предположим, что этот пример используется в качестве анонимной схемы.

{
  "type": "object",
  "properties": {
    "first_name": { "type": "string" },
    "last_name": { "type": "string" },
    "shipping_address": { "$ref": "https://example.com/schemas/address" },
    "billing_address": { "$ref": "/schemas/address" }
  },
  "required": ["first_name", "last_name", "shipping_address", "billing_address"]
}

"$ref" в "/properties/shipping_address" может разрешаться просто отлично без не относительного базового URI для разрешения, но "$ref" в "/properties/billing_address" не может разрешаться в не относительный URI и, следовательно, не может использоваться для извлечения схемы адресов.

 

$defs

Иногда у нас есть небольшие подсхемы, которые предназначены только для использования в текущей схеме, и нет смысла определять их как отдельные схемы. Хотя мы можем идентифицировать любую подсхему с помощью указателей JSON или именованных привязок, "$defs" ключевое слово предоставляет нам стандартизированное место для хранения подсхем, предназначенных для повторного использования в текущем документе схемы.

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

{
  "$id": "https://example.com/schemas/customer",

  "type": "object",
  "properties": {
    "first_name": { "$ref": "#/$defs/name" },
    "last_name": { "$ref": "#/$defs/name" },
    "shipping_address": { "$ref": "/schemas/address" },
    "billing_address": { "$ref": "/schemas/address" }
  },
  "required": ["first_name", "last_name", "shipping_address", "billing_address"],

  "$defs": {
    "name": { "type": "string" }
  }
}

 

"$ref" это не просто хорошо для того, чтобы избежать дублирования. Это также может быть полезно для написания схем, которые легче читать и поддерживать. Сложные части схемы могут быть определены "$defs" c описательными именами и ссылаться там, где это необходимо. Это позволяет читателям схемы быстрее и легче понять схему на высоком уровне, прежде чем погружаться в более сложные части.

Примечание: Можно ссылаться на внешнюю подсхему, но обычно вы хотите ограничить "$ref" ссылками либо на внешнюю схему, либо на внутреннюю подсхему, определенную в "$defs".

 

Рекурсия

Ключевое слово "$ref" может использоваться для создания рекурсивных схем, которые ссылаются сами на себя. Например, у вас может быть  схема "person", содержащая массив "children" каждый из которых также "person" является экземпляром.

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "children": {
      "type": "array",
      "items": { "$ref": "#" }
    }
  }
}

Фрагмент британского королевского генеалогического древа

{
  "name": "Elizabeth",
  "children": [
    {
      "name": "Charles",
      "children": [
        {
          "name": "William",
          "children": [
            { "name": "George" },
            { "name": "Charlotte" }
          ]
        },
        {
          "name": "Harry"
        }
      ]
    }
  ]
}

Выше мы создали схему, которая ссылается на саму себя, эффективно создавая “цикл” в валидаторе, который одновременно разрешен и полезен. Однако обратите внимание, что "$ref" ссылка на другое"$ref" может привести к бесконечному циклу в распознавателе и явно запрещена.

{
  "$defs": {
    "alice": { "$ref": "#/$defs/bob" },
    "bob": { "$ref": "#/$defs/alice" }
  }
}

 

Обвязка

Работа с несколькими документами схемы удобна для разработки, но для распространения часто удобнее объединять все ваши схемы в один документ схемы. Это можно сделать с помощью "$id" ключевого слова в подсхеме. Когда "$id" используется в подсхеме, это указывает на встроенную схему. Идентификатор встроенной схемы-это значение "$id", сопоставленное с базовым URI схемы, в которой она отображается. Документ схемы, включающий встроенные схемы, называется Составным документом схемы. Каждая схема с "$id" документом составной схемы называется ресурсом схемы.

Примечание: Это аналогично "<iframe>" тегу в HTML.

Примечание: При разработке схем необычно использовать встроенные схемы. Обычно лучше не использовать эту функцию явно и использовать инструменты компоновки схем для создания связанных схем, если это необходимо.

В этом примере показан пример схемы клиента и пример схемы адреса, объединенные в документ составной схемы.

{
  "$id": "https://example.com/schemas/customer",
  "$schema": "https://json-schema.org/draft/2020-12/schema",

  "type": "object",
  "properties": {
    "first_name": { "type": "string" },
    "last_name": { "type": "string" },
    "shipping_address": { "$ref": "/schemas/address" },
    "billing_address": { "$ref": "/schemas/address" }
  },
  "required": ["first_name", "last_name", "shipping_address", "billing_address"],

  "$defs": {
    "address": {
      "$id": "/schemas/address",
      "$schema": "http://json-schema.org/draft-07/schema#",

      "type": "object",
      "properties": {
        "street_address": { "type": "string" },
        "city": { "type": "string" },
        "state": { "$ref": "#/definitions/state" }
      },
      "required": ["street_address", "city", "state"],

      "definitions": {
        "state": { "enum": ["CA", "NY", "... etc ..."] }
      }
    }
  }
}

Все ссылки в документе составной схемы должны быть одинаковыми независимо от того, включены ресурсы Схемы в комплект или нет. Обратите внимание, что "$ref" ключевые слова из схемы клиента не изменились. Единственное отличие состоит в том, что схема адреса теперь определена в "/$defs/address", а не в отдельном документе схемы. Вы не могли использовать "#/$defs/address" ссылку на схему адресов, потому что, если вы разблокируете схему, эта ссылка больше не будет указывать на схему адресов.

Вы также должны увидеть, что "$ref": "#/definitions/state" это соответствует ключевому слову "definitions" в схеме адресов, а не в схеме верхнего уровня, как это было бы, если бы встроенная схема не использовалась.

Каждый ресурс схемы оценивается независимо и может использовать различные диалекты схемы JSON. В приведенном выше примере Ресурс схемы адреса использует версию 7, в то время как ресурс схемы клиента использует Проект 2020-12. Если "$schema" во встроенной схеме объявлено "нет", по умолчанию используется диалект родительской схемы.

 

---

Благодарю за внимание

См. также

Сайты и интернет-магазины WEB-интеграция Системный администратор Программист Пользователь Платформа 1С v8.3 Конфигурации 1cv8 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM разных брендов в одной информационной базе в ручном и автоматическом режиме.

36000 руб.

03.08.2020    17794    19    22    

17

Сайты и интернет-магазины Интеграция WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 Управленческий учет Платные (руб)

Интеграция 1С и Битрикс 24. Разработка имеет двухстороннюю синхронизацию 1С и Bitrix24 задачами. Решение позволяет создавать пользователя в 1С из Битрикс24 и наоборот. Данная разработка технически подходит под все основные конфигурации линейки продуктов 1С:Предприятие 8.3 (платформа начиная с 8.3.23). При приобретении предоставляется 1 месяц бесплатных обновлений разработки. Доступна демо-версия продукта с подключением Вашего Битрикс24

5040 руб.

04.05.2021    19878    13    17    

17

WEB-интеграция 8.3.8 Конфигурации 1cv8 Автомобили, автосервисы Беларусь Украина Россия Казахстан Управленческий учет Платные (руб)

Расширение предназначено для конфигурации "1С:Предприятие 8. Управление Автотранспортом. ПРОФ". Функционал модуля: 1. Заполнение регистров сведений по подсистеме "Мониторинг", а именно: события по мониторингу, координаты по мониторингу, пробег и расход по мониторингу, текущее местоположение ТС по мониторингу 2. Заполнение путевого листа: пробег по мониторингу, время выезда/заезда, табличная часть ГСМ, места стоянок по геозонам. 3. Отчеты по данным загруженным в регистры сведений. 4. Предусмотрена автоматическая загрузка данных в фоновом режиме (условия работы данной загрузке читайте в описании товара) Модуль работает без включенной константы по настройкам мониторинга. Модуль формы предоставляется с открытым кодом, общий модуль защищен. Любой заинтересованный пользователь, имеет возможность скачать демо-версию расширения.

22656 руб.

25.05.2021    14432    42    8    

18

WEB-интеграция Программист Руководитель проекта Платформа 1С v8.3 Конфигурации 1cv8 1С:Франчайзи, автоматизация бизнеса Платные (руб)

Расширение значительно упрощает написание API на 1С. Веб программисты получают простой и понятный доступ к 1С. Описание API создаётся автоматически и представляется в виде удобном как для человека, так и для программной обработки.

24000 руб.

27.09.2024    1190    1    0    

3
Оставьте свое сообщение