Xema (xema v0.13.6) View Source

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 @default true 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.

Examples

Sinlge 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
...>
...>   @pos integer(minimum: 0)
...>   @neg integer(maximum: 0)
...>
...>   @default true
...>   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 do
...>     field :foo, :integer, minimum: 0
...>   end
...> end
...>
...> defmodule StructB do
...>   use Xema
...>
...>   xema 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 {:ok, result} or {:error, reason}. The result is converted and validated with the schema.

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

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 returns an error tuple.

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 section Types

Specs

result() :: Xema.Validator.result()

The return type of a validation run.

Specs

schema() :: Xema.Schema.t()

Specs

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 {:ok, result} or {:error, reason}. The result is converted and validated with the schema.

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

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

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

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

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

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

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

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

from_json_schema(json_schema, opts \\ [])

View Source

Specs

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

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
  }
}

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

Specs

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

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.

Specs

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

iex> {:integer, minimum: 1} |> Xema.new() |> Xema.source()
{:integer, minimum: 1}

Specs

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(schema, value, opts \\ [])

View Source

Specs

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.
Link to this function

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

View Source

Specs

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.