JSV is a JSON Schema validator for Elixir, designed for modern applications. It provides full compliance with the latest JSON Schema specifications while offering a seamless, Elixir-native developer experience.
- Full Specification Compliance: 100% support for Draft 2020-12
(including
$dynamicRef,$dynamicAnchor, and all vocabularies) and Draft 7. - High Performance: Schemas can be pre-compiled into an optimized internal representation. Build your validation roots at compile-time for near-zero runtime overhead.
- Elixir-Native Schemas: Use
defschemato define schemas as Elixir modules. Supports automatic casting to Elixir structs with default values. - Advanced Casting & Transformation:
- Built-in support for casting to
Date,DateTime,Duration, andDecimal. - Extensible casting system using the
jsv-castkeyword anddefcastmacros.
- Built-in support for casting to
- Extensible Resolution: Fetch remote schemas via HTTP or resolve them from local files and directories using custom or built-in resolvers.
- Flexible Workflows: Supports schemas as atoms or binaries, from Elixir code, JSON files, or the network. JSV supports many ways to work with schemas.
- Extensible Vocabularies: Thanks to features of Draft 2020-12, custom meta-schemas with custom schema keywords are supported out of the box.
- Functional Builder API: Compose schemas dynamically using a functional API.
- Rich Error Handling: Detailed validation errors that can be easily normalized into JSON-compatible structures for API responses.
- Complete support in Oaskit: Oaskit is an OpenAPI 3.1 validator for Phoenix and is entirely built on JSV.
Comprehensive guides and API documentation are available on hexdocs.pm.
JSV supports 100% of features from Draft 2020-12 and Draft 7 as verified by the JSON Schema Compliance Test Suite.
Add jsv to your mix.exs:
def deps do
[
{:jsv, "~> 0.15"},
]
endJSV integrates with popular Elixir libraries to provide enhanced functionality:
def deps do
[
# JSV Supports Decimal and will validate Decimal structs as numbers.
{:decimal, "~> 2.0"},
# Required for resolving schemas via HTTP on Elixir < 1.18.
{:jason, "~> 1.0"}, # OR {:poison, "~> 6.0"}
]
endschema = %{
type: :object,
properties: %{
name: %{type: :string}
},
required: [:name]
}
root = JSV.build!(schema)
case JSV.validate(%{"name" => "Alice"}, root) do
# %{"name" => "Alice"}
{:ok, data} -> IO.inspect(data)
{:error, err} -> IO.inspect(JSV.normalize_error(err))
endDefine your business objects and validation logic in one place:
defmodule MyApp.User do
use JSV.Schema
defschema %{
type: :object,
properties: %{
name: string(minLength: 1),
age: integer(minimum: 0, default: 18)
},
required: [:name]
}
end
# Build at compile-time for maximum performance
root = JSV.build!(MyApp.User)
# Casting to structs is enabled by default
%MyApp.User{name: "Alice", age: 18} = JSV.validate!(%{"name" => "Alice"}, root)use JSV.Schema
defschema MyApp.Data.Food,
~SD"""
A Tasty dish, hopefully
""",
name: string(),
origin: string()
defschema MyApp.Data.Profile,
~SD"""
Information about a user profile
""",
name: string(),
birthdate: optional(date()),
favorite_food: MyApp.Data.Food
defschema MyApp.Data.User,
~SD"""
System user information
""",
profile: MyApp.Data.Profile,
role: string_enum_to_atom([:admin, :writer, :reader])
data = %{
"profile" => %{
"name" => "Alice",
"birthdate" => "1994-01-08",
"favorite_food" => %{
"name" => "Pad Thai",
"origin" => "Thailand"
}
},
"role" => "admin"
}
root = JSV.build!(MyApp.Data.User, formats: true)
JSV.validate!(data, root, cast_formats: true)With this simple module form you can define many struct schemas in a compact way. The code above will cast the data (and the birthdate as well):
%MyApp.Data.User{
profile: %MyApp.Data.Profile{
birthdate: ~D[1994-01-08],
favorite_food: %MyApp.Data.Food{name: "Pad Thai", origin: "Thailand"},
name: "Alice"
},
role: :admin
}Please ensure your changes include thorough tests and follow the existing documentation style.
JSV is released under the MIT License.