ex_json_schema
0.6.1 with this PR
A JSON Schema validator with full support for the draft 4 specification and zero dependencies. Passes the official JSON Schema Test Suite.
Add the project to your Mix dependencies in mix.exs
:
defp deps do
[{:ex_json_schema, git: "https://github.com/gumi/ex_json_schema.git", tag: "v0.6.2-hotfix"}]
end
Update your dependencies with:
$ mix deps.get
If you have remote schemata that need to be fetched at runtime, you have to register a function that takes a URL and returns a Map
of the parsed JSON. So in your Mix configuration in config/config.exs
you should have something like this:
config :ex_json_schema,
:remote_schema_resolver,
fn url -> HTTPoison.get!(url).body |> Poison.decode! end
Alternatively, you can specify a module and function name for situations where using anonymous functions is not possible (i.e. working with Erlang releases):
config :ex_json_schema,
:remote_schema_resolver,
{MyModule, :my_resolver}
You do not have to do that for the official draft 4 meta-schema found at http://json-schema.org/draft-04/schema# though. That schema is bundled with the project and will work out of the box without any network calls.
In this step the schema is validated against its meta-schema (the draft 4 schema definition) and $ref
s are being resolved (making sure that the reference points to an existing fragment). You should only resolve a schema once to avoid the overhead of resolving it in every validation call.
schema = %{
"type" => "object",
"properties" => %{
"foo" => %{
"type" => "string"
}
}
} |> ExJsonSchema.Schema.resolve()
Note that Map
keys are expected to be strings, since in practice that data will always come from some JSON parser.
If you're only interested in whether a piece of data is valid according to the schema:
iex> ExJsonSchema.Validator.valid?(schema, %{"foo" => "bar"})
true
iex> ExJsonSchema.Validator.valid?(schema, %{"foo" => 1})
false
Or in case you want to have detailed validation errors:
iex> ExJsonSchema.Validator.validate(schema, %{"foo" => "bar"})
:ok
iex> ExJsonSchema.Validator.validate(schema, %{"foo" => 1})
{:error, [{"Type mismatch. Expected String but got Integer.", "#/foo"}]}
By default, errors are formatted using a string formatter that returns errors as tuples of error message and path. If you want to get raw validation error structs, you can pass the following option:
iex> ExJsonSchema.Validator.validate(schema, %{"foo" => 1}, error_formatter: false)
{:error,
[
%ExJsonSchema.Validator.Error{
error: %ExJsonSchema.Validator.Error.Type{
actual: "integer",
expected: ["string"]
},
path: "#/foo"
}
]}
You can also pass your own custom error formatter as a module that implements a format/1
function that takes a list of raw errors via the same option:
defmodule MyFormatter do
def format(errors) do
Enum.map(errors, fn %ExJsonSchema.Validator.Error{error: error, path: path} ->
{error.__struct__, path}
end)
end
end
iex> ExJsonSchema.Validator.validate(schema, %{"foo" => 1}, error_formatter: MyFormatter)
{:error, [{ExJsonSchema.Validator.Error.Type, "#/foo"}]}
It is also possible to validate against a subset of the schema by providing either a fragment:
iex> fragment = ExJsonSchema.Schema.get_fragment!(schema, "#/properties/foo")
%{"type" => "string"}
iex> ExJsonSchema.Validator.valid_fragment?(schema, fragment, "bar")
true
iex> ExJsonSchema.Validator.validate_fragment(schema, fragment, "bar")
:ok
or a path:
iex> ExJsonSchema.Validator.valid_fragment?(schema, "#/foo", "bar")
true
The validator supports all the formats specified by draft 4 (date-time
, email
, hostname
, ipv4
, ipv6
), with the exception of the uri
format which has confusing/broken requirements in the official test suite (see json-schema-org/JSON-Schema-Test-Suite#77).
Released under the MIT license.
- Add some source code documentation
- Enable providing JSON for known schemata at resolve time