# `JSONSchex`
[🔗](https://github.com/xinz/jsonschex/blob/main/lib/jsonschex.ex#L1)

JSON Schema Draft 2020-12 validator for Elixir.

Compile a schema once, then validate data against it repeatedly:

    iex> schema = %{"type" => "integer", "minimum" => 10}
    iex> {:ok, compiled} = JSONSchex.compile(schema)
    iex> JSONSchex.validate(compiled, 15)
    :ok
    iex> {:error, [error]} = JSONSchex.validate(compiled, 5)
    iex> error.rule
    :minimum
    iex> JSONSchex.format_error(error)
    "Value 5 is less than minimum 10"

With format assertion:

    iex> schema = %{"type" => "string", "format" => "email"}
    iex> {:ok, compiled} = JSONSchex.compile(schema, format_assertion: true)
    iex> JSONSchex.validate(compiled, "user@example.com")
    :ok
    iex> {:error, [error]} = JSONSchex.validate(compiled, "not-an-email")
    iex> error.rule
    :format
    iex> JSONSchex.format_error(error)
    "Format mismatch: email"

# `compile`

Compiles a raw JSON Schema into a reusable `Schema` struct.

## Options

- `:external_loader` — `(uri -> {:ok, map()} | {:error, term()})` for remote `$ref` schemas
- `:base_uri` — Starting base URI for resolving relative references
- `:format_assertion` — Enable strict `format` validation (default: `false`)
- `:content_assertion` — Enable strict content vocabulary validation (default: `false`)

See the [Loader guide](guide/loader.md) and
[Content and Format guide](guide/content_and_format.md) for details.

## Examples

    iex> {:ok, schema} = JSONSchex.compile(%{"type" => "string"})
    iex> is_struct(schema, JSONSchex.Types.Schema)
    true

    iex> {:ok, schema} = JSONSchex.compile(%{"type" => "string", "format" => "email"}, format_assertion: true)
    iex> JSONSchex.validate(schema, "test@example.com")
    :ok

# `format_error`

```elixir
@spec format_error(JSONSchex.Types.Error.t() | JSONSchex.Types.CompileError.t()) ::
  String.t()
```

Formats a validation error into a human-readable string.

## Examples

    iex> error = %JSONSchex.Types.Error{path: ["age", "user"], rule: :minimum, context: %{minimum: 0, actual: -5}}
    iex> JSONSchex.format_error(error)
    "At /user/age: Value -5 is less than minimum 0"

    iex> {:error, error} = JSONSchex.compile(%{"type" => "1"})
    iex> JSONSchex.format_error(error)
    ~s(Keyword 'type' must be one of [string, integer, number, boolean, object, array, null], got: "1")

    iex> {:error, error} = JSONSchex.compile(%{"minimum" => "five"})
    iex> JSONSchex.format_error(error)
    ~s(Keyword 'minimum' must be a number, got: "five")

    iex> {:error, error} = JSONSchex.compile(%{"multipleOf" => -3})
    iex> JSONSchex.format_error(error)
    ~s(Keyword 'multipleOf' must be a strictly positive number, got: -3)

    iex> {:error, error} = JSONSchex.compile(%{"minLength" => -1})
    iex> JSONSchex.format_error(error)
    ~s(Keyword 'minLength' must be a non-negative integer, got: -1)

    iex> {:error, error} = JSONSchex.compile(%{"uniqueItems" => "yes"})
    iex> JSONSchex.format_error(error)
    ~s(Keyword 'uniqueItems' must be a boolean, got: "yes")

# `validate`

```elixir
@spec validate(JSONSchex.Types.Schema.t(), term()) ::
  :ok | {:error, [JSONSchex.Types.Error.t()]}
```

Validates data against a compiled schema.

Returns `:ok` or `{:error, [Error.t()]}`. Use `JSONSchex.format_error/1` on
individual errors to produce human-readable messages.

## Examples

    iex> {:ok, schema} = JSONSchex.compile(%{"type" => "integer"})
    iex> JSONSchex.validate(schema, 42)
    :ok

    iex> {:ok, schema} = JSONSchex.compile(%{"type" => "integer"})
    iex> {:error, errors} = JSONSchex.validate(schema, "not an integer")
    iex> [error] = errors
    iex> error.rule
    :type
    iex> error.path
    []
    iex> JSONSchex.format_error(error)
    "Expected type \"integer\", got \"string\""

---

*Consult [api-reference.md](api-reference.md) for complete listing*
