JSONSchemer
JSON Schema validator. Supports drafts 4, 6, 7, 2019-09, 2020-12, OpenAPI 3.0, and OpenAPI 3.1.
Installation
Add this line to your application's Gemfile:
gem 'json_schemer'
And then execute:
$ bundle
Or install it yourself as:
$ gem install json_schemer
Usage
require 'json_schemer'
schema = {
'type' => 'object',
'properties' => {
'abc' => {
'type' => 'integer',
'minimum' => 11
}
}
}
schemer = JSONSchemer.schema(schema)
schemer.valid?({ 'abc' => 11 })
schemer.valid?({ 'abc' => 10 })
schemer.validate({ 'abc' => 10 }).to_a
data = {}
JSONSchemer.schema(
{
'properties' => {
'foo' => {
'default' => 'bar'
}
}
},
insert_property_defaults: true
).valid?(data)
data
require 'pathname'
schema = Pathname.new('/path/to/schema.json')
schemer = JSONSchemer.schema(schema)
schema = '{ "type": "integer" }'
schemer = JSONSchemer.schema(schema)
JSONSchemer.valid_schema?({ '$id' => 'valid' })
JSONSchemer.validate_schema({ '$id' => '#invalid' }).to_a
schema = {
'type' => 'integer',
'$defs' => {
'foo' => {
'type' => 'string'
}
}
}
schemer = JSONSchemer.schema(schema)
schemer.ref('#/$defs/foo').validate(1).to_a
schema = {
'$id' => 'http://example.com/schema',
'allOf' => [
{ '$ref' => 'schema/one' },
{ '$ref' => 'schema/two' }
]
}
refs = {
URI('http://example.com/schema/one') => {
'type' => 'integer'
},
URI('http://example.com/schema/two') => {
'minimum' => 11
}
}
schemer = JSONSchemer.schema(schema, :ref_resolver => refs.to_proc)
schemer.bundle
Options
JSONSchemer.schema(
schema,
meta_schema: 'https://json-schema.org/draft/2020-12/schema',
format: true,
formats: {
'int32' => proc do |instance, _format|
instance.is_a?(Integer) && instance.bit_length <= 32
end,
'email' => false
},
content_encodings: {
'urlsafe_base64' => proc do |instance|
[true, Base64.urlsafe_decode64(instance)]
rescue
[false, nil]
end
},
content_media_types: {
'text/csv' => proc do |instance|
[true, CSV.parse(instance)]
rescue
[false, nil]
end
},
insert_property_defaults: true,
before_property_validation: proc do |data, property, property_schema, _parent|
data[property] ||= 42
end,
after_property_validation: proc do |data, property, property_schema, _parent|
data[property] = Date.iso8601(data[property]) if property_schema.is_a?(Hash) && property_schema['format'] == 'date'
end,
ref_resolver: 'net/http',
regexp_resolver: proc do |pattern|
RE2::Regexp.new(pattern)
end,
output_format: 'basic',
access_mode: 'read'
)
Global Configuration
Configuration options can be set globally by modifying JSONSchemer.configuration. Global options are applied to any new schemas at creation time (global configuration changes are not reflected in existing schemas). They can be overridden with the regular keyword arguments described above.
JSONSchemer.configure do |config|
config.regexp_resolver = 'ecma'
end
JSONSchemer.configuration.insert_property_defaults = true
Custom Error Messages
Error messages can be customized using the x-error keyword and/or I18n translations. x-error takes precedence if both are defined.
x-error Keyword
schemer = JSONSchemer.schema({
'type' => 'string',
'x-error' => 'custom error for schema and all keywords'
})
schemer.validate(1).first
schemer.validate(1, :output_format => 'basic')
schemer = JSONSchemer.schema({
'type' => 'string',
'minLength' => 10,
'x-error' => {
'type' => 'custom error for `type` keyword',
'^' => 'custom error for schema',
'*' => 'fallback error for schema and all keywords'
}
})
schemer.validate(1).map { _1.fetch('error') }
schemer.validate('1').map { _1.fetch('error') }
schemer.validate(1, :output_format => 'basic').fetch('error')
schemer = JSONSchemer.schema({
'$id' => 'https://example.com/schema',
'properties' => {
'abc' => {
'type' => 'object',
'required' => ['xyz'],
'x-error' => <<~ERROR
instance: %{instance}
instance location: %{instanceLocation}
formatted instance location: %{formattedInstanceLocation}
keyword value: %{keywordValue}
keyword location: %{keywordLocation}
absolute keyword location: %{absoluteKeywordLocation}
details: %{details}
details__missing_keys: %{details__missing_keys}
ERROR
}
}
})
puts schemer.validate({ 'abc' => {} }).first.fetch('error')
I18n
When the I18n gem is loaded, custom error messages are looked up under the json_schemer key. It may be necessary to restart your application after adding the root key because the existence check is cached for performance reasons.
Translation keys are looked up in this order:
$LOCALE.json_schemer.errors.$ABSOLUTE_KEYWORD_LOCATION
$LOCALE.json_schemer.errors.$SCHEMA_ID.$KEYWORD_LOCATION
$LOCALE.json_schemer.errors.$KEYWORD_LOCATION
$LOCALE.json_schemer.errors.$SCHEMA_ID.$KEYWORD
$LOCALE.json_schemer.errors.$SCHEMA_ID.*
$LOCALE.json_schemer.errors.$META_SCHEMA_ID.$KEYWORD
$LOCALE.json_schemer.errors.$META_SCHEMA_ID.*
$LOCALE.json_schemer.errors.$KEYWORD
$LOCALE.json_schemer.errors.*
Example translations file:
en:
json_schemer:
errors:
'https://example.com/schema#/properties/abc/required': |
custom error for absolute keyword location
instance: %{instance}
instance location: %{instanceLocation}
formatted instance location: %{formattedInstanceLocation}
keyword value: %{keywordValue}
keyword location: %{keywordLocation}
absolute keyword location: %{absoluteKeywordLocation}
details: %{details}
details__missing_keys: %{details__missing_keys}
'https://example.com/schema':
'#/properties/abc/required': custom error for keyword location, nested under schema $id
'required': custom error for `required` keyword, nested under schema $id
'^': custom error for schema, nested under schema $id
'*': fallback error for schema and all keywords, nested under schema $id
'#/properties/abc/required': custom error for keyword location
'http://json-schema.org/draft-07/schema#':
'required': custom error for `required` keyword, nested under meta-schema $id ($schema)
'^': custom error for schema, nested under meta-schema $id
'*': fallback error for schema and all keywords, nested under meta-schema $id ($schema)
'required': custom error for `required` keyword
'^': custom error for schema
'*': fallback error for schema and all keywords
And output:
require 'i18n'
I18n.locale = :en
schemer = JSONSchemer.schema({
'$id' => 'https://example.com/schema',
'$schema' => 'http://json-schema.org/draft-07/schema#',
'properties' => {
'abc' => {
'required' => ['xyz']
}
}
})
schemer.validate({ 'abc' => {} }).first
puts schemer.validate({ 'abc' => {} }).first.fetch('error')
In the example above, custom error messsages are looked up using the following keys (in order until one is found):
en.json_schemer.errors.'https://example.com/schema#/properties/abc/required'
en.json_schemer.errors.'https://example.com/schema'.'#/properties/abc/required'
en.json_schemer.errors.'#/properties/abc/required'
en.json_schemer.errors.'https://example.com/schema'.required
en.json_schemer.errors.'https://example.com/schema'.*
en.json_schemer.errors.'http://json-schema.org/draft-07/schema#'.required
en.json_schemer.errors.'http://json-schema.org/draft-07/schema#'.*
en.json_schemer.errors.required
en.json_schemer.errors.*
OpenAPI
document = JSONSchemer.openapi({
'openapi' => '3.1.0',
'info' => {
'title' => 'example'
},
'components' => {
'schemas' => {
'example' => {
'type' => 'integer'
}
}
}
})
document.valid?
document.validate.to_a
document.schema('example').valid?(1)
document.schema('example').valid?('one')
document.ref('#/components/schemas/example').valid?(1)
document.ref('#/components/schemas/example').valid?('one')
CLI
The json_schemer executable takes a JSON schema file as the first argument followed by one or more JSON data files to validate. If there are any validation errors, it outputs them and returns an error code.
Validation errors are output as single-line JSON objects. The --errors option can be used to limit the number of errors returned or prevent output entirely (and fail fast).
The schema or data can also be read from stdin using -.
% json_schemer --help
Usage:
json_schemer [options] <schema> <data>...
json_schemer [options] <schema> -
json_schemer [options] - <data>...
json_schemer -h | --help
json_schemer --version
Options:
-e, --errors MAX Maximum number of errors to output
Use "0" to validate with no output
-h, --help Show help
-v, --version Show version
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
Build Status


Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/davishmcclurg/json_schemer.
License
The gem is available as open source under the terms of the MIT License.