-
Notifications
You must be signed in to change notification settings - Fork 0
Phase Schema
This document provides the Phase schema specification.
Phase provides a simple, intuitive, easy to write and easy to process schema language for declaring type information that can be transformed into formal JSON Schema to describe and validate JSON data.
For more background, see the following:
- [JSON Overview](JSON Overview)
- [JSON Schema Overview](JSON Schema Overview)
- [Phase Overview](Phase Overview)
An instance is any JSON value. It may be described by one or more Phase schemas. An instance may also be referred to as a "Phase instance" or "Phase data".
A Phase schema can be used to validate an instance. A valid instance is a JSON value that satisfies the criteria and constraints specified by a Phase schema.
An empty schema is an empty document (zero or more whitespace characters) or a document with a single, empty compound type declaration.
{}
An empty schema matches any instance.
A Phase schema consists of one of the following at the root of a document.
- An anonymous declaration
- A declaration
An anonymous declaration is only valid at the root of a Phase schema. It declares a type to be used to validate data. The type is either a simple annotated type or a complex type with nested property declarations.
An example of an anonymous declaration with an annotated type:
number @minimum(21) @format('integer')
An example of an anonymous declaration with a compound type containing nested property declarations.
{
name string @required
age number @minimum(21) @format('integer')
}
A declaration is a name followed by an annotated type.
An example of a declaration:
age number @minimum(21) @format('integer')
Another example of a declaration:
customer {
name string @required
age number @minimum(18) @format('integer')
}
Schemas can be nested.
customer {
name {
first string @required
last string
} @required
birthday string @format('yyyy-mm-dd') @minimumAge(18) @required
address {
street string
city string
state string @upper @length(2)
zip number @format('#####', '#####-####') @required
}
}
Names are used to declare property keys.
A name must be a valid identifier according to JavaScript. In other words, an identifier is a string of Unicode characters that begin with an uppercase or lowercase letter, underscore (_), or dollar sign ($), followed by a sequence of zero or more uppercase and lowercase letters, underscores, dollar signs, and digits (0-9).
name -> id
id -> [a-zA-Z_$] [a-zA-Z0-9_$]*
For example, the following Phase schema declares two property names, name and age.
{
name string
age integer
}
This schema is matched by the following two JSON instances:
{
"name": "Sally"
"age": 28
}
and
{
"name": "Bob"
"age": 30
}
When a name is used as part of a root declaration, it is considered informational. For example, the following two schemas are equivalent:
{ name string; age integer }
and
person { name string; age integer }
The following instance would match either schema:
{
"name": "Sally"
"age": 28
}
The following are valid Phase data type specifiers:
boolean
integer
number
string
array
object
The following are examples of valid type declarations:
isAwesome boolean
age integer
temperature number
name string
attendees array
something object
An array type declaration can also used brackets notation instead of 'array':
attendees []
An array can also specify constraints on elements. The following example declares that a valid list property must be an array with three elements (a string, followed by two numbers):
list [ string, number, number ]
This defines a list property that is an array with at least one element, which must be a string, followed by any number of integer elements:
list [ string, integer... ]
The previous example is different from the following, which specifies that a list is an array in which the first element must be a string, the second must be an integer, followed by any number of elements of any type:
list [ string, integer, ... ]
The following declaration provides further constraints using an annotation to specify the array must have exactly four elements:
list [ string, integer... ] @length(4)
The following declares an list property in which the first element can be either a string or integer, followed by any number of integers; the list must have at least one element and no more than four:
list [ string | integer, integer... ] @minLength(1) @maxLength(4)
The previous is equivalent to the following:
list [ string | integer, integer, integer, integer ]
An object type declaration can also used curly braces notation instead of 'object'.
The following example declares that an address property must be an object:
{
address {}
}
In Phase, an object declaration is called a compound declaration, because the curly braces can contain Phase declarations that specify valid properties for instances.
For example, the following declaration specifies the properties of an address object:
person {
address {
street string
city string
state string @upper @length(2)
zip number @format('#####', '#####-####') @required
}
}
An annotated type is a type followed by a sequence of zero or more annotations. All type declarations in Phase are annotated types.
Annotations provide the means to decorate types with auxiliary information, validation constraints, and formatting specifications.
An example of simple annotation is @required
, which can be applied to any
type.
An example of an annotation that requires an argument is @pattern
, which can
be applied to the string type. It takes a regular expression for validating
string instance values.
Phase comes with an number of standard annotations that correlate directly to JSON Schema validation keywords.
@required
The @required
annotation can be used with any declaration of any type in a
compound type, including declarations of compound types.
An object instance is valid if property set contains all properties with values
of the specified types declared with the @required
annotation.
Example:
{
name string @required
}
is matched by all of the following instances:
{
"name": "Bob"
}
{
"name": "Bob",
"age": 30
}
{
"name": "Bob",
"age": 30,
"email": "[email protected]"
}
Example:
{
name string @required
address {
street string
city string
state string @pattern('^[A-Z]{2}$') @required
zip string @pattern('(^[0-9]{5}$)|(^[0-9]{5}-[0-9]{4}$)') @required
} @required
}
is matched by the following:
{
"name": "Bob",
"address": {
"state": "CA"
"zip": "94041"
}
}
but fails with the following (missing zip)
{
"name": "Bob",
"address": {
"state": "CA"
}
}
and also fails with the following (missing address)
{
"name": "Bob"
}
The argument for @enum
must be an array with at least one element. Elements
in the array must be unique and may be of any type, including null
.
An instance is valid if its value is equal to one of the elements of the array
declared by @enum
.
Note: the @enum
annotation treats an argument list as an array, so array
bracket notation is optional.
Examples:
// traffic light colors
color string @enum( 'red', 'yellow', 'blue' )
// valid U.S. currency bill denominations, enumerated with optional array notation
denominations number @enum( [ 1, 2, 5, 10, 20, 50, 100 ] )
The argument for @allOf
must be an array of one or more subschemas.
The argument for @anyOf
must be an array of one or more subschemas.
The argument for @oneOf
must be an array of one or more subschemas.
The argument for @oneOf
must be a subschema.
@multipleOf(number > 0)
The argument for @multipleOf
must be a number greater than 0
. A
numeric instance is valid against multipleOf
if the result of the division
of the instance by this annotation's value is an integer.
Example:
donuts integer @multipleOf(12)
@maximum(number) [ @exclusiveMaximum(boolean) ]
The argument for @maximum
must be a number. The argument for
@exclusiveMaximum
must be a boolean. @exclusiveMaximum
is optional, but if
it is present, @maximum
must also be present.
Instance validation depends on the presence of @exclusiveMaximum
:
- If
@exclusiveMaximum
is present and evaluates to boolean valuetrue
, then an instance is valid if its value is lower than, or equal to, the value of@maximum
; - otherwise, an instance is valid if its value is strictly lower than
@maximum
.
Examples:
qty integer @maximum(5) // qty must be less than or equal to 5
qty integer @maximum(5) @exclusiveMaximum(false) // qty must be less than or equal to 5
qty integer @maximum(5) @exclusiveMaximum(true) // qty must be less than 5
@minimum(number) [ @exclusiveMinimum(boolean) ]
The argument for @minimum
must be a number. The argument for
@exclusiveMinimum
must be a boolean. @exclusiveMinimum
is optional, but if
it is present, @minimum
must also be present.
Instance validation depends on the presence of @exclusiveMinimum
:
- If
@exclusiveMinimum
is present and evaluates to boolean valuetrue
, then an instance is valid if its value is greater than, or equal to, the value of@minimum
; - otherwise, an instance is valid if its value is strictly greater than
@minimum
.
Examples:
qty integer @minimum(5) // qty must be greater than or equal to 5
qty integer @minimum(5) @exclusiveMinimum(false) // qty must be greater than or equal to 5
qty integer @minimum(5) @exclusiveMinimum(true) // qty must be greater than 5
@maxLength(number >= 0)
The argument for @maxLength
must be an integer greater than, or equal to, 0
.
A string instance is valid if its length is less than, or equal to, the value of @maxLength. The length of a string instance is defined as the number of its characters as defined by RFC 4627.
Example:
username string @maxLength(10)
@minLength(number >= 0)
The argument for @minLength
must be an integer greater than, or equal to, 0
.
A string instance is valid if its length is greater than, or equal to, the value of @minLength. The length of a string instance is defined as the number of its characters as defined by RFC 4627.
@minLength
, if absent, may be considered as being present with an integer
value 0
.
Example:
username string @minLength(3)
@pattern(string)
The argument for @pattern
must be a string, and it should be a valid regular
expression, according to ECMA 262 regular expression dialect.
A string instance is considered valid if the regular expression matches the instance successfully.
Example:
username string @pattern('^[a-zA-Z]+[a-zA-Z0-9-_]*$')
@additionalItems(boolean)
The argument for @additionalItems
must be boolean value.
An array instance is always valid if the @additionalItems
annotation is not
present. If present with a false value, then an array instance is only valid if
its size is less than, or equal to, the size of the declared array type.
@additionalItems
, if absent, may be considered as being present with a
boolean value true
.
@maxItems(integer >= 0)
The argument for @maxItems
must be an integer greater than, or equal to, 0
.
An array instance is valid if its size is less than, or equal to, the value of
@maxItems
.
@minItems(integer >= 0)
The argument for @minItems
must be an integer greater than, or equal to, 0
.
An array instance is valid if its size is greater than, or equal to, the value
of @maxItems
.
@minLength
, if absent, may be considered as being present with an integer
value 0
.
@uniqueItems(boolean)
The argument for @uniqueItems
must be a boolean value.
If @uniqueItems
is present and its value is true, then an array instance is
only valid if all of its elements are unique.
@uniqueItems
, if absent, may be considered as being present with a
boolean value false
.
A compound type declares the properties that must be matched by valid object instances. Like any type, compound types can be decorated with a sequence of zero or more annotations.
This is an example of an anonymous root declaration of a compound type with annotations:
{
x number
y number
} @minProperties(2) @maxProperties(2) @patternProperties({ "[xy]" number })
Here is another example with a nested compound type with annotations:
{
part number
codes {
p1 number
} @patternProperties({ p number })
}
@maxProperties(number >= 0)
The argument for @maxProperties
must be an integer greater than, or equal to
0
.
An object instance is valid if the number of its properties is less than, or
equal to, the value of @maxProperties
.
@minProperties(number >= 0)
The argument for @minProperties
must be an integer greater than, or equal to
0
.
An object instance is valid if the number of its properties is greater than, or
equal to, the value of @maxProperties
.
@minProperties
, if absent, may be considered as being present with a
boolean value 0
.
{
p1 boolean
} @patternProperties({
p boolean
})
Here is another example with a nested compound type with annotations:
{
part number
codes {
p1 number
} @patternProperties({ p number })
}