JSV

hex.pm VersionBuild StatusLicense

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

Documentation

Comprehensive guides and API documentation are available on hexdocs.pm.

Supported Dialects

JSV supports 100% of features from Draft 2020-12 and Draft 7 as verified by the JSON Schema Compliance Test Suite.

Installation

Add jsv to your mix.exs:

def deps do
  [
    {:jsv, "~> 0.18"},
  ]
end

Optional Dependencies

JSV 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"}
  ]
end

Usage

Simple Validation

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

Module-Based Schemas

Define 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)

Pydantic style modules

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
}

Contributing

Please ensure your changes include thorough tests and follow the existing documentation style.

License

JSV is released under the MIT License.