# JSV
<!-- rdmx :badges
hexpm : "jsv?color=4e2a8e"
github_action : "lud/jsv/elixir.yaml?label=CI&branch=main"
license : jsv
-->
[](https://hex.pm/packages/jsv)
[](https://github.com/lud/jsv/actions/workflows/elixir.yaml?query=branch%3Amain)
[](https://hex.pm/packages/jsv)
<!-- rdmx /:badges -->
**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.
## Key Features
- **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 `defschema` to 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`, and
`Decimal`.
- Extensible casting system using the `jsv-cast` keyword and `defcast` macros.
- **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](https://github.com/lud/oaskit)**: Oaskit is an
OpenAPI 3.1 validator for Phoenix and is entirely built on JSV.
## Documentation
[Comprehensive guides and API documentation are available on hexdocs.pm](https://hexdocs.pm/jsv/).
## Supported Dialects
JSV supports 100% of features from Draft 2020-12 and Draft 7 as verified by the
[JSON Schema Compliance Test Suite](https://bowtie.report/).
* [](https://bowtie.report/#/implementations/elixir-jsv)
* [](https://bowtie.report/#/implementations/elixir-jsv)
## Installation
Add `jsv` to your `mix.exs`:
<!-- rdmx :app_dep vsn:$app_vsn -->
```elixir
def deps do
[
{:jsv, "~> 0.15"},
]
end
```
<!-- rdmx /:app_dep -->
### Optional Dependencies
JSV integrates with popular Elixir libraries to provide enhanced functionality:
```elixir
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"}
]
end
```
## Usage
### Simple Validation
<!-- rdmx :section format:true name:"simple-validation" -->
```elixir
schema = %{
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))
end
```
<!-- rdmx /:section -->
### Module-Based Schemas
Define your business objects and validation logic in one place:
<!-- rdmx :section format:true name:"module-based" -->
```elixir
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)
```
<!-- rdmx /:section -->
### Pydantic style modules
<!-- rdmx :section format:true name:"pydantic" -->
```elixir
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)
```
<!-- rdmx /:section -->
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):
<!-- rdmx :eval section:pydantic -->
```elixir
%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
}
```
<!-- rdmx /:eval -->
## Contributing
Please ensure your changes include thorough tests and follow the existing
documentation style.
## License
JSV is released under the [MIT License](LICENSE).