Статьи

Джейсон Скима — Проверка данных нотации объектов JavaScript


Схема — это просто шаблон. Чистая форма. В вычислительном отношении его можно использовать для сопоставления экземпляра с шаблоном или создания экземпляра из него. Именно так традиционно использовались XML-схема и даже DTD — в основном для проверки, но также и в качестве простого способа создания шаблона с заполнением пробелов. Поскольку JSON штурмом овладел XML, необходимость в схеме (в моем случае, 
наконец,! ) Одолела минималистский инстинкт простоты любителей JSON. Так родился JSON Schema. Судя по самой ранней активности в Группе Google, где зависал комитет по спецификации (
https://groups.google.com/forum/#!forum/json-schema), первоначальный проект был сделан в 2008 году, и он был очень вдохновлен XML-схемой. Что не обязательно плохо. С одной стороны, синтаксис, используемый для определения схемы, это просто JSON. Быстрый пример:

{
  "type":"object",
  "properties":{
    "firstName":{"type":"string"},
    "age":{"type":"number"}
  },
  "required":["firstName", "lastName"]
}

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

  1. Это должен быть объект JSON, потому что мы поставили  «type»: «object» .
  2. Если оно имеет   свойство firstName , значением этого свойства должна быть строка.
  3. Значение   свойства age , если оно присутствует, должно быть числом.
  4. Свойства  firstName  и  lastName  являются обязательными.

Довольно просто. Не говоря уже о том, что мы не определили формат (то есть 
подсхему ) для
свойства
lastName  , нам все еще требуется его наличие, нам просто все равно, какое значение будет. Итак, вот как это происходит: схема — это объект JSON, в котором вы указываете различные ограничения. Если заданные данные JSON удовлетворяют всем ограничениям, схема соответствует, в противном случае — нет. Стандарт стандартизирует возможные ограничения, но с несколькими дополнительными ключевыми словами для структурирования больших и сложных схем.

Почему я забочусь?


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

Несколько лет назад я принял решение отказаться от бобов для проекта, и с тех пор я использовал эту стратегию в нескольких других, менее масштабных проектах, где она работает еще лучше. Но популярные библиотеки Java для JSON — это катастрофа. Уже был один JSR 353 о JSON, «любой» API, не удивительно, что он кажется мертвым по прибытии, почти так же плохо, как Джексон и Гсон. И теперь Java 9 обещает «легкий JSON API», который выглядит так, как будто он действительно хорошо спроектирован, хотя у него иные цели, чем мне нужно, и простота — не одна из них. Итак, я написал 
MJSON, Это небольшой 1 файл Java, библиотека JSON. Я хотел что-то простое, элегантное и мощное. Первые два, я думаю, я достиг, «мощная» часть только на полпути. Например, многие люди ожидают, что библиотеки JSON будут иметь сопоставления JSON <-> POJOs, а mJson — нет, хотя у него есть точки расширения, которые можно легко реализовать самостоятельно (откровенно говоря, для реализации этого материала требуется 1/2 дня, если он вам так нужен) ).

Моделирование с помощью bean-компонентов предлагает справку средства проверки типов при проверке того, что структуры имеют желаемую форму. Если вы используете JSON только для преобразования его в Java-бины, я полагаю, что процесс отображения в некоторой степени является окольным способом проверки. В противном случае вы либо соглашаетесь жить с риском ошибок, либо с дополнительным раздуванием, необходимым для защитного кодирования против структуры, которая может быть сломана. Чтобы избежать этих проблем, вы можете написать схему, наподобие правил вашего собственного типа, и использовать ее в стратегических точках программы. Например, когда вы получаете данные из Интернета. Или когда вы тестируете новый модуль. Не то чтобы я выступал за использование JSON + Schema вместо Java POJO при любых обстоятельствах. Но вы должны попробовать это некоторое время, посмотреть, где это имеет смысл. Кстати,в дополнение к тому, что схемы инструмента проверки являются по сути метаданными, которые представляют вашу модель (как XML-схема).


Хороший.
Теперь я хочу дать вам быстрый …

Ускоренный курс по схеме JSON


Во-первых, ограничения организованы по типу JSON.
вероятно, это ваша отправная точка для описания того, как выглядит JSON:
{"type": "string"|"object"|"array"|"number"|"boolean"|"null"|"integer"|"any"}

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

Теперь, учитывая тип объекта JSON, существуют дополнительные ограничения. Начнем с объекта. С помощью 
свойств  вы описываете формат свойств объекта в форме подсхем для каждого возможного свойства, о котором вы хотите поговорить. Вы не должны перечислить их все , но если ваш является исчерпывающим, вы можете утверждать , что с 
additionalProperties  ключевым словом:

{
  "properties": { "firstName": { "type":"string"}, etc.... },
  "additionalProperties":false
}

Эти ключевые слова на самом деле довольно универсальны. Здесь мы запрещаем любые другие свойства, кроме явно указанных. Если вместо этого мы хотим позволить объекту иметь другие свойства, мы можем установить для него значение true или не устанавливать его вообще. Или, значение  additionalProperties  ключевого слова может быть альтернативно суб-схема , которая определяет вид всех дополнительных свойств в объекте.

Мы видели, как указать  обязательные  свойства в примере выше. Два других параметра ограничивают размер объекта:  minProperties  и  maxProperties, А для большей гибкости в именовании вы можете использовать шаблоны регулярных выражений для имен свойств — это может быть полезно, если у вас есть какой-либо формат, состоящий из букв и цифр или UUID, например. Ключевое слово для этого —  patternProperties :

{
  "patternProperties": { "j_
 
  +[0-9]+": { "type":"object"} },
  "minProperties":1,
  "maxProperties":100
}

 

Выше можно указать от 1 до 100 свойств, имена которых соответствуют шаблону j_ letters_digits  . Вот и все об объектах. Это важно.

Проверка массивов в основном сводится к проверке их элементов, поэтому вы предоставляете подсхему для элементов с   ключевым словом items . Либо вы даете одну схему или массив схем. Одна схема будет применяться ко всем элементам массива, в то время как массив должен соответствовать элементу для элемента (так называемая  типизация кортежей ). Это основа. Вот дополнительные функции: у нас есть  minItems  и maxItems  для управления размером массива; у нас есть  дополнительные элементы,  которые применяются только когда элементы это массив, и он контролирует, что делать с дополнительными элементами, когда они есть. Подобно  additionalProperties  ключевых слов, вы можете поместить ложные запретить дополнительные элементы или предоставить схему для проверки их. Наконец, вы можете потребовать, чтобы все элементы массива отличались по ключевому  слову uniqueItems . Пример:

{
  "items": { "type": "string" },
  "uniqueItems":true, 
  "minItems":10,
  "additionalItems":false
}

Здесь мы указываем ровно 10 уникальных строк в массиве. Вот и все для областей. Числа и строки довольно просты. Для числа вы можете определить диапазон и делимость. Это ключевые слова: минимальное  (для> =),  максимальное  (для <=),  exclusiveMinimum  (если true,  минимальное значение  >), exclusiveMaximum  (если true,  максимальное значение  <). Строки могут быть проверены с помощью регулярного выражения с  ключевым словом pattern и путем ограничения их длины с помощью  minLength  и  maxLength . Надеюсь, вам не нужны примеры, чтобы увидеть проверку строк и чисел в действии. Синтаксис регулярного выражения: ECMA 262 (http://www.ecma-international.org/publications/standards/Ecma-262.htm ).

Обратите внимание, что пока нет логических операторов. В предыдущей итерации спецификации схемы JSON (черновик 3) некоторые из этих ключевых слов допускали области в качестве значений с интерпретацией «или». Например,  типом  может быть  [«string», «number»],  указывающее, что значением может быть либо строка, либо число. От них отказались в пользу всеобъемлющего набора логических операторов для объединения схемы в более сложное проверочное поведение. Давайте рассмотрим их: «и» — это  allOf , или «- это  anyOf»  , «xor» — это  oneOf,  «not» —  нет . Это буквально следует понимать как стандартную логику:не  должен быть подсхемой, которая не должна совпадать для успешной проверки,  allOf должен быть массивом схем, и все они должны совпадать для принятия JSON. Аналогично, anyOf  — это массив, которому должен соответствовать хотя бы один, а  oneOf  означает, что ровно одна из схем в массиве должна соответствовать целевому JSON. Например, для обеспечения того, чтобы человек был женат, мы могли бы заявить, что у него должно быть имущество мужа или жены, но не оба:

{ "oneOf":[
  {"required":["husband"]},
  {"required":["wife"]}
 ]
}

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

{ 
  "properties":{
  "gender":{"enum":["male", "female"]}
  }
}

При этом вы знаете почти все, что нужно знать о JSON Schema. Почти. Выше я упомянул «несколько дополнительных ключевых слов для структурирования больших сложных схем». Я утрировал. На самом деле есть только одно такое ключевое слово:  $ ref  ( идентификатор связанного ключевого слова на   самом деле не нужен). $ ref  позволяет вам ссылаться на схемы, определенные в другом месте, вместо того, чтобы повторять те же конструкции. Например, если где-то в Интернете существует стандартный формат адреса с определенной для него схемой и если эту схему можно получить по адресу http://standardschemas.org/address (составленный URL-адрес), вы можете сделать следующее:

{ 
  "properties":{
  "address":{"$ref":"http://standardschemas.org/address"}
  }
}

Интересная часть  $ ref  заключается в том, что URI может относиться к текущей схеме,  и  вы можете использовать указатель JSON во фрагменте URI (часть после знака pound #), чтобы ссылаться на часть схемы в документе! JSON Pointer — это небольшой RFC (http://tools.ietf.org/html/rfc6901), в котором определены пути-подобные выражения Unix для навигации по свойствам и массивам в сложной структуре JSON. Например, выражение  / дети / 0 / Жандр  относится к Жандр первого элемента в  детях  собственности массива. Обратите внимание, что используется только косая черта, без скобок и точек, и этого вполне достаточно. Если вы хотите избежать косой черты внутри имени свойства, напишите  ~ 1,  а чтобы избежать тильды, напишите  ~ 0, Например, чтобы получить в свое распоряжение надежную проверку почтового индекса, вы можете сделать следующее: 

{ 
  "properties":{
  "zip":{"$ref":"http://standardschemas.org/address#/postalCode"}
  }
}

Это означает, что вы можете определить схемы для вашего RESTful API в стандартном месте и опубликовать их и / или сослаться на них в своих ответах API. Любой валидатор JSON должен быть в состоянии извлечь правильную подсхему, и хорошая реализация кеширует их, чтобы вам не приходилось беспокоиться о сетевых скачках. Ссылочный URI может относиться к текущей схеме, поэтому, если у вас есть другие схемы в том же базовом местоположении, они могут ссылаться друг на друга независимо от того, где они развернуты. В качестве особого случая вы можете разрешать фрагменты, относящиеся к текущей схеме. Например:

{ 
  "myschemas": {
    "properName": { "type":"string", "pattern":"
 
  [a-z]+"}
  }
  "properties":{
    "firstName":{ "$ref":"#/myschemas/properName"},
    "lastName":{ "$ref":"#/myschemas/properName"}
  }
}

 

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

{ 
  "definitions": {
    "properName": { "type":"string", "pattern":"[A-Z][a-z]+"}
  }
  "properties":{
    "firstName":{ "$ref":"#/definitions/properName"},
    "lastName":{ "$ref":"#/definitions/properName"}
  }
}

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

Ресурсы


Теперь, чтобы использовать схему JSON, на самом деле не так много доступных реализаций.
Популярный (и раздутый) Джексон до сих пор поддерживает черновик 3, и эта часть, похоже, не поддерживается активно. Один из авторов спецификации JSON Schema реализовал полную поддержку поверх Джексона: 
https://github.com/fge/json-schema-validator , поэтому вам следует знать об этой реализации, особенно если вы уже являетесь пользователем Джексона. Но если вы этого не сделаете, я хочу указать вам еще один вариант, доступный с недавнего времени:
mJson 1.3,  который поддерживает проверку JSON Schema Draft 4:
 

Json schema = Json.read(new URL("http://mycompany.com/schemas/model");
Json data = Json.object("firstName", "John", "lastName", "Smith")
            .set("children", Json.array().add(/* etc... */));
Json errors = schema.validate(data);
for (Json e: errors.asJsonList())
    System.out.println("JSON validation error:" + e);

Честно говоря, некоторые другие библиотеки также поддерживают генерацию JSON на основе схемы со значениями по умолчанию, заданными 
 ключевым словом default, которое я здесь не рассматривал. mJson пока не делает этого, но, если есть спрос, я добавлю его. Ключевыми словами, которые я не охватил, являются
заголовок
описание  (ключевые слова метаданных, не использованные во время проверки) и 
идентификатор . Чтобы стать и экспертом, вы всегда можете прочитать спецификацию. Вот он, наряду с некоторыми другими ресурсами:

На десерт

Чтобы расстаться, я хочу оставить вам немного драгоценности, еще один ресурс. Кто-то придумал гораздо более краткий язык для описания структур JSON. Он называется Orderly, он компилируется в схему JSON, и я не пробовал его. Если вы делаете, пожалуйста, сообщите. Это на 
http://orderly-json.org/   и выглядит это так:

object {
string name;
string description?;
string homepage /^http:/;
integer {1500,3000} invented;
}*;