Эта библиотека в настоящее время находится в режиме обслуживания и заменена erosb/json-sKema.
В этом репозитории не будет никаких новых функций. Он обеспечивает надежную поддержку версий Draft-04, Draft-06 и Draft-07 спецификации JSON Schema.
Последний проект 2020-12 поддерживается только erosb/json-sKema.
Этот проект представляет собой реализацию спецификаций JSON Schema Draft v4, Draft v6 и Draft v7. Он использует API org.json (созданный Дугласом Крокфордом) для представления данных JSON.
Предположим, вы уже знаете, что такое схема JSON, и хотите использовать ее в приложении Java для проверки данных JSON. Но, как вы, возможно, уже заметили, существует и другая Java-реализация спецификации JSON Schema. Итак, вот несколько советов о том, какой из них использовать:
Добавьте следующую зависимость в ваш pom.xml
:
< dependency >
< groupId >com.github.erosb</ groupId >
< artifactId >everit-json-schema</ artifactId >
< version >1.14.4</ version >
</ dependency >
Примечание о более старых версиях : версии между 1.6.0
и 1.9.1
можно найти только в JitPack с координатами com.github.everit-org.json-schema:org.everit.json.schema
. Версии 1.0.0
... 1.5.1
доступны на Maven Central по координатам org.everit.json:org.everit.json.schema
.
Было пару попыток заставить библиотеку работать на Java 6/7.
Порт Java6 версии 1.9.2 был разработан @mindbender1 и доступен через Maven Central по следующим координатам:
< dependency >
< groupId >com.github.erosb</ groupId >
< artifactId >everit-json-schema-jdk6</ artifactId >
< version >1.9.2</ version >
</ dependency >
Бэкпорты старых версий:
com.doctusoft:json-schema-java7:1.4.1
com.github.rdruilhe.json-schema:org.everit.json.schema:1.1.1
import org . everit . json . schema . Schema ;
import org . everit . json . schema . loader . SchemaLoader ;
import org . json . JSONObject ;
import org . json . JSONTokener ;
// ...
try ( InputStream inputStream = getClass (). getResourceAsStream ( "/path/to/your/schema.json" )) {
JSONObject rawSchema = new JSONObject ( new JSONTokener ( inputStream ));
Schema schema = SchemaLoader . load ( rawSchema );
schema . validate ( new JSONObject ( "{ " hello " : " world " }" )); // throws a ValidationException if this object is invalid
}
В настоящее время существует 4 основных выпуска JSON Schema: Черновик 3, Черновик 4, Черновик 6 и Черновик 7. В этой библиотеке реализованы три новые версии. Вы можете быстро просмотреть различия здесь и здесь. Поскольку эти две версии имеют ряд различий (а черновик 6 не имеет обратной совместимости с черновиком 4), полезно знать, какую версию вы будете использовать.
Лучший способ обозначить версию схемы JSON, которую вы хотите использовать, — включить URL-адрес ее мета-схемы в корень документа с помощью ключа "$schema"
. Это общепринятая запись, с помощью которой библиотека определяет, какую версию следует использовать.
Краткая справка:
"$schema": "http://json-schema.org/draft-04/schema"
, то будет использоваться черновик 4"$schema": "http://json-schema.org/draft-06/schema"
, то будет использоваться черновик 6"$schema": "http://json-schema.org/draft-07/schema"
, то будет использоваться черновик 7Если вы хотите явно указать версию мета-схемы, вы можете изменить значение по умолчанию с «Черновика 4» на «Черновик 6/7», настроив загрузчик следующим образом:
SchemaLoader loader = SchemaLoader . builder ()
. schemaJson ( yourSchemaJSON )
. draftV6Support () // or draftV7Support()
. build ();
Schema schema = loader . load (). build ();
Начиная с версии 1.1.0
валидатор собирает все нарушения схемы (вместо того, чтобы сразу же давать сбой при первом из них). Каждый сбой обозначается указателем JSON, указывающим от корня документа к нарушающей части. Если обнаружено более одного нарушения схемы, то исключение ValidationException
будет выдано для наиболее распространенных родительских элементов нарушений, и каждое отдельное нарушение можно получить с помощью метода ValidationException#getCausingExceptions()
.
Чтобы продемонстрировать вышеизложенные концепции, давайте рассмотрим пример. Рассмотрим следующую схему:
{
"type" : " object " ,
"properties" : {
"rectangle" : { "$ref" : " #/definitions/Rectangle " }
},
"definitions" : {
"size" : {
"type" : " number " ,
"minimum" : 0
},
"Rectangle" : {
"type" : " object " ,
"properties" : {
"a" : { "$ref" : " #/definitions/size " },
"b" : { "$ref" : " #/definitions/size " }
}
}
}
}
Следующий документ JSON имеет только одно нарушение схемы (поскольку «a» не может быть отрицательным):
{
"rectangle" : {
"a" : -5 ,
"b" : 5
}
}
В этом случае выброшенное исключение ValidationException
будет указывать на #/rectangle/a
и не будет содержать дополнительных исключений:
try {
schema . validate ( rectangleSingleFailure );
} catch ( ValidationException e ) {
// prints #/rectangle/a: -5.0 is not higher or equal to 0
System . out . println ( e . getMessage ());
}
Теперь, чтобы проиллюстрировать способ обработки нескольких нарушений, давайте рассмотрим следующий документ JSON, в котором свойства «a» и «b» нарушают приведенную выше схему:
{
"rectangle" : {
"a" : -5 ,
"b" : " asd "
}
}
В этом случае выброшенное исключение ValidationException
будет указывать на #/rectangle
и имеет два подисключения, указывающие на #/rectangle/a
и #/rectangle/b
:
try {
schema . validate ( rectangleMultipleFailures );
} catch ( ValidationException e ) {
System . out . println ( e . getMessage ());
e . getCausingExceptions (). stream ()
. map ( ValidationException :: getMessage )
. forEach ( System . out :: println );
}
Это напечатает следующий вывод:
#/rectangle: 2 schema violations found
#/rectangle/a: -5.0 is not higher or equal to 0
#/rectangle/b: expected type: Number, found: String
Начиная с версии 1.4.0
можно распечатать экземпляры ValidationException
как отчеты об ошибках в формате JSON. Метод ValidationException#toJSON()
возвращает экземпляр JSONObject
со следующими ключами:
"message"
: удобное для программиста сообщение об исключении (описание ошибки проверки)"keyword"
: ключевое слово схемы JSON, которое было нарушено."pointerToViolation"
: указатель JSON, обозначающий путь от корня входного документа до его фрагмента, вызвавшего сбой проверки."schemaLocation"
: указатель JSON, обозначающий путь от корня схемы JSON до нарушенного ключевого слова."causingExceptions"
: массив (возможно, пустой) вложенных исключений. Каждое подисключение представлено как объект JSON с той же структурой, что описана в этом листинге. Подробнее о возникновении исключений см. выше. Обратите внимание, что полный отчет об ошибке представляет собой иерархическую древовидную структуру : подпричины причины можно получить с помощью #getCausingExceptions()
.
ValidationListener
может служить для устранения неоднозначности относительно того, как экземпляр JSON соответствует (или не соответствует) схеме. Вы можете прикрепить реализацию ValidationListener
к валидатору, чтобы получать уведомления о событиях о промежуточных результатах успеха/неудачи.
Пример:
import org . everit . json . schema . Validator ;
...
Validator validator = Validator . builder ()
. withListener ( new YourValidationListenerImplementation ())
. build ();
validator . performValidation ( schema , input );
Поддерживаемые в настоящее время события:
"$ref"
разрешается"allOf"
/ "anyOf"
/ "oneOf"
"allOf"
/ "anyOf"
/ "oneOf"
не соответствует"if"
"if"
не соответствует"then"
"then"
не соответствует"else"
"else"
не соответствует Дополнительную информацию см. в javadoc интерфейса org.everit.json.schema.event.ValidationListener
. Конкретные классы событий также имеют правильные реализации #toJSON()
и #toString()
поэтому вы можете распечатать их в удобном для анализа формате.
По умолчанию отчет об ошибках проверки отображается в режиме сбора (см. главу «Расследование ошибок»). Это удобно для получения подробного отчета об ошибках, но в некоторых случаях более целесообразно остановить проверку при обнаружении сбоя, не проверяя остальную часть документа JSON. Чтобы переключить этот режим проверки с быстрым сбоем
Validator
для вашей схемы вместо вызова Schema#validate(input)
failEarly()
ValidatorBuilder
Пример:
import org . everit . json . schema . Validator ;
...
Validator validator = Validator . builder ()
. failEarly ()
. build ();
validator . performValidation ( schema , input );
Примечание: класс Validator
является неизменяемым и потокобезопасным, поэтому вам не придется создавать новый для каждой проверки, достаточно настроить его только один раз.
В некоторых случаях при проверке чисел или логических значений имеет смысл принимать строковые значения, которые можно анализировать как такие примитивы, поскольку любая последующая обработка также автоматически преобразует эти литералы в правильные числовые и логические значения. Кроме того, нестроковые примитивные значения легко преобразовать в строки, так почему бы не разрешить использование любых примитивов json в качестве строк?
Например, возьмем такую схему:
{
"properties" : {
"booleanProp" : {
"type" : " boolean "
},
"integerProp" : {
"type" : " integer "
},
"nullProp" : {
"type" : " null "
},
"numberProp" : {
"type" : " number "
},
"stringProp" : {
"type" : " string "
}
}
}
Следующий документ JSON не прошел проверку, хотя все строки можно легко преобразовать в соответствующие значения:
{
"numberProp" : " 12.34 " ,
"integerProp" : " 12 " ,
"booleanProp" : " true " ,
"nullProp" : " null " ,
"stringProp" : 12.34
}
В этом случае, если вы хотите, чтобы приведенный выше экземпляр прошел проверку на соответствие схеме, вам необходимо использовать включенную мягкую примитивную конфигурацию проверки. Пример:
import org . everit . json . schema .*;
...
Validator validator = Validator . builder ()
. primitiveValidationStrategry ( PrimitiveValidationStrategy . LENIENT )
. build ();
validator . performValidation ( schema , input );
Примечание. В режиме щадящего анализа все 22 возможных логических литерала будут приниматься как логические значения.
Спецификация схемы JSON определяет ключевое слово «default» для обозначения значений по умолчанию, хотя в ней явно не указано, как оно должно влиять на процесс проверки. По умолчанию эта библиотека не устанавливает значения по умолчанию, но если вам нужна эта функция, вы можете включить ее с помощью метода SchemaLoaderBuilder#useDefaults(boolean)
перед загрузкой схемы:
{
"properties" : {
"prop" : {
"type" : " number " ,
"default" : 1
}
}
}
JSONObject input = new JSONObject ( "{}" );
System . out . println ( input . get ( "prop" )); // prints null
Schema schema = SchemaLoader . builder ()
. useDefaults ( true )
. schemaJson ( rawSchema )
. build ()
. load (). build ();
schema . validate ( input );
System . out . println ( input . get ( "prop" )); // prints 1
Если во input
отсутствуют некоторые свойства, которые имеют в схеме значения "default"
, то они будут установлены валидатором во время проверки.
Для поддержки ключевого слова "regex"
схемы JSON библиотека предлагает две возможные реализации:
java.util.regex
. Хотя библиотека RE2J обеспечивает значительно более высокую производительность, чем java.util.regex
, она не полностью совместима с синтаксисом, поддерживаемым java.util
или ECMA 262. Поэтому RE2J рекомендуется, если вас беспокоит производительность и ее ограничения приемлемы.
Реализация RE2J может быть активирована с помощью вызова SchemaLoaderBuilder#regexpFactory()
:
SchemaLoader loader = SchemaLoader . builder ()
. regexpFactory ( new RE2JRegexpFactory ())
// ...
. build ();
Примечания:
pom.xml
, чтобы она не увеличивала размер вашего артефакта без необходимостиjava.util
, в 1.8.0 использовалась реализация RE2J, а в 1.9.0 мы сделали ее настраиваемой из-за некоторых сообщений о регрессиях. Библиотека поддерживает ключевые слова readOnly
и writeOnly
которые впервые появились в проекте 7. Если вы хотите использовать эту функцию, то перед проверкой вам необходимо сообщить валидатору, происходит ли проверка в контексте чтения или записи. Пример:
схема.json:
{
"properties" : {
"id" : {
"type" : " number " ,
"readOnly" : true
}
}
}
Фрагмент кода проверки:
Validator validator = Validator . builder ()
. readWriteContext ( ReadWriteContext . WRITE )
. build ();
validator . performValidation ( schema , new JSONObject ( "{ " id " :42}" ));
В данном случае мы сообщили валидатору, что проверка происходит в контексте WRITE
, и во входном объекте JSON появляется свойство "id"
, которое в схеме помечено как "readOnly"
, поэтому этот вызов выдаст исключение ValidationException
.
Начиная с версии 1.2.0
библиотека поддерживает ключевое слово "format"
(которое является необязательной частью спецификации).
Поддерживаемые форматы различаются в зависимости от используемой вами версии спецификации схемы (поскольку стандартные форматы были представлены в разных версиях спецификации проверки).
Вот таблица совместимости поддерживаемых стандартных форматов:
Проект 4 | Проект 6 | Проект 7 | |
---|---|---|---|
дата-время | ✅ | ✅ | ✅ |
электронная почта | ✅ | ✅ | ✅ |
имя хоста | ✅ | ✅ | ✅ |
ipv4 | ✅ | ✅ | ✅ |
IPv6 | ✅ | ✅ | ✅ |
ури | ✅ | ✅ | ✅ |
uri-ссылка | ✅ | ✅ | |
uri-шаблон | ✅ | ✅ | |
JSON-указатель | ✅ | ✅ | |
дата | ✅ | ||
время | ✅ | ||
регулярное выражение | ✅ | ||
относительный указатель JSON | ✅ |
Библиотека также поддерживает добавление пользовательских валидаторов формата. Чтобы использовать собственный валидатор, вам необходимо
org.everit.json.schema.FormatValidator
org.everit.json.schema.loader.SchemaLoader.SchemaLoaderBuilder
перед загрузкой фактической схемыПредположим, что задача состоит в том, чтобы создать собственный валидатор, который принимает строки с четным количеством символов.
Пользовательский FormatValidator
будет выглядеть примерно так:
public class EvenCharNumValidator implements FormatValidator {
@ Override
public Optional < String > validate ( final String subject ) {
if ( subject . length () % 2 == 0 ) {
return Optional . empty ();
} else {
return Optional . of ( String . format ( "the length of string [%s] is odd" , subject ));
}
}
}
Чтобы привязать EvenCharNumValidator
к значению "format"
(например, "evenlength"
), вам необходимо привязать экземпляр валидатора к ключевому слову в конфигурации загрузчика схемы:
JSONObject rawSchema = new JSONObject ( new JSONTokener ( inputStream ));
SchemaLoader schemaLoader = SchemaLoader . builder ()
. schemaJson ( rawSchema ) // rawSchema is the JSON representation of the schema utilizing the "evenlength" non-standard format
. addFormatValidator ( "evenlength" , new EvenCharNumValidator ()) // the EvenCharNumValidator gets bound to the "evenlength" keyword
. build ();
Schema schema = schemaLoader . load (). build (); // the schema is created using the above created configuration
schema . validate ( jsonDocument ); // the document validation happens here
В документе схемы JSON можно использовать относительные URI для ссылки на ранее определенные типы. Такие ссылки выражаются с помощью ключевых слов "$ref"
и "$id"
. Хотя спецификация подробно описывает изменение области разрешения и разыменование, она не объясняет ожидаемое поведение, когда первый встречающийся "$ref"
или "$id"
является относительным URI.
В случае этой реализации можно явно определить абсолютный URI, служащий базовым URI (областью разрешения), используя соответствующий метод компоновщика:
SchemaLoader schemaLoader = SchemaLoader . builder ()
. schemaJson ( jsonSchema )
. resolutionScope ( "http://example.org/" ) // setting the default resolution scope
. build ();
По мере роста ваших схем вам захочется разделить их на несколько исходных файлов и связать их ссылками "$ref"
. Если вы хотите хранить схемы в пути к классам (вместо того, чтобы, например, обслуживать их через HTTP), рекомендуемым способом является использование протокола classpath:
чтобы схемы ссылались друг на друга. Чтобы протокол classpath:
работал:
SchemaClient
поддерживающий путь к классам, например: SchemaLoader schemaLoader = SchemaLoader . builder ()
. schemaClient ( SchemaClient . classPathAwareClient ())
. schemaJson ( jsonSchema )
. resolutionScope ( "classpath://my/schemas/directory/" ) // setting the default resolution scope
. build ();
Учитывая эту конфигурацию, следующие ссылки будут правильно разрешены в jsonSchema
:
{
"properties" : {
"sameDir" : { "$ref" : " sameDirSchema.json " },
"absPath" : { "$ref" : " classpath://somewhere/else/otherschema.json " },
"httpPath" : { "$ref" : " http://example.org/http-works-as-usual " },
}
}
и sameDirSchema.json
будет искаться в /my/schemas/directory/sameDirSchema.json
в пути к классам.
Иногда полезно работать с предварительно загруженными схемами, которым мы присваиваем произвольный URI (возможно, uuid) вместо загрузки схемы через URL. Это можно сделать, присвоив схемы URI с помощью метода #registerSchemaByURI()
загрузчика схемы. Пример:
SchemaLoader schemaLoader = SchemaLoader . builder ()
. registerSchemaByURI ( new URI ( "urn:uuid:a773c7a2-1a13-4f6a-a70d-694befe0ce63" ), aJSONObject )
. registerSchemaByURI ( new URI ( "http://example.org" ), otherJSONObject )
. schemaJson ( jsonSchema )
. resolutionScope ( "classpath://my/schemas/directory/" )
. build ();
Примечания:
JSONObject
или Boolean
(формальный тип параметра — Object
только потому, что у этих двух объектов нет другого общего суперкласса).SchemaClient
тоже подойдет, или вы даже можете использовать расширяемую обработку протокола пакета java.net
). Некоторые зависимости можно исключить из библиотеки, и она по-прежнему останется пригодной для использования с некоторыми ограничениями:
com.damnhandy:handy-uri-templates
, ваша схема не должна использовать формат "uri-template"
commons-validator:commons-validator
, ваша схема не должна использовать следующие форматы: "email"
, "ipv4"
, "ipv6"
, "hostname"
По версии библиотеки:
Сгенерированный javadoc версий 1.0.0–1.5.1 доступен на javadoc.io.
Для промежуточных версий (1.6.0–1.9.1) он нигде не публикуется.