The JSON schema validation library that works with https://github.com/Kotlin/kotlinx.serialization
MIT License
This multiplatform library is an implementation of JSON schema that can validate JsonElement from kotlinx.serialization-json library.
Target |
---|
jvm |
js |
wasmJs |
macosX64 |
macosArm64 |
iosArm64 |
iosSimulatorArm64 |
linuxX64 |
linuxArm64 |
mingwX64 |
In order to use releases add Maven Central repository to the list of repositories.
repositories {
mavenCentral()
}
implementation("io.github.optimumcode:json-schema-validator:0.2.3")
repositories {
mavenCentral()
}
implementation 'io.github.optimumcode:json-schema-validator:0.2.3'
Release are published to Sonatype repository. The synchronization with Maven Central takes time. If you want to use the release right after the publication you should add Sonatype Release repository to your build script.
repositories {
maven(url = "https://s01.oss.sonatype.org/content/repositories/releases/")
}
repositories {
maven { url 'https://s01.oss.sonatype.org/content/repositories/releases/' }
}
If you want to use SNAPSHOT version you should add Sonatype Snapshot repository to your build script.
repositories {
maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots")
}
implementation("io.github.optimumcode:json-schema-validator:0.2.4-SNAPSHOT")
repositories {
maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots' }
}
implementation 'io.github.optimumcode:json-schema-validator:0.2.4-SNAPSHOT'
If you have just one JSON schema or many independent schemes
you can create it using factory methods defined on JsonSchema
class.
import io.github.optimumcode.json.schema.JsonSchema
import io.github.optimumcode.json.schema.ValidationError
import kotlinx.serialization.json.JsonElement
val key = "\$" // to use $ in multiline string
val schema = JsonSchema.fromDefinition(
"""
{
"${key}schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"positiveInteger": {
"type": "integer",
"minimum": 0
}
},
"properties": {
"size": { "${key}ref": "#/definitions/positiveInteger" }
}
}
""".trimIndent(),
)
val errors = mutableListOf<ValidationError>()
val elementToValidate: JsonElement = loadJsonToValidate()
val valid = schema.validate(elementToValidate, errors::add)
You can also use predefined ValidationOutput
s to collect the results.
Output formats are defined in draft 2020-12.
The most performance can be achieved by using either flag
or basic
collectors.
The detailed
and verbose
provide more structured information but this adds additional cost to the validation process
(because they collect hierarchical output).
import io.github.optimumcode.json.schema.JsonSchema
import io.github.optimumcode.json.schema.OutputCollector
import io.github.optimumcode.json.schema.ValidationOutput.Flag
import io.github.optimumcode.json.schema.ValidationOutput.Basic
import io.github.optimumcode.json.schema.ValidationOutput.OutputUnit
val flag: Flag = schema.validate(elementToValidate, OutputCollector.flag())
val basic: Basic = schema.validate(elementToValidate, OutputCollector.basic())
val detailed: OutputUnit = schema.validate(elementToValidate, OutputCollector.detailed())
val verbose: OutputUnit = schema.validate(elementToValidate, OutputCollector.verbose())
If you need to use more than one schema, and they have references to other schemas you should use JsonSchemaLoader
class.
import io.github.optimumcode.json.schema.JsonSchemaLoader
import io.github.optimumcode.json.schema.JsonSchema
import io.github.optimumcode.json.schema.ValidationError
import kotlinx.serialization.json.JsonElement
val schema: JsonSchema = JsonSchemaLoader.create()
.register(
"""
{
"${KEY}id": "https://test.com",
"properties": {
"name": {
"type": "string"
}
}
}
""".trimIndent(),
).fromDefinition(
"""
{
"properties": {
"anotherName": {
"${KEY}ref": "https://test.com#/properties/name"
}
}
}
""".trimIndent(),
)
val errors = mutableListOf<ValidationError>()
val elementToValidate: JsonElement = loadJsonToValidate()
val valid = schema.validate(elementToValidate, errors::add)
Keyword | Status |
---|---|
$id | Supported. $id in sub-schemas are collected as well and can be used in $ref |
$schema | Supported. Validates if schema is one of the supported schemas. The last supported is used if empty |
$ref | Supported |
definitions | Supported. Definitions are loaded and can be referenced |
Category | Assertion | Status |
---|---|---|
General | type | Supported all type defined in the specification |
enum | Supported | |
const | Supported | |
Numbers | multipleOf | Supported |
maximum | Supported | |
exclusiveMaximum | Supported | |
minimum | Supported | |
exclusiveMinimum | Supported | |
Strings | maxLength | Supported |
minLength | Supported | |
pattern | Supported (kotlin.text.Regex is used) | |
Arrays | items | Supported |
additionalItems | Supported | |
maxItems | Supported | |
uniqueItems | Supported | |
contains | Supported | |
Objects | maxProperties | Supported |
minProperties | Supported | |
required | Supported | |
properties | Supported | |
patternProperties | Supported (kotlin.text.Regex is used) | |
additionalProperties | Supported | |
dependencies | Supported | |
propertyNames | Supported | |
Conditions | if/then/else | Supported |
Boolean logic | allOf | Supported |
anyOf | Supported (all validation will be executed even if the element matches the first one) | |
oneOf | Supported | |
not | Supported |
Keyword | Status |
---|---|
$id | Supported. $id in sub-schemas are collected as well and can be used in $ref |
$schema | Supported. Validates if schema is one of the supported schemas. The last supported is used if empty |
$ref | Supported |
$recursiveRef | Supported |
$defs/definitions | Supported. Definitions are loaded and can be referenced |
$vocabulary | Supported. You can disable and enable vocabularies through custom meta-schemes |
Category | Assertion | Status |
---|---|---|
General | type | Supported all type defined in the specification |
enum | Supported | |
const | Supported | |
Numbers | multipleOf | Supported |
maximum | Supported | |
exclusiveMaximum | Supported | |
minimum | Supported | |
exclusiveMinimum | Supported | |
Strings | maxLength | Supported |
minLength | Supported | |
pattern | Supported (kotlin.text.Regex is used) | |
Arrays | items | Supported |
additionalItems | Supported | |
unevaluatedItems | Supported | |
maxItems | Supported | |
uniqueItems | Supported | |
contains | Supported | |
minContains | Supported (does not affect the work of contains assertion anyhow even if minContains=0) | |
maxContains | Supported | |
Objects | maxProperties | Supported |
minProperties | Supported | |
required | Supported | |
properties | Supported | |
patternProperties | Supported (kotlin.text.Regex is used) | |
additionalProperties | Supported | |
unevaluatedProperties | Supported | |
dependentRequired | Supported | |
dependentSchemas | Supported | |
propertyNames | Supported | |
Conditions | if/then/else | Supported |
Boolean logic | allOf | Supported |
anyOf | Supported (all validation will be executed even if the element matches the first one) | |
oneOf | Supported | |
not | Supported |
Keyword | Status |
---|---|
$id | Supported. $id in sub-schemas are collected as well and can be used in $ref |
$schema | Supported. Validates if schema is one of the supported schemas. The last supported is used if empty |
$ref | Supported |
$dynamicRef/$dynamicAnchor | Supported |
$defs/definitions | Supported. Definitions are loaded and can be referenced |
$vocabulary | Supported. You can disable and enable vocabularies through custom meta-schemes |
Category | Assertion | Status |
---|---|---|
General | type | Supported all type defined in the specification |
enum | Supported | |
const | Supported | |
Numbers | multipleOf | Supported |
maximum | Supported | |
exclusiveMaximum | Supported | |
minimum | Supported | |
exclusiveMinimum | Supported | |
Strings | maxLength | Supported |
minLength | Supported | |
pattern | Supported (kotlin.text.Regex is used) | |
Arrays | prefixItems | Supported |
items | Supported | |
unevaluatedItems | Supported | |
maxItems | Supported | |
uniqueItems | Supported | |
contains | Supported | |
minContains | Supported | |
maxContains | Supported | |
Objects | maxProperties | Supported |
minProperties | Supported | |
required | Supported | |
properties | Supported | |
patternProperties | Supported (kotlin.text.Regex is used) | |
additionalProperties | Supported | |
unevaluatedProperties | Supported | |
dependentRequired | Supported | |
dependentSchemas | Supported | |
propertyNames | Supported | |
Conditions | if/then/else | Supported |
Boolean logic | allOf | Supported |
anyOf | Supported (all validation will be executed even if the element matches the first one) | |
oneOf | Supported | |
not | Supported |
The library supports format
assertion.
All formats from JSON schema draft 2020-12 are supported.
According to JSON schema specification the format
keyword produces assertion by default for all drafts before draft 2019-09.
Starting from draft 2019-09 the format
keyword produces only annotation by default.
You can change the default behaviour by specifying the corresponding option in JsonSchemaLoader
.
import io.github.optimumcode.json.schema.FormatBehavior.ANNOTATION_AND_ASSERTION
import io.github.optimumcode.json.schema.JsonSchemaLoader
import io.github.optimumcode.json.schema.SchemaOption
val schema = JsonSchemaLoader.create()
// option to change the default behavior
.withSchemaOption(SchemaOption.FORMAT_BEHAVIOR_OPTION, ANNOTATION_AND_ASSERTION)
// or
.withSchemaOption(SchemaOption.FORMAT_BEHAVIOR_OPTION, ANNOTATION_ONLY)
.fromDefinition(schemaDefinition)
Alternatively, for drafts starting from the draft 2019-09 you can either use a custom meta-schema with format-assertion vocabulary enabled
and use this meta-schema in $schema
property or define $vocabulary
block with enabled format assertion in your schema.
Implementation details:
Regex
class is used.There is also an API to implement the user's defined format validation. The FormatValidator interface can be user for that. The custom format validators can be register in JsonSchemaLoader.
Please note, that the format validation API is marked as experimental and will require OptIn
declaration in your code.
You can implement custom assertions and use them. Read more here.
This library uses official JSON schema test suites as a part of the CI to make sure the validation meet the expected behavior. Not everything is supported right now but the missing functionality might be added in the future. The tests are located here.
NOTE: Python 3.* is required to run test-suites. It is used to generate list of remote schemas using this script
This library is also integrated into bowtie and runs against the official test suite along with other libraries. You can find the report here.
There is a benchmark project that compares this library with some other ones:
The benchmark is scheduled to run every night on Monday. You can see the results in the latest workflow execution.
Also, benchmark results are available here in nice charts. Many thanks to maintainers of github-action-benchmark repo.
The update to Kotlin 1.9.22 came with an issue for JS incremental compilation.
In case you see an error about main function that already bind please execute clean
task.
When you build project for linux target you might get an error about missing native library.
This is because com.doist.x:normalize
requires this library to perform string normalization.
This is needed to support idn-hostname
format. Install this library with the following command:
sudo apt-get install -y libunistring-dev
Devcontainers is a cool feature. However, by default in Codespaces and DevPod you will use VS Code. This is a good IDE but not for Kotlin, unfortunately. The extension that is available for VS Code to support Kotlin works quite slow (when workspace is just started) and sometimes does not work correctly with multiplatform definitions. Because of that I higly recoment using JetBrains Gateway (but it looks like the GitHub Codespace provider is not currently compatible with latest Gateway version). However, there is a way to connect to Codespace and work with project using JetBrains IDE. Please, read details here.
$schema
property validation (if not set the latest supported will be used)$id
support (for nested schemas and for referencing)