Packages
xema
0.1.1
0.17.9
0.17.8
0.17.7
0.17.6
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.1
0.16.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.10
0.13.9
0.13.8
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.1
0.13.0
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.1
A schema validator inspired by JSON Schema.
Current section
Files
Jump to
Current section
Files
README.md
# Xema
[](https://travis-ci.org/hrzndhrn/xema)
[](https://coveralls.io/github/hrzndhrn/xema?branch=master)
[](https://opensource.org/licenses/MIT)
Xema is a schema validator inspired by [JSON Schema](http://json-schema.org).
Xema allows you to annotate and validate elixir data structures.
Xema is in early beta. If you try it and has an issue, report them.
## Installation
First, add Xema to your `mix.exs` dependencies:
```elixir
def deps do
[{:xema, "~> 0.1"}]
end
```
Then, update your dependencies:
```Shell
$ mix deps.get
```
## Usage
Xema supported the following types to validate data structures.
* [Type any](#any)
* [Type nil](#nil)
* [Type boolean](#boolean)
* [Type string](#string)
* [Length](#length)
* [Regular Expression](#regex)
* [Types number, integer and float](#number)
* [Multiples](#multi)
* [Range](#range)
* [Type list](#list)
* [Items](#items)
* [Additional Items](#additional_items)
* [Length](#list_length)
* [Uniqueness](#unique)
* [Type map](#map)
* [Keys](#keys)
* [Properties](#properties)
* [Required Properties](#required_properties)
* [Pattern Properties](#pattern_properties)
* [Size](#map_size)
* [Enumerations](#enum)
### <a name="any"></a> Type any
The schema any will accept any data.
```elixir
iex> import Xema
Xema
iex> schema = xema :any
%Xema{type: %Xema.Any{}}
iex> validate schema, 42
:ok
iex> validate schema, "foo"
:ok
iex> validate schema, nil
:ok
```
### <a name="nil"></a> Type nil
The nil type matches only `nil`.
```elixir
iex> import Xema
Xema
iex> schema = xema :nil
%Xema{type: %Xema.Nil{}}
iex> validate schema, nil
:ok
iex> validate schema, 0
{:error, %{reason: :wrong_type, type: :nil}}
```
### <a name="boolean"></a> Type boolean
The boolean type matches only `true` and `false`.
```Elixir
iex> import Xema
Xema
iex> schema = xema :boolean
%Xema{type: %Xema.Boolean{}}
iex> validate schema, true
:ok
iex> is_valid? schema, false
true
iex> validate schema, 0
{:error, %{reason: :wrong_type, type: :boolean}}
iex> is_valid? schema, nil
false
```
### <a name="string"></a> Type string
The string type is used for strings.
```elixir
iex> import Xema
Xema
iex> schema = xema :string
%Xema{type: %Xema.String{}}
iex> validate schema, "José"
:ok
iex> validate schema, 42
{:error, %{reason: :wrong_type, type: :string}}
iex> is_valid? schema, "José"
true
iex> is_valid? schema, 42
false
```
#### <a name="length"></a> Length
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.
```elixir
iex> import Xema
Xema
iex> schema = xema :string, min_length: 2, max_length: 3
%Xema{type: %Xema.String{min_length: 2, max_length: 3}}
iex> validate schema, "a"
{:error, %{reason: :too_short, min_length: 2}}
iex> validate schema, "ab"
:ok
iex> validate schema, "abc"
:ok
iex> validate schema, "abcd"
{:error, %{reason: :too_long, max_length: 3}}
```
#### <a name="regex"></a> Regular Expression
The `pattern` keyword is used to restrict a string to a particular regular expression.
```Elixir
iex> import Xema
Xema
iex> schema = xema :string, pattern: ~r/[0-9]-[A-B]+/
%Xema{type: %Xema.String{pattern: ~r/[0-9]-[A-B]+/}}
iex> validate schema, "1-AB"
:ok
iex> validate schema, "foo"
{:error, %{reason: :no_match, pattern: ~r/[0-9]-[A-B]+/}}
```
### <a name="number"></a> 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.
```Elixir
iex> import Xema
Xema
iex> schema = xema :number
%Xema{type: %Xema.Number{}}
iex> validate schema, 42
:ok
iex> validate schema, 21.5
:ok
iex> validate schema, "foo"
{:error, %{reason: :wrong_type, type: :number}}
```
The `integer` type is used for integral numbers.
```Elixir
iex> import Xema
Xema
iex> schema = xema :integer
%Xema{type: %Xema.Integer{}}
iex> validate schema, 42
:ok
iex> validate schema, 21.5
{:error, %{reason: :wrong_type, type: :integer}}
```
The `float` type is used for floating point numbers.
```Elixir
iex> import Xema
Xema
iex> schema = xema :float
%Xema{type: %Xema.Float{}}
iex> validate schema, 42
{:error, %{reason: :wrong_type, type: :float}}
iex> validate schema, 21.5
:ok
```
#### <a name="multi"></a> Multiples
Numbers can be restricted to a multiple of a given number, using the
`multiple_of` keyword. It may be set to any positive number.
```Elixir
iex> import Xema
Xema
iex> schema = xema :number, multiple_of: 2
%Xema{type: %Xema.Number{multiple_of: 2}}
iex> validate schema, 8
:ok
iex> validate schema, 7
{:error, %{reason: :not_multiple, multiple_of: 2}}
iex> is_valid? schema, 8.0
true
```
#### <a name="range"></a> Range
Ranges of numbers are specified using a combination of the `minimum`, `maximum`,
`exclusive_minimum` and `exclusive_maximum` keywords.
* `minimum` specifies a minimum numeric value.
* `exclusive_minimum` is 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.
* `maximum` specifies a maximum numeric value.
* `exclusive_maximum` is 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.
```Elixir
iex> import Xema
Xema
iex> schema = xema :float, minimum: 1.2, maximum: 1.4, exclusive_maximum: true
%Xema{type: %Xema.Float{minimum: 1.2, maximum: 1.4, exclusive_maximum: true}}
iex> validate schema, 1.1
{:error, %{reason: :too_small, minimum: 1.2}}
iex> validate schema, 1.2
:ok
iex> is_valid? schema, 1.3
true
iex> validate schema, 1.4
{:error, %{reason: :too_big, maximum: 1.4, exclusive_maximum: true}}
iex> validate schema, 1.5
{:error, %{reason: :too_big, maximum: 1.4}}
```
### <a name="list"></a> Type list
List are used for ordered elements, each element may be of a different type.
```Elixir
iex> import Xema
Xema
iex> schema = xema :list
%Xema{type: %Xema.List{}}
iex> is_valid? schema, [1, "two", 3.0]
true
iex> validate schema, 9
{:error, %{reason: :wrong_type, type: :list}}
```
#### <a name="items"></a> Items
The `items` keyword will be used to validate all items of a list to a single
schema.
```Elixir
iex> import Xema
Xema
iex> schema = xema :list, items: :string
%Xema{type: %Xema.List{items: %Xema.String{}}}
iex> is_valid? schema, ["a", "b", "abc"]
true
iex> validate schema, ["a", 1]
{
:error,
%{reason: :invalid_item, at: 1, error: %{reason: :wrong_type, type: :string}}
}
```
The next example shows how to add keywords to the items schema.
```Elixir
iex> import Xema
Xema
iex> schema = xema :list, items: {:integer, minimum: 1, maximum: 10}
%Xema{type: %Xema.List{items: %Xema.Integer{minimum: 1, maximum: 10}}}
iex> validate schema, [1, 2, 3]
:ok
iex> validate schema, [3, 2, 1, 0]
{
:error,
%{reason: :invalid_item, at: 3, error: %{reason: :too_small, minimum: 1}}
}
```
`items` can also be used to give each item a specific schema.
```Elixir
iex> import Xema
Xema
iex> schema = xema :list,
...> items: [:integer, {:string, min_length: 5}]
%Xema{type: %Xema.List{
items: [%Xema.Integer{}, %Xema.String{min_length: 5}]
}}
iex> is_valid? schema, [1, "hello"]
true
iex> validate schema, [1, "five"]
{
:error,
%{reason: :invalid_item, at: 1, error: %{reason: :too_short, min_length: 5}}
}
# It’s okay to not provide all of the items:
iex> validate schema, [1]
:ok
# And, by default, it’s also okay to add additional items to end:
iex> validate schema, [1, "hello", "foo"]
:ok
```
#### <a name="additional_items"></a> Additional Items
The `additional_items` keyword controls whether it is valid to have additional
items in the array beyond what is defined in the schema.
```Elixir
iex> import Xema
Xema
iex> schema = xema :list,
...> items: [:integer, {:string, min_length: 5}],
...> additional_items: false
%Xema{type: %Xema.List{
items: [%Xema.Integer{}, %Xema.String{min_length: 5}],
additional_items: false
}}
# It’s okay to not provide all of the items:
iex> validate schema, [1]
:ok
# But, since additionalItems is false, we can’t provide extra items:
iex> validate schema, [1, "hello", "foo"]
{:error, %{reason: :additional_item, at: 2}}
iex> validate schema, [1, "hello", "foo", "bar"]
{:error, %{reason: :additional_item, at: 2}}
```
#### <a name="list_length"></a> 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.
```Elixir
iex> import Xema
Xema
iex> schema = xema :list, min_items: 2, max_items: 3
%Xema{type: %Xema.List{min_items: 2, max_items: 3}}
iex> validate schema, [1]
{:error, %{reason: :too_less_items, min_items: 2}}
iex> validate schema, [1, 2]
:ok
iex> validate schema, [1, 2, 3]
:ok
iex> validate schema, [1, 2, 3, 4]
{:error, %{reason: :too_many_items, max_items: 3}}
```
#### <a name="unique"></a> Uniqueness
A schema can ensure that each of the items in an array is unique.
```Elixir
iex> import Xema
Xema
iex> schema = xema :list, unique_items: true
%Xema{type: %Xema.List{unique_items: true}}
iex> is_valid? schema, [1, 2, 3]
true
iex> validate schema, [1, 2, 3, 2, 1]
{:error, %{reason: :not_unique}}
```
### <a name="map"></a> 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”.
```Elixir
iex> import Xema
Xema
iex> schema = xema :map
%Xema{type: %Xema.Map{}}
iex> is_valid? schema, %{"foo" => "bar"}
true
iex> validate schema, "bar"
{:error, %{reason: :wrong_type, type: :map}}
# Using non-strings as keys are also valid:
iex> is_valid? schema, %{foo: "bar"}
true
iex> is_valid? schema, %{1 => "bar"}
true
```
#### <a name="keys"></a> Keys
The keyword `keys` can restrict the keys to atoms or strings.
Atoms as keys:
```Elixir
iex> import Xema
Xema
iex> schema = xema :map, keys: :atoms
%Xema{type: %Xema.Map{keys: :atoms}}
iex> is_valid? schema, %{"foo" => "bar"}
false
iex> is_valid? schema, %{foo: "bar"}
true
iex> is_valid? schema, %{1 => "bar"}
false
```
Strings as keys:
```Elixir
iex> import Xema
Xema
iex> schema = xema :map, keys: :strings
%Xema{type: %Xema.Map{keys: :strings}}
iex> is_valid? schema, %{"foo" => "bar"}
true
iex> is_valid? schema, %{foo: "bar"}
false
iex> is_valid? schema, %{1 => "bar"}
false
```
#### <a name="properties"></a> Properties
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.
```Elixir
iex> import Xema
Xema
iex> schema = xema :map,
...> properties: %{
...> a: :integer,
...> b: {:string, min_length: 5}
...> }
%Xema{type: %Xema.Map{
properties: %{
a: %Xema.Integer{},
b: %Xema.String{min_length: 5}
}
}}
iex> is_valid? schema, %{a: 5, b: "hello"}
true
iex> validate schema, %{a: 5, b: "ups"}
{:error, %{
reason: :invalid_property,
property: :b,
error: %{
reason: :too_short,
min_length: 5
}
}}
# Additinonal properties are allowed by default:
iex> is_valid? schema, %{a: 5, b: "hello", add: :prop}
true
```
#### <a name="required_properties"></a> Required 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.
```Elixir
iex> import Xema
Xema
iex> schema = xema :map, properties: %{foo: :string}, required: [:foo]
%Xema{
type: %Xema.Map{
properties: %{foo: %Xema.String{}},
required: MapSet.new([:foo])
}
}
iex> validate schema, %{foo: "bar"}
:ok
iex> validate schema, %{bar: "foo"}
{:error, %{reason: :missing_properties, missing: [:foo], required: [:foo]}}
```
#### <a name="additional_properties"></a> 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 object. If
`additional_properties` is a boolean and set to false, no additional properties
will be allowed.
```Elixir
iex> import Xema
Xema
iex> schema = xema :map,
...> properties: %{foo: :string},
...> required: [:foo],
...> additional_properties: false
%Xema{
type: %Xema.Map{
properties: %{foo: %Xema.String{}},
required: MapSet.new([:foo]),
additional_properties: false
}
}
iex> validate schema, %{foo: "bar"}
:ok
iex> validate schema, %{foo: "bar", bar: "foo"}
{:error, %{
reason: :no_additional_properties_allowed,
additional_properties: [:bar]}
}
```
#### <a name="pattern_properties"></a> Pattern Properties
The keyword `pattern_properties` defined additional properties by regular
expressions.
```Eixir
iex> import Xema
Xema
iex> schema = xema :map,
...> additional_properties: false,
...> pattern_properties: %{
...> ~r/^s_/ => :string,
...> ~r/^i_/ => :integer
...> }
%Xema{type: %Xema.Map{
additional_properties: false,
pattern_properties: %{
~r/^s_/ => %Xema.String{},
~r/^i_/ => %Xema.Integer{}
}
}}
iex> is_valid? schema, %{"s_0" => "foo", "i_1" => 6}
true
iex> is_valid? schema, %{s_0: "foo", i_1: 6}
true
iex> validate schema, %{s_0: "foo", f_1: 6.6}
{:error, %{
reason: :no_additional_properties_allowed,
additional_properties: [:f_1]
}}
```
#### <a name="map_size"></a> Size
The number of properties on an object can be restricted using the
`min_properties` and `max_properties` keywords.
```Elixir
iex> import Xema
Xema
iex> schema = xema :map,
...> min_properties: 2,
...> max_properties: 3
%Xema{type: %Xema.Map{
min_properties: 2,
max_properties: 3
}}
iex> is_valid? schema, %{a: 1, b: 2}
true
iex> validate schema, %{}
{:error, %{reason: :too_less_properties, min_properties: 2}}
iex> validate schema, %{a: 1, b: 2, c: 3, d: 4}
{:error, %{reason: :too_many_properties, max_properties: 3}}
```
#### <a name="dependencies"></a> Dependencies
The `dependencies` keyword allows the schema of the object to change based on
the presence of certain special properties.
```Elixir
iex> import Xema
Xema
iex> schema = xema :map,
...> properties: %{
...> a: :number,
...> b: :number,
...> c: :number
...> },
...> dependencies: %{
...> b: [:c]
...> }
%Xema{type: %Xema.Map{
properties: %{a: %Xema.Number{}, b: %Xema.Number{}, c: %Xema.Number{}},
dependencies: %{b: [:c]}
}}
iex> is_valid? schema, %{a: 5}
true
iex> is_valid? schema, %{c: 9}
true
iex> is_valid? schema, %{b: 1}
false
iex> is_valid? schema, %{b: 1, c: 7}
true
```
### <a name="enum"></a> 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.
```Elixir
iex> import Xema
Xema
iex> schema = xema :any, enum: [1, "foo", :bar]
%Xema{type: %Xema.Any{enum: [1, "foo", :bar]}}
iex> is_valid? schema, :bar
true
iex> is_valid? schema, 42
false
```
## References
The home of JSON Schema: http://json-schema.org/
Specification:
* [JSON Schema core](http://json-schema.org/latest/json-schema-core.html)
defines the basic foundation of JSON Schema
* [JSON Schema Validation](http://json-schema.org/latest/json-schema-validation.html)
defines the validation keywords of JSON Schema
[Understanding JSON Schema](https://spacetelescope.github.io/understanding-json-schema/index.html)
a great tutorial for JSON Schema authors and a template for the description of
Xema.