Skip to content

lud/jsv

Repository files navigation

JSV

hex.pm Version Build Status License

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: 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.

Supported Dialects

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

  • Draft 2020-12
  • Draft 7

Installation

Add jsv to your mix.exs:

def deps do
  [
    {:jsv, "~> 0.15"},
  ]
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.

About

Json Schema Validator for Elixir with full spec support

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages