View Source Usage
This page describes all keywords that are available for creating a schema.
All schemas will construct in the "raw" format. The Xema.Builder provides some
convenience functions to create schemas. The recommended method to construct a
schema is use Xema described in the module documentation of Xema.
Type any
The schema any will accept any data.
iex> schema = Xema.new :any
iex> Xema.validate schema, 42
:ok
iex> Xema.validate schema, "foo"
:ok
iex> Xema.validate schema, nil
:okType nil
The nil type matches only nil.
iex> schema = Xema.new :nil
iex> Xema.validate schema, nil
:ok
iex> {:error, error} = Xema.validate schema, 0
{:error, %Xema.ValidationError{
reason: %{type: :nil, value: 0}
}}
iex> Exception.message(error)
"Expected nil, got 0."Type boolean
The boolean type matches only true and false.
iex> schema = Xema.new :boolean
iex> Xema.validate schema, true
:ok
iex> Xema.valid? schema, false
true
iex> {:error, error} = Xema.validate schema, 0
{:error, %Xema.ValidationError{
reason: %{type: :boolean, value: 0}
}}
iex> Exception.message(error)
"Expected :boolean, got 0."
iex> Xema.valid? schema, nil
falseType atom
The atom type matches only atoms. Schemas of type atom match also the atoms
true, false, and nil.
iex> schema = Xema.new :atom
iex> Xema.validate schema, :foo
:ok
iex> Xema.valid? schema, "foo"
false
iex> {:error, error} = Xema.validate schema, 0
{:error, %Xema.ValidationError{
reason: %{type: :atom, value: 0}
}}
iex> Exception.message(error)
"Expected :atom, got 0."
iex> Xema.valid? schema, nil
true
iex> Xema.valid? schema, false
trueType string
JSON Schema Draft: 4/6/7
The string type is used for strings.
iex> schema = Xema.new :string
iex> Xema.validate schema, "José"
:ok
iex> {:error, error} = Xema.validate schema, 42
{:error, %Xema.ValidationError{
reason: %{type: :string, value: 42}
}}
iex> Exception.message(error)
"Expected :string, got 42."
iex> Xema.valid? schema, "José"
true
iex> Xema.valid? schema, 42
falseLength
The length of a string can be constrained using the min_length and max_length
keywords. For both keywords, the value must be a non-negative number.
iex> schema = Xema.new {:string, min_length: 2, max_length: 3}
iex> {:error, error} = Xema.validate schema, "a"
{:error, %Xema.ValidationError{
reason: %{value: "a", min_length: 2}
}}
iex> Exception.message(error)
~s|Expected minimum length of 2, got "a".|
iex> Xema.validate schema, "ab"
:ok
iex> Xema.validate schema, "abc"
:ok
iex> {:error, error} = Xema.validate schema, "abcd"
{:error, %Xema.ValidationError{
reason: %{value: "abcd", max_length: 3}
}}
iex> Exception.message(error)
~s|Expected maximum length of 3, got "abcd".|Regular Expression
The pattern keyword is used to restrict a string to a particular regular
expression.
iex> schema = Xema.new {:string, pattern: ~r/[0-9]-[A-B]+/}
iex> Xema.validate schema, "1-AB"
:ok
iex> {:error, error} = Xema.validate schema, "foo"
{:error, %Xema.ValidationError{
reason: %{value: "foo", pattern: ~r/[0-9]-[A-B]+/}
}}
iex> Exception.message(error)
~s|Pattern ~r/[0-9]-[A-B]+/ does not match value "foo".|The regular expression can also be a string.
iex> schema = Xema.new {:string, pattern: "[0-9]-[A-B]+"}
iex> Xema.validate schema, "1-AB"
:ok
iex> {:error, error} = Xema.validate schema, "foo"
{:error, %Xema.ValidationError{
reason: %{value: "foo", pattern: ~r/[0-9]-[A-B]+/}
}}
iex> Exception.message(error)
~s|Pattern ~r/[0-9]-[A-B]+/ does not match value "foo".|Format
JSON Schema Draft: 4/6/7
Basic semantic validation of strings.
:date_timevalidation as defined by RFC 3339iex> schema = Xema.new {:string, format: :date_time} iex> Xema.valid? schema, "today" false iex> Xema.valid? schema, "1963-06-19T08:30:06.283185Z" true:emailvalidation as defined by RFC 5322iex> {:string, format: :email} ...> |> Xema.new() ...> |> Xema.valid?("marion.mustermann@otto.net") true:hostchecks if thestringis an valid IPv4, IPv6, or hostname.:hostnamevalidation as defined by RFC 1034:ipv4validation as defined by RFC 2673:ipv6validation as defined by RFC 2373:urivalidation as defindex by RFC 3986:uri_fragment:uri_path:uri_query:uri_userinfo
JSON Schema Draft: -/-/7
:regexchecks if thestringis a valid regular expression.
Types number, integer and float
There are three numeric types in Xema: number, integer and float. They
share the same validation keywords.
The number type is used for numbers.
iex> schema = Xema.new :number
iex> Xema.validate schema, 42
:ok
iex> Xema.validate schema, 21.5
:ok
iex> {:error, error} = Xema.validate schema, "foo"
{:error, %Xema.ValidationError{
reason: %{type: :number, value: "foo"}
}}
iex> Exception.message(error)
~s|Expected :number, got "foo".|The integer type is used for integral numbers.
iex> schema = Xema.new :integer
iex> Xema.validate schema, 42
:ok
iex> {:error, error} = Xema.validate schema, 21.5
{:error, %Xema.ValidationError{
reason: %{type: :integer, value: 21.5}
}}
iex> Exception.message(error)
"Expected :integer, got 21.5."The float type is used for floating point numbers.
iex> schema = Xema.new :float
iex> {:error, error} = Xema.validate schema, 42
{:error, %Xema.ValidationError{
reason: %{type: :float, value: 42}
}}
iex> Exception.message(error)
"Expected :float, got 42."
iex> Xema.validate schema, 21.5
:okMultiples
JSON Schema Draft: 4/6/7
Numbers can be restricted to a multiple of a given number, using the
multiple_of keyword. It may be set to any positive number.
iex> schema = Xema.new {:number, multiple_of: 2}
iex> Xema.validate schema, 8
:ok
iex> {:error, error} = Xema.validate schema, 7
{:error, %Xema.ValidationError{
reason: %{value: 7, multiple_of: 2}
}}
iex> Exception.message(error)
"Value 7 is not a multiple of 2."
iex> Xema.valid? schema, 8.0
trueRange
JSON Schema Draft: 4/-/-
Ranges of numbers are specified using a combination of the minimum, maximum,
exclusive_minimum and exclusive_maximum keywords.
minimumspecifies a minimum numeric value.exclusive_minimumis a boolean. When true, it indicates that the range excludes the minimum value, i.e., x > minx > min. When false (or not included), it indicates that the range includes the minimum value, i.e., x≥minx≥min.maximumspecifies a maximum numeric value.exclusive_maximumis a boolean. When true, it indicates that the range excludes the maximum value, i.e., x < maxx < max. When false (or not included), it indicates that the range includes the maximum value, i.e., x ≤ maxx ≤ max.
iex> schema = Xema.new {
...> :float,
...> minimum: 1.2, maximum: 1.4, exclusive_maximum: true
...> }
iex> {:error, error} = Xema.validate schema, 1.1
{:error, %Xema.ValidationError{
reason: %{value: 1.1, minimum: 1.2}
}}
iex> Exception.message(error)
"Value 1.1 is less than minimum value of 1.2."
iex> Xema.validate schema, 1.2
:ok
iex> Xema.valid? schema, 1.3
true
iex> {:error, error} = Xema.validate schema, 1.4
{:error, %Xema.ValidationError{
reason: %{value: 1.4, maximum: 1.4, exclusive_maximum: true}
}}
iex> Exception.message(error)
"Value 1.4 equals exclusive maximum value of 1.4."
iex> {:error, error} = Xema.validate schema, 1.5
{:error, %Xema.ValidationError{
reason: %{value: 1.5, maximum: 1.4, exclusive_maximum: true}
}}
iex> Exception.message(error)
"Value 1.5 exceeds maximum value of 1.4."JSON Schema Draft: 6/7
The keywords exclusive_maximum and exclusive_minimum changed from a boolean
to a number. Wherever one of these would be true before, they have now the
value of the corresponding keyword maximum or minimum. The keyword
maximum/minimum can be removed.
iex> schema = Xema.new {:float, minimum: 1.2, exclusive_maximum: 1.4}
iex> {:error, error} = Xema.validate schema, 1.1
{:error, %Xema.ValidationError{
reason: %{value: 1.1, minimum: 1.2}
}}
iex> Exception.message(error)
"Value 1.1 is less than minimum value of 1.2."
iex> Xema.validate schema, 1.2
:ok
iex> Xema.valid? schema, 1.3
true
iex> {:error, error} = Xema.validate schema, 1.4
{:error, %Xema.ValidationError{
reason: %{value: 1.4, exclusive_maximum: 1.4}
}}
iex> Exception.message(error)
"Value 1.4 equals exclusive maximum value of 1.4."
iex> {:error, error} = Xema.validate schema, 1.5
{:error, %Xema.ValidationError{
reason: %{value: 1.5, exclusive_maximum: 1.4}
}}
iex> Exception.message(error)
"Value 1.5 exceeds maximum value of 1.4."Type list
List are used for ordered elements, each element may be of a different type.
iex> schema = Xema.new :list
iex> Xema.valid? schema, [1, "two", 3.0]
true
iex> {:error, error} = Xema.validate schema, 9
{:error, %Xema.ValidationError{
reason: %{type: :list, value: 9}
}}
iex> Exception.message(error)
"Expected :list, got 9."Items
The items keyword will be used to validate all items of a list to a single
schema.
iex> schema = Xema.new {:list, items: :string}
iex> Xema.valid? schema, ["a", "b", "abc"]
true
iex> {:error, error} = Xema.validate schema, ["a", 1]
{:error, %Xema.ValidationError{
reason: %{items: %{1 => %{type: :string, value: 1}}}
}}
iex> Exception.message(error)
"Expected :string, got 1, at [1]."The next example shows how to add keywords to the items schema.
iex> schema = Xema.new {:list, items: {:integer, minimum: 1, maximum: 10}}
iex> Xema.validate schema, [1, 2, 3]
:ok
iex> {:error, error} = Xema.validate schema, [3, 2, 1, 0]
{:error, %Xema.ValidationError{
reason: %{items: %{3 => %{value: 0, minimum: 1}}}
}}
iex> Exception.message(error)
"Value 0 is less than minimum value of 1, at [3]."items can also be used to give each item a specific schema.
iex> schema = Xema.new {
...> :list,
...> items: [:integer, {:string, min_length: 5}]
...> }
iex> Xema.valid? schema, [1, "hello"]
true
iex> {:error, error} = Xema.validate schema, [1, "five"]
{:error, %Xema.ValidationError{
reason: %{items: %{1 => %{value: "five", min_length: 5}}}
}}
iex> Exception.message(error)
~s|Expected minimum length of 5, got "five", at [1].|
# It’s okay to not provide all of the items:
iex> Xema.validate schema, [1]
:ok
# And, by default, it’s also okay to add additional items to end:
iex> Xema.validate schema, [1, "hello", "foo"]
:okAdditional Items
The additional_items keyword controls whether it is valid to have additional
items in the array beyond what is defined in the schema.
iex> schema = Xema.new {
...> :list,
...> items: [:integer, {:string, min_length: 5}],
...> additional_items: false
...> }
iex> Xema.validate schema, [1]
:ok
# It’s okay to not provide all of the items:
# But, since additionalItems is false, we can’t provide extra items:
iex> {:error, error} = Xema.validate schema, [1, "hello", "foo"]
{:error, %Xema.ValidationError{
reason: %{items: %{2 => %{additional_items: false}}}
}}
iex> Exception.message(error)
"Unexpected additional item, at [2]."
iex> {:error, error} = Xema.validate schema, [1, "hello", "foo", "bar"]
{:error, %Xema.ValidationError{
reason: %{
items: %{
2 => %{additional_items: false},
3 => %{additional_items: false}
}
}
}}
iex> Exception.message(error)
"""
Unexpected additional item, at [2].
Unexpected additional item, at [3].\
"""The keyword can also contain a schema to specify the type of additional items.
iex> schema = Xema.new {
...> :list,
...> items: [:integer, {:string, min_length: 3}],
...> additional_items: :integer
...> }
iex> Xema.valid? schema, [1, "two", 3, 4]
true
iex> {:error, error} = Xema.validate schema, [1, "two", 3, "four"]
{:error, %Xema.ValidationError{
reason: %{items: %{3 => %{type: :integer, value: "four"}}}
}}
iex> Exception.message(error)
~s|Expected :integer, got "four", at [3].|Length
The length of the array can be specified using the min_items and max_items
keywords. The value of each keyword must be a non-negative number.
iex> schema = Xema.new {:list, min_items: 2, max_items: 3}
iex> {:error, error} = Xema.validate schema, [1]
{:error, %Xema.ValidationError{
reason: %{value: [1], min_items: 2}
}}
iex> Exception.message(error)
"Expected at least 2 items, got [1]."
iex> Xema.validate schema, [1, 2]
:ok
iex> Xema.validate schema, [1, 2, 3]
:ok
iex> {:error, error} = Xema.validate schema, [1, 2, 3, 4]
{:error, %Xema.ValidationError{
reason: %{value: [1, 2, 3, 4], max_items: 3}
}}
iex> Exception.message(error)
"Expected at most 3 items, got [1, 2, 3, 4]."Uniqueness
A schema can ensure that each of the items in an array is unique.
iex> schema = Xema.new {:list, unique_items: true}
iex> Xema.valid? schema, [1, 2, 3]
true
iex> {:error, error} = Xema.validate schema, [1, 2, 3, 2, 1]
{:error, %Xema.ValidationError{
reason: %{value: [1, 2, 3, 2, 1], unique_items: true}
}}
iex> Exception.message(error)
"Expected unique items, got [1, 2, 3, 2, 1]."Type tuple
Tuples are intended as fixed-size containers for multiple elements. The validation of tuples is similar to lists.
iex> schema = Xema.new {:tuple, min_items: 2, max_items: 3}
iex> {:error, error} = Xema.validate schema, {1}
{:error, %Xema.ValidationError{
reason: %{value: {1}, min_items: 2}
}}
iex> Exception.message(error)
"Expected at least 2 items, got {1}."
iex> Xema.validate schema, {1, 2}
:ok
iex> Xema.validate schema, {1, 2, 3}
:ok
iex> {:error, error} = Xema.validate schema, {1, 2, 3, 4}
{:error, %Xema.ValidationError{
reason: %{value: {1, 2, 3, 4}, max_items: 3}
}}
iex> Exception.message(error)
"Expected at most 3 items, got {1, 2, 3, 4}."Type map
Whenever you need a key-value store, maps are the “go to” data structure in Elixir. Each of these pairs is conventionally referred to as a “property”.
iex> schema = Xema.new :map
iex> Xema.valid? schema, %{"foo" => "bar"}
true
iex> {:error, error} = Xema.validate schema, "bar"
{:error, %Xema.ValidationError{
reason: %{type: :map, value: "bar"}
}}
iex> Exception.message(error)
~s|Expected :map, got "bar".|
# Using non-strings as keys are also valid:
iex> Xema.valid? schema, %{foo: "bar"}
true
iex> Xema.valid? schema, %{1 => "bar"}
trueKeys
The keyword keys can restrict the keys to atoms or strings.
Atoms as keys:
iex> schema = Xema.new {:map, keys: :atoms}
iex> Xema.valid? schema, %{"foo" => "bar"}
false
iex> Xema.valid? schema, %{foo: "bar"}
true
iex> Xema.valid? schema, %{1 => "bar"}
falseStrings as keys:
iex> schema = Xema.new {:map, keys: :strings}
iex> Xema.valid? schema, %{"foo" => "bar"}
true
iex> Xema.valid? schema, %{foo: "bar"}
false
iex> Xema.valid? schema, %{1 => "bar"}
falseProperties
The properties on a map are defined using the properties keyword. The value
of properties is a map, where each key is the name of a property and each
value is a schema used to validate that property.
iex> schema = Xema.new {:map,
...> properties: %{
...> a: :integer,
...> b: {:string, min_length: 5}
...> }
...> }
iex> Xema.valid? schema, %{a: 5, b: "hello"}
true
iex> {:error, error} = Xema.validate schema, %{a: 5, b: "ups"}
{:error, %Xema.ValidationError{
reason: %{
properties: %{
b: %{value: "ups", min_length: 5}
}
}
}}
iex> Exception.message(error)
"Expected minimum length of 5, got \"ups\", at [:b]."
# Additinonal properties are allowed by default:
iex> Xema.valid? schema, %{a: 5, b: "hello", add: :prop}
trueRequired Properties
By default, the properties defined by the properties keyword are not required.
However, one can provide a list of required properties using the required
keyword.
iex> schema = Xema.new {:map, properties: %{foo: :string}, required: [:foo]}
iex> Xema.validate schema, %{foo: "bar"}
:ok
iex> {:error, error} = Xema.validate schema, %{bar: "foo"}
{:error, %Xema.ValidationError{
reason: %{required: [:foo]}
}}
iex> Exception.message(error)
"Required properties are missing: [:foo]."Additional Properties
The additional_properties keyword is used to control the handling of extra
stuff, that is, properties whose names are not listed in the properties keyword.
By default any additional properties are allowed.
The additional_properties keyword may be either a boolean or an schema. If
additional_properties is a boolean and set to false, no additional properties
will be allowed.
iex> schema = Xema.new {:map,
...> properties: %{foo: :string},
...> required: [:foo],
...> additional_properties: false
...> }
iex> Xema.validate schema, %{foo: "bar"}
:ok
iex> {:error, error} = Xema.validate schema, %{foo: "bar", bar: "foo"}
{:error, %Xema.ValidationError{
reason: %{
properties: %{bar: %{additional_properties: false}}
}
}}
iex> Exception.message(error)
"Expected only defined properties, got key [:bar]."additional_properties can also contain a schema to specify the type of
additional properites.
iex> schema = Xema.new {
...> :map,
...> properties: %{foo: :string},
...> additional_properties: :integer
...> }
iex> Xema.valid? schema, %{foo: "foo", add: 1}
true
iex> {:error, error} = Xema.validate schema, %{foo: "foo", add: "one"}
{:error, %Xema.ValidationError{
reason: %{properties: %{add: %{type: :integer, value: "one"}}}
}}
iex> Exception.message(error)
~s|Expected :integer, got "one", at [:add].|Pattern Properties
The keyword pattern_properties defined additional properties by regular
expressions.
iex> schema = Xema.new {
...> :map,
...> additional_properties: false,
...> pattern_properties: %{
...> ~r/^s_/ => :string,
...> ~r/^i_/ => :integer
...> }
...> }
iex> Xema.valid? schema, %{"s_0" => "foo", "i_1" => 6}
true
iex> Xema.valid? schema, %{s_0: "foo", i_1: 6}
true
iex> {:error, error} = Xema.validate schema, %{s_0: "foo", f_1: 6.6}
{:error, %Xema.ValidationError{
reason: %{properties: %{f_1: %{additional_properties: false}}}
}}
iex> Exception.message(error)
"Expected only defined properties, got key [:f_1]."Size
The number of properties on an object can be restricted using the
min_properties and max_properties keywords.
iex> schema = Xema.new {:map,
...> min_properties: 2,
...> max_properties: 3
...> }
iex> Xema.valid? schema, %{a: 1, b: 2}
true
iex> {:error, error} = Xema.validate schema, %{}
{:error, %Xema.ValidationError{
reason: %{min_properties: 2, value: %{}}
}}
iex> Exception.message(error)
"Expected at least 2 properties, got %{}."
iex> {:error, error} = Xema.validate schema, %{a: 1, b: 2, c: 3, d: 4}
{:error, %Xema.ValidationError{
reason: %{max_properties: 3, value: %{a: 1, b: 2, c: 3, d: 4}}
}}
iex> Exception.message(error)
"Expected at most 3 properties, got %{a: 1, b: 2, c: 3, d: 4}."Size
The type of a key in the schema also matters in validation.
iex> schema = Xema.new {:map,
...> properties: %{foo: :integer},
...> additional_properties: false
...> }
iex> Xema.valid? schema, %{foo: 1}
true
iex> {:error, error} = Xema.validate schema, %{"foo" => 1}
{:error, %Xema.ValidationError{
reason: %{properties: %{"foo" => %{additional_properties: false}}}
}}
iex> Exception.message(error)
~s|Expected only defined properties, got key [\"foo\"].|Dependencies
The dependencies keyword allows the schema of the object to change based on
the presence of certain special properties.
iex> schema = Xema.new {
...> :map,
...> properties: %{
...> a: :number,
...> b: :number,
...> c: :number
...> },
...> dependencies: %{
...> b: [:c]
...> }
...> }
iex> Xema.valid? schema, %{a: 5}
true
iex> Xema.valid? schema, %{c: 9}
true
iex> Xema.valid? schema, %{b: 1}
false
iex> Xema.valid? schema, %{b: 1, c: 7}
trueType struct
Structs can also be validated.
iex> schema = Xema.new :struct
iex> Xema.valid? schema, ~r/.*/
true
iex> Xema.valid? schema, %{}
falseModule
The module keyword allows specifying which struct is expected.
iex> schema = Xema.new {:struct, module: Regex}
iex> Xema.valid? schema, ~r/.*/
true
iex> Xema.valid? schema, URI.parse("")
falseMap keywords for structs
The validations for map are also available.
The next example shows a schema for an URI that needs a fragment.
iex> schema = Xema.new {
...> :struct, module: URI, properties: %{fragment: :string}
...> }
iex>
iex> Xema.valid?(schema, URI.parse("http://example.com"))
false
iex> Xema.valid?(schema, URI.parse("http://example.com#frag"))
trueMultiples Types
JSON Schema Draft: 4/6/7
It is also possible to check if a value matches one of several types.
iex> schema = Xema.new {[:string, nil], min_length: 1}
iex> Xema.valid? schema, "foo"
true
iex> Xema.valid? schema, nil
true
iex> Xema.valid? schema, ""
falseAllow Additional Types
JSON Schema Draft: -
The keyword allow adds an extra type to the schema validation.
iex> schema = Xema.new {:string, min_length: 1, allow: nil}
iex> Xema.valid? schema, "foo"
true
iex> Xema.valid? schema, nil
true
iex> Xema.valid? schema, ""
falseConstants
JSON Schema Draft: -/6/7
This keyword checks if a value is equals to the given const.
iex> schema = Xema.new(const: 4711)
iex> Xema.validate schema, 4711
:ok
iex> {:error, error} = Xema.validate schema, 333
{:error, %Xema.ValidationError{
reason: %{const: 4711, value: 333}
}}
iex> Exception.message(error)
"Expected 4711, got 333."Enumerations
The enum keyword is used to restrict a value to a fixed set of values. It must
be an array with at least one element, where each element is unique.
iex> schema = Xema.new {:any, enum: [1, "foo", :bar]}
iex> Xema.valid? schema, :bar
true
iex> Xema.valid? schema, 42
falseNegate Schema
The keyword not negates a schema.
iex> schema = Xema.new(not: {:integer, minimum: 0})
iex> Xema.valid? schema, 10
false
iex> Xema.valid? schema, -10
trueIf-Then-Else
The keywords if, then, else work together to implement conditional
application of a subschema based on the outcome of another subschema.
iex> schema =
...> Xema.new(
...> if: :list,
...> then: [items: :integer, min_items: 2],
...> else: :integer
...> )
...>
...> Xema.valid?(schema, 3)
true
iex> Xema.valid?(schema, "3")
false
iex> Xema.valid?(schema, [1])
false
iex> Xema.valid?(schema, [1, 2])
trueCustom validator
With the keyword validator a custom validator can be defined. The validator
expected a function or a tuple of module and function name. The validator
function gets the current value and return :ok on success and an error tuple
on failure.
iex> defmodule Palindrome do
...> @xema Xema.new(
...> properties: %{
...> palindrome: {:string, validator: &Palindrome.check/1}
...> }
...> )
...>
...> def check(value) do
...> case value == String.reverse(value) do
...> true -> :ok
...> false -> {:error, :no_palindrome}
...> end
...> end
...>
...> def validate(value) do
...> Xema.validate(@xema, value)
...> end
...> end
iex>
iex> Palindrome.validate(%{palindrome: "abba"})
:ok
iex> {:error, error} = Palindrome.validate(%{palindrome: "beatles"})
{:error, %Xema.ValidationError{
reason: %{
properties: %{
palindrome: %{validator: :no_palindrome, value: "beatles"}
}
}
}}
iex> Exception.message(error)
~s|Validator fails with :no_palindrome for value "beatles", at [:palindrome].|Combine Schemas
The keywords all_of, any_of, and one_of combines schemas.
With all_of all schemas have to match.
iex> all = Xema.new(all_of: [
...> {:integer, multiple_of: 2},
...> {:integer, multiple_of: 3}
...> ])
iex> 0..9 |> Enum.map(&Xema.valid?(all, &1)) |> Enum.with_index()
[true: 0, false: 1, false: 2, false: 3, false: 4,
false: 5, true: 6, false: 7, false: 8, false: 9]With any_of any schema have to match.
iex> any = Xema.new(any_of: [
...> {:integer, multiple_of: 2},
...> {:integer, multiple_of: 3}
...> ])
iex> 0..9 |> Enum.map(&Xema.valid?(any, &1)) |> Enum.with_index()
[true: 0, false: 1, true: 2, true: 3, true: 4,
false: 5, true: 6, false: 7, true: 8, true: 9]With one_of exactly on schema have to match.
iex> one = Xema.new(one_of: [
...> {:integer, multiple_of: 2},
...> {:integer, multiple_of: 3}
...> ])
iex> 0..9 |> Enum.map(&Xema.valid?(one, &1)) |> Enum.with_index()
[false: 0, false: 1, true: 2, true: 3, true: 4,
false: 5, false: 6, false: 7, true: 8, true: 9]Structuring a schema
This section will present some options to structuring and reuse schemas.
definitions and ref
To reuse a schema put it under the keyword definitions. Later on, the schema
can be referenced with the keyword ref.
iex> schema = Xema.new {
...> :map,
...> definitions: %{
...> positive: {:integer, minimum: 1},
...> negative: {:integer, maximum: -1}
...> },
...> properties: %{
...> a: {:ref, "#/definitions/positive"},
...> b: {:ref, "#/definitions/positive"},
...> c: {:ref, "#/definitions/negative"},
...> # You can also reference any other schema.
...> d: {:ref, "#/properties/c"}
...> }
...> }
...> Xema.validate schema, %{a: 1, c: -1}
:ok
iex> {:error, error} = Xema.validate schema, %{b: 1, c: 1}
{:error, %Xema.ValidationError{
reason: %{properties: %{c: %{maximum: -1, value: 1}}}
}}
iex> Exception.message(error)
"Value 1 exceeds maximum value of -1, at [:c]."
iex> Xema.validate schema, %{d: -1}
:ok
iex> {:error, error} = Xema.validate schema, %{d: 1}
{:error, %Xema.ValidationError{
reason: %{properties: %{d: %{maximum: -1, value: 1}}}
}}
iex> Exception.message(error)
"Value 1 exceeds maximum value of -1, at [:d]."
Without definitions and ref
It is also possible to use a schema in another schema, as in the following code.
iex> positive = Xema.new {:integer, minimum: 1}
...> negative = Xema.new {:integer, maximum: -1}
...> schema = Xema.new {
...> :map,
...> properties: %{
...> a: positive,
...> b: positive,
...> c: negative
...> }
...> }
...> Xema.validate schema, %{a: 1, b: 2, c: -3}
:okIn multiple files
To structure schemas in multiple files you have to configure a loader to load the files. The section "Configure a loader" described the configuration and implementation of an loader.
Let's assume you have the following file available at
https://localhost:1234/positive.exon.
{
:string,
definitions: %{
or_nil: {:any_of, [:nil, {:ref, "#"}]}
}
}I admit this example is a little absurd. But the URL could be also any other URL and with another loader, the file could be on your hard disk.
You can use the schema above as follow.
iex> alias Test.RemoteLoaderExon
iex> schema = Xema.new({
...> :map,
...> id: "http://localhost:1234",
...> properties: %{
...> name: {:ref, "xema_name.exon#/definitions/or_nil"},
...> str: {:ref, "xema_name.exon"}
...> }
...> }, loader: RemoteLoaderExon)
iex> Xema.validate schema, %{str: "foo"}
:ok
iex> {:error, error} = Xema.validate schema, %{str: nil}
{:error, %Xema.ValidationError{
reason: %{properties: %{str: %{type: :string, value: nil}}}
}}
iex> Exception.message(error)
"Expected :string, got nil, at [:str]."
iex> Xema.validate schema, %{name: nil}
:ok
iex> Xema.validate schema, %{name: "Funny van Dannen"}
:ok
iex> {:error, error} = Xema.validate schema, %{name: 66}
{:error,
%Xema.ValidationError{
reason: %{
properties: %{
name: %{any_of: [
%{type: nil, value: 66},
%{type: :string, value: 66}
],
value: 66}
}
}
}
}
iex> Exception.message(error)
"""
No match of any schema, at [:name].
Expected nil, got 66, at [:name].
Expected :string, got 66, at [:name].\
"""