View Source Xema (xema v0.16.0)

A schema validator inspired by JSON Schema.

All available keywords to construct a schema are described on page Usage.

This module can be used to construct a schema module. Should a module contain multiple schemas the option multi: true is required.

use Xema imports Xema.Builder and extends the module with the functions

  • __MODULE__.valid?/2
  • __MODULE__.validate/2
  • __MODULE__.validate!/2
  • __MODULE__.cast/2
  • __MODULE__.cast!/2
  • __MODULE__.xema/1

The macro xema/2 supports the construction of a schema. After that the schema is available via the functions above.

In a multi schema module a schema can be tagged with the option default: :schema_name and then called by

  • __MODULE__.valid?/1
  • __MODULE__.validate/1
  • __MODULE__.validate!/1
  • __MODULE__.cast/1
  • __MODULE__.cast!/1
  • __MODULE__.xema/0

The functions with arity 1 are also available for single schema modules.

The macro xema_struct/1 creates a schema with the coresponding struct.

examples

Examples

Single schema module:

iex> defmodule SingleSchema do
...>   use Xema
...>
...>   # The name :num is optional.
...>   xema :num, do: number(minimum: 1)
...> end
iex>
iex> SingleSchema.valid?(:num, 6)
true
iex> SingleSchema.valid?(5)
true
iex> SingleSchema.validate(0)
{:error, %Xema.ValidationError{
   reason: %{minimum: 1, value: 0}
}}
iex> SingleSchema.cast("5")
{:ok, 5}
iex> SingleSchema.cast("-5")
{:error, %Xema.ValidationError{
   reason: %{minimum: 1, value: -5}
}}

Multi schema module:

iex> defmodule Schema do
...>   use Xema, multi: true, default: :user
...>
...>   @pos integer(minimum: 0)
...>   @neg integer(maximum: 0)
...>
...>   xema :user do
...>     map(
...>       properties: %{
...>         name: string(min_length: 1),
...>         age: @pos
...>       }
...>     )
...>   end
...>
...>   xema :nums do
...>     map(
...>       properties: %{
...>         pos: list(items: @pos),
...>         neg: list(items: @neg)
...>       }
...>     )
...>   end
...> end
iex>
iex> Schema.valid?(:user, %{name: "John", age: 21})
true
iex> Schema.valid?(%{name: "John", age: 21})
true
iex> Schema.valid?(%{name: "", age: 21})
false
iex> Schema.validate(%{name: "John", age: 21})
:ok
iex> Schema.validate(%{name: "", age: 21})
{:error, %Xema.ValidationError{
  reason: %{
    properties: %{name: %{min_length: 1, value: ""}}}
  }
}
iex> Schema.valid?(:nums, %{pos: [1, 2, 3]})
true
iex> Schema.valid?(:nums, %{neg: [1, 2, 3]})
false

Struct schema module:

iex> defmodule StructA do
...>   use Xema
...>
...>   xema_struct do
...>     field :foo, :integer, minimum: 0
...>   end
...> end
...>
...> defmodule StructB do
...>   use Xema
...>
...>   xema_struct do
...>     field :a, :string, min_length: 3
...>     field :b, StructA
...>     required [:a]
...>   end
...> end
...>
...> data = StructB.cast!(a: "abc", b: %{foo: 5})
...> data.a
"abc"
iex> Map.from_struct(data.b)
%{foo: 5}

For more examples to construct schemas see "Examples".

Link to this section Summary

Types

The return type of a validation run.

t()

This struct contains the schema and references of the schema.

Functions

Converts the given data using the specified schema. Returns the converted data or an exception.

Converts the given data using the specified schema. Returns {:ok, result} or {:error, reason}. The result is converted and validated with the schema.

Creates a Xema from a JSON Schema. The argument json_schema is expected as a decoded JSON Schema.

This function creates the schema from the given data.

Returns the source for a given xema. The output can differ from the input if the schema contains references. To get the original source the schema must be created with inline: false.

Returns true if the value is a valid value against the given schema; otherwise returns false.

Returns :ok if the value is a valid value against the given schema; otherwise raises a Elixir.Xema.ValidationError. See validate3 for available options.

Returns :ok if the value is a valid value against the given schema; otherwise returns an error tuple.

Link to this section Types

@type result() :: Xema.Validator.result()

The return type of a validation run.

@type schema() :: Xema.Schema.t()
@type t() :: %Xema{refs: map(), schema: Xema.Schema.t()}

This struct contains the schema and references of the schema.

Link to this section Functions

Link to this function

cast!(xema, value, opts \\ [])

View Source

Converts the given data using the specified schema. Returns the converted data or an exception.

Link to this function

cast(xema, value, opts \\ [])

View Source

Converts the given data using the specified schema. Returns {:ok, result} or {:error, reason}. The result is converted and validated with the schema.

examples

Examples:

iex> schema = Xema.new({:integer, minimum: 1})
iex> Xema.cast(schema, "5")
{:ok, 5}
iex> Xema.cast(schema, "five")
{:error, %Xema.CastError{
  key: nil,
  path: [],
  to: :integer,
  value: "five"
}}
iex> Xema.cast(schema, "0")
{:error, %Xema.ValidationError{
  reason: %{minimum: 1, value: 0}
}}

multiple-types

Multiple types

If for a value multiple types are defined the function used the result of the first successful conversion.

examples-1

Examples

iex> schema = Xema.new([:integer, :string, nil])
iex> Xema.cast(schema, 5)
{:ok, 5}
iex> Xema.cast(schema, 5.5)
{:ok, "5.5"}
iex> Xema.cast(schema, "5")
{:ok, 5}
iex> Xema.cast(schema, "five")
{:ok, "five"}
iex> Xema.cast(schema, nil)
{:ok, nil}
iex> Xema.cast(schema, [5])
{:error,
  %Xema.CastError{path: [], to: [:integer, :string, nil], value: [5]}
}

cast-with-any_of-all_of-and-one_of

Cast with any_of, all_of, and one_of

Schemas in a combiner will be cast independently one by one in reverse order.

examples-2

Examples

iex> schema = Xema.new(any_of: [
...>   [properties: %{a: :integer}],
...>   [properties: %{a: :string}]
...> ])
iex> Xema.cast(schema, %{a: 5})
{:ok, %{a: 5}}
iex> Xema.cast(schema, %{a: 5.5})
{:ok, %{a: "5.5"}}
iex> Xema.cast(schema, %{a: "5"})
{:ok, %{a: 5}}
iex> Xema.cast(schema, %{a: "five"})
{:ok, %{a: "five"}}
iex> Xema.cast(schema, %{a: [5]})
{:error,
  %Xema.CastError{
    error: nil,
    key: nil,
    message: nil,
    path: [],
    to: [
      %{path: [:a], to: :integer, value: [5]},
      %{path: [:a], to: :string, value: [5]}
    ],
    value: %{a: [5]}
}}

options

Options

With the option additional_properties: :delete additional properties will be deleted on cast. Additional properties will be deleted in schemas with additional_properties: false.

examples-3

Examples

iex> schema = Xema.new(
...>   properties: %{
...>     a: [
...>       properties: %{
...>         foo: :integer
...>       },
...>       additional_properties: false
...>     ],
...>     b: [
...>       properties: %{
...>         foo: :integer
...>       }
...>     ]
...>   }
...> )
iex>
iex> Xema.cast(schema, %{
...>   a: %{foo: "6", bar: "7"},
...>   b: %{foo: "6", bar: "7"},
...> }, additional_properties: :delete)
{:ok, %{
  a: %{foo: 6},
  b: %{foo: 6, bar: "7"}
}}
Link to this function

from_json_schema(json_schema, opts \\ [])

View Source
@spec from_json_schema(
  atom() | map(),
  keyword()
) :: t()

Creates a Xema from a JSON Schema. The argument json_schema is expected as a decoded JSON Schema.

All keys that are not standard JSON Schema keywords have to be known atoms. If the schema has additional keys that are unknown atoms the option atom: :force is needed. In this case the atoms will be created. This is not needed for keys expected by JSON Schema (e.g. in properties)

Options:

  • :draft specifies the draft to check the given JSON Schema. Possible values are "draft4", "draft6", and "draft7", default is "draft7". If :draft not set and the schema contains $schema then the value for $schema is used for this option.
  • :atoms creates atoms for unknown atoms when set to :force. This is just needed for additional JSON Schema keywords.

examples

Examples

iex> Xema.from_json_schema(%{"type" => "integer", "minimum" => 5})
%Xema{schema: %Xema.Schema{minimum: 5, type: :integer}}

iex> schema = %{
...>   "type" => "object",
...>   "properties" => %{"foo" => %{"type" => "integer"}}
...> }
iex> Xema.from_json_schema(schema)
%Xema{schema:
  %Xema.Schema{
    properties: %{"foo" => %Xema.Schema{type: :integer}},
    type: :map,
    keys: :strings
  }
}

iex> Xema.from_json_schema(%{"type" => "integer", "foo" => "bar"}, atom: :force)
%Xema{schema: %Xema.Schema{data: %{foo: "bar"}, type: :integer}}

iex> Xema.from_json_schema(%{"exclusiveMaximum" => 5}, draft: "draft7")
%Xema{schema: %Xema.Schema{exclusive_maximum: 5}}

iex> Xema.from_json_schema(%{"exclusiveMaximum" => 5}, draft: "draft4")
** (Xema.SchemaError) Can't build schema:
Dependencies for "exclusiveMaximum" failed. Missing required key "maximum".
@spec new(
  Xema.Schema.t() | Xema.Schema.type() | tuple() | atom() | keyword(),
  keyword()
) :: t()

This function creates the schema from the given data.

Possible options:

  • :loader - a loader for remote schemas. This option will overwrite the

            loader from the config.
            See [Configure a loader](loader.html) to how to define a loader.
  • inline - inlined all references in the schema. Default :true.

examples

Examples

Simple schema:

iex> schema = Xema.new :string
iex> Xema.valid? schema, "hello"
true
iex> Xema.valid? schema, 42
false

Schema:

iex> schema = Xema.new {:string, min_length: 3, max_length: 12}
iex> Xema.valid? schema, "hello"
true
iex> Xema.valid? schema, "hi"
false

Nested schemas:

iex> schema = Xema.new {:list, items: {:number, minimum: 2}}
iex> Xema.validate(schema, [2, 3, 4])
:ok
iex> Xema.valid?(schema, [2, 3, 4])
true
iex> Xema.validate(schema, [2, 3, 1])
{:error, %Xema.ValidationError{
  reason: %{
    items: %{2 => %{value: 1, minimum: 2}}}
  }
}

More examples can be found on page Usage.

@spec source(t() | Xema.Schema.t()) :: atom() | keyword() | {atom(), keyword()}

Returns the source for a given xema. The output can differ from the input if the schema contains references. To get the original source the schema must be created with inline: false.

examples

Examples

iex> {:integer, minimum: 1} |> Xema.new() |> Xema.source()
{:integer, minimum: 1}
@spec valid?(t() | schema(), any()) :: boolean()

Returns true if the value is a valid value against the given schema; otherwise returns false.

Link to this function

validate!(xema, value, opts \\ [])

View Source
@spec validate!(t() | schema(), any(), keyword()) :: :ok

Returns :ok if the value is a valid value against the given schema; otherwise raises a Elixir.Xema.ValidationError. See validate3 for available options.

Link to this function

validate(schema, value, opts \\ [])

View Source
@spec validate(t() | schema(), any(), keyword()) :: result()

Returns :ok if the value is a valid value against the given schema; otherwise returns an error tuple.

With the option :fail, you can define when the validation is aborted. This also influences how many error reasons are returned.

  • :immediately aborts the validation when the first validation fails.
  • :early (default) aborts on failed validations, but runs validations for all properties and items.
  • :finally aborts after all possible validations.