JSON is a very simple, widely established syntax for storing and exchanging objects, arrays and atomic values that are strings, numbers, booleans and nulls.
{
"name" : "Cooper",
"first" : "Sheldon",
"middle" : "Lee",
"multiplication" : "MITOSIS",
"birthdate" : "1980-02-26",
"picture" : "0123456789abcdef",
"friends" : [ 1, 2, 4, 5 ]
}
TYSON, defined in a
specification of its own, is a superset of JSON that supports type annotations in a language-agnostic way.
Object, array and atomic types can be added and defined by users at will, and used as annotations.
Validation of types beyond TYSON builtin types (object, array, string, integer, decimal, double, boolean, null) is beyond the scope of TYSON. This is where JSound comes into play. JSound allows users to define their own types, and perform validation and annotation of TYSON documents.
("person") {
"name" : "Cooper",
"first" : "Sheldon",
"middle" : "Lee",
"multiplication" : ("cell-division-kind") "MITOSIS",
"birthdate" : ("date") "1980-02-26",
"picture" : ("hexBinary") "0123456789abcdef",
"friends" : ("ids") [ 1, 2, 4, 5 ]
}
This is a TYSON value. A candidate instance may or not be valid against a type.
A candidate instance may be in particular a JSON value or even, often, a JSON object.
In the TYSON model, values can be objects, arrays, or atomics.
An object has an unordered list of field/value pairs, and is annotated with an object type (most often implicitly the builtin "object" type).
An array has an ordered list of values, and is annotated with an array type (most often implicitly the builtin "array" type).
An atomic has a value annotated with an atomic type.
Typically, the candidate instance will have been freshly parsed. In the case that it is JSON, it will only have atomics of type string, integer, decimal, double, boolean and null, as well as "raw" object and array types.
An annotated instance is TYSON value. It is obtained from a valid candidate instance by annotating all values with the types, as specified in the schemas. If the type annotations in the candidate instance are already identical to those expected by the schema, it is identical to the candidate instance.
A schema document is a JSON object which defines types against which candidate instances are being validated. A schema document is also a candidate instance and must be valid against the meta schema type. A schema document must also fulfill additional consistency constraints not captured by the meta schema.
A schema set is a set of schemas that may refer to each other's type names. Schema sets may not have colliding type names.
It is left out of scope of this specification how to assemble schema documents into consistent schema sets, in particular, additional specifications may introduce naming conventions such as the use of namespaces to prevent collisions.
A Meta schema is a JSON object that defines the type against which all schema socuments are valid including itself.
A schema document defines types, which may or may not be anonymous. A candidate instance may or may not be valid against a type.
A candidate instance can be annotated against a type, which results in an annotated instance.
There are four kinds of types: atomic, array, object and union. Types are represented with objects that are nested in a schema document. Named types can also be referred to with their names.
Type names are strings. Some strings are reserved by TYSON. Some others are reserved by JSound.
The type names defined and reserved by TYSON are: object, array, integer, decimal, double, string, boolean, null.
The type names defined and reserved by JSound are: value, atomic, anyURI, base64Binary, hexBinary, date, dateTime, time, dateTimeStamp, duration.
All above types are called builtin types.
2.10. Lexical and value space
The lexical space of an atomic type is the set of all (string) literals used to serialize this type in some syntax. For example, lexical values of the type boolean are "true" and "false"
The value space of an atomic type is the set of all (logical) typed values. For example, typed values of the type boolean are the mathematical concepts of trueness and falseness (boolean algebra).
An atomic type also includes a mapping from lexical to typed values, as well as a canonical mapping from a typed value to a "default" literal.
These definitions are consistent with the lexical and value spaces of XML Schema atomic types.
JSound focuses on validating lexical values, that is, literals. Converting these literals to typed values for further processing is left to the host language, based on its own specific type system.
JSound types are organized in a subtype hierarchy. There is a number of builtin types, and a schema document can define new derived types that restrict the value space of their base types.
JSound's type hierarchy is very strict about value spaces. The value space of a derived type is always a subset of the value space of its base type. To restrict its base type's value space, a derived type can provide new facets, or make existing facets more restrictive.
The subtype relationship is defined as the reflexive and transitive closure as the derived type relationship.
The supertype relationship is defined as the reflexive and transitive closure as the base type relationship.
JSound also has a very compact syntax making schemas look similar to actual candidate instances. This syntax is only a syntactic sugar for the actual, more verbose JSound schema syntax. The design goal was that 80% of the schemas used in real life can be expressed with that compact syntax for high productivity, and the more verbose syntax can be used for more complex user needs.
Chapter 3. Schema Documents
Schema documents define multiple types.
3.2. Schema document properties
Schema documents are (serialized) JSON objects which have the following properties
metadata (JSON object): contains general schema properties such as name (string/anURI), previous (string/anyURI), date (string/date), authors (array of strings).
types (JSON array containing objects representing types) : the types defined in this document. How these objects look like is explained in subsequent sections.
3.3. Type names and references to types
Type names are strings. Type names are used to (optionally) name types, or to refer to another type as a base type.
Types that can be referred with their type name across an entire schema set.
It is forbidden to define types with builtin names (either TYSON or JSound). Otherwise, a static error JDST0013
is raised.
When a type name cannot be resolved, a static error JDST0002
is raised.
If two types in the same schema set have the same name, a static error JDST014
is raised. It is thus the responsibility of the user to not assemble a schema set with schema that use colliding type names. Grouping type names in namespaces, as well as a machinery to include namespaces in type names are out of scope of this specification, but conventions may be added at a later point in time.
3.4. Recursive definitions
Recursive definitions are allowed in general, for example, the type of a value in a pair of an object type may be identical to that object type to allow arbitrary nesting.
However, there are a few restrictions in order to avoid cycles in the type hierarchy: a type may not be derived from itself, indirectly or indirectly, and a union type may not be in the transitive closure of its own membership relationship.
This Schema document defines two atomic types and with the names "small-number" and "big-number".
{
"types" : [
{
"name" : "small-number",
"kind" : "atomic",
"baseType" : "integer",
"enumeration" : [ 1, 2, 4, 8 ]
},
{
"name" : "big-number"
"kind" : "atomic",
"baseType" : "integer",
"enumeration" : [ 1000, 2000, 4000, 8000 ]
}
]
}
This schema document defines one object type named "small-and-big".
{
"types" : [
{
"name" : "small-and-big",
"kind" : "object",
"content" : [
{
"name" : "small",
"type" : "small-number",
"required" : true
},
{
"name" : "big",
"type" : "big-number"
}
]
}
]
}
Given this set of two schema documents, the following JSON object:
{
"small" : 4
}
is valid against the type named "small-and-big".
This JSON object is not valid against the type "small-and-big", because the value associated with "big" is not in the value space of the atomic type "big-number".
{
"small" : 4,
"big" : 3
}
There are four kinds of types: atomic, object, array and union.
Types are either builtin or derived.
The topmost type is builtin and is named "value".
The topmost object type is builtin and is named "object".
The topmost array type is builtin and is named "array".
The topmost atomic type is builtin and is named "atomic". There are also further builtin atomic types such as "date", "time", "hexBinary", etc.
Derived types are always defined by restricting the value space of a base type by means of facets. They have a JSON object representation.
Derived object types may be derived from any other object type. Derived array types may be derived from any other array type. Derived union types are always directly derived from "value". Derived atomic types may be derived from any other atomic type except "atomic".
3.7. Derived type properties
A derived type has the following properties:
kind (string): the kind of the type. One of "atomic, "object", "array", "union".
name (string): a string containing the name (as defined above) of this type.
baseType (string): a string containing the name of the base type.
metadata (value): free content (documentation, comments, ...).
various facets properties. Which facets are available defines on the kind of the type.
There are the following constraints on these properties:
An error JDST0001
is raised if $kind is missing.
An error JDST0003
is raised if any other value is encountered for kind.
If name is used in a top-level type, it must match the corresponding object field, otherwise an error JDST0004
is raised.
baseType must refer to a known type - builtin, in the same schema document or in another schema document in the schema set. Otherwise, a static error JDST0002
is raised.
Recursion is not allowed through the baseType property, i.e., no cycles in the base type relation are allowed. If a cycle is detected, then error JDST0018
is raised.
If kind is "object", baseType must be the name of an existing object type. If absent, it is by default "object".
If kind is "array", baseType must be the name of an existing array type. If absent, it is by default "array".
If kind is "union", baseType must be "value" if provided. If absent, it is by default "value".
If kind is "atomic", baseType must be the name of an existing atomic type, but not "atomic". It cannot be absent.
If kind and baseType are not consistent as specified above, a static error JDST0007
is raised.
Here is an example of an invalid schema document, because it does not fulfill many of the above constraints.
{
"types" : [
{
"name" : "type1",
"kind" : "atomic",
"baseType" : "object", (: base type MUST also be an atomic type :)
"maxInclusive" : 4
},
{
"name" : "object1",
"kind" : "object",
"baseType" : "type1" (: base type MUST be "object":)
"content" : {}
},
{
"name" : "object2",
"kind" : "object",
"baseType" : "object1" (: base type MUST be "object":)
}
}
}
A derived type inherits its base type's facets. It can set a new facet if its base type does not already have it, or it can redefine a facet. However, a facet can only be redefined if its new value is more restrictive that in its base type, otherwise, a static error JDST0005
is raised.
Some facets, like constraints, are accumulative facets. This means that redefining them is done by adding new constraints to the already present ones, without raising JDST0007
.
There are two facet common to all types:
enumeration (array): Constrains a value space to a specified set of values. If one of these values is not valid against the type at hand, then JDST0006
is raised.
constraints (array of strings): implementation-defined constraints defined in an implementation-defined host language, the choice of which is orthogonal to this specification.
{
"types" : [
{
"name" : "two-objects",
"kind" : "object"
(: "baseType" : "object" is implicit :)
"enumeration" : [ { "foo" : "bar" }, {} ] (: only these two objects :)
},
{
"name" : "uniform-array",
"kind" : "array"
(: "baseType" : "array" is implicit :)
"constraints" : [ "every $i in 1 to size($$) satisfies deep-equals($$($i), $$(1))" ] (: all members must be the same :)
},
]
}
The following JSON object is valid against type "two-objects".
{ "foo" : "bar" }
The following JSON array is valid against type "uniform-array".
[ 42, 42, 42 ]
The entire type hierarchy is shown below. "value", "atomic" and user union types are not instantiable and cannot be used as type annotations in TYSON syntax. An instantiable subtype must be used instead.
Atomic types match atomics (TYSON leaf values: strings, numbers, booleans, nulls, etc).
Atomic types, except the topmost "atomic", have a lexical space (a set of literals denoting the values), a value space (a set of actual values), and a lexical mapping which maps the former into the latter. This is consistent with the atomic type requirements drawn out in the TYSON specification. These types may thus all be used as TYSON type annotations.
An atomic type can be either the topmost atomic, or a primitive builtin type, or a builtin type derived from a primitive type, or a user-defined type derived from any other atomic type (except atomic).
A derived atomic type can be defined by restricting the value space of another (non-topmost) atomic type by specifying atomic facets. A restriction can also be made with the general facet enumeration.
Given the following schema document:
{
"types" : [
{
"name" : "foo-and-bar",
"kind" : "atomic",
"baseType" : "string",
"enumeration" : [ "foo", "bar" ]
},
{
"name" : "digits",
"kind" : "atomic",
"baseType" : "integer",
"minInclusive" : 1,
"maxExclusive" : 10
},
{
"name" : "few-digits",
"kind" : "atomic",
"baseType" : "my:digits",
"enumeration" : [ 4, 6 ]
}
]
}
The strings "foo" and "bar" are valid against type named "foo-and-bar". The string "foobar" and the array [ "foo", "bar" ] are not.
The atomics (integers) 2 and 7 are valid against the type named "digits". The string "2", the integer 0 and the array [ "foo", "bar" ] are not.
The integer 4 is valid against the type named "few-digits". The integer 2, the integer 0 and the array [ "foo", "bar" ] are not.
4.3. Builtin Atomic Types
A number of builtin atomic types are predefined. Most of them have counterparts in XML Schema 1.1, because they are very useful also in JSON (for example : dates, times, ...). In particular, they have the same value space, the same lexical space, the same lexical mapping and (for primitive types) the same associated set of atomic facets.
Some of these builtin types are primitive and marked as such below. Others are derived from another builtin type.
There is also a special builtin type atomic, which is a supertype of all primitive types and, by transition, of all atomic types.
The lexical space of dateTime as defined in XML Schema 1.1 is a superset of the date representation defined in
ECMAScript. In addition, JSound extends the lexical representation of respectively date, time, dateTime defined above, to allow the format defined in
RFC 2822 (nonterminals date, time, date-time respectively). This is because many JavaScript implementations do so.
Restriction is done using the general facets, or the following atomic facets (they must be available for the base type).
These facets are defined in XML Schema 1.1. For convenience, the summary from the XML Schema 1.1 specification is provided below. Which primitive type has which facets is defined in XML Schema 1.1 as well.
The following atomic facets are available for the primitive types string, anyURI, base64Binary, hexBinary:
length (integer): Constraining a value space to values with a specific number of units of length, where units of length varies depending on the base type.
minLength (integer): Constraining a value space to values with at least a specific number of units of length, where units of length varies depending on the base type.
maxLength (integer): Constraining a value space to values with at most a specific number of units of length, where units of length varies depending on the base type.
The following atomic facets are available for the primitive types date, dateTime, time, duration, decimal, double:
maxInclusive (atomic): Constraining a value space to values with a specific inclusive upper bound.
maxExclusive (atomic): Constraining a value space to values with a specific exclusive upper bound.
minExclusive (atomic): Constraining a value space to values with a specific exclusive lower bound.
minInclusive (atomic): Constraining a value space to values with a specific inclusive lower bound.
The following atomic facets are available for the primitive type decimal:
totalDigits (integer): Restricting the magnitude and arithmetic precision of values in the value spaces of decimal and datatypes derived from it.
fractionDigits (integer): Placing an upper limit on the arithmetic precision of decimal values.
The following atomic facets are available for the primitive types date, dateTime, time:
explicitTimezone ("required", "prohibited" or "optional"): Requiring or prohibiting the time zone offset in date/time datatypes.
All above facets are non-accumulative. Facets are all inherited by a derived type from its base type, but can be overriden. The error JDST0007
is raised if they are redefined in a less restrictive way.
4.5. Mapping to a few languages
This non-normative section shows how builtin types can be mapped to existing languages (Python, JavaScript). Asterisks (*) indirect an impedance mismatch where the mapping is not perfect.
Table 4.1. Mapping of atomic types
JSound | Python | JavaScript |
---|
string | str | String |
boolean | bool | Boolean |
null | any type | Null |
double | float | Number |
decimal | float* | Number* |
integer | int | Number* |
anyURI | str | String |
base64Binary | bytes | Uint8Array |
hexBinary | bytes | Uint8Array |
date | date | N/A |
dateTime | datetime | N/A |
time | time | N/A |
dateTimeStamp | aware datetime | Date |
duration | timedelta | N/A |
Validation of a candidate instance I against an object type T is defined as follows.
If I is not an atomic value, then I is not valid agains T.
Otherwise, if T is "atomic", then I is valid against T.
Otherwise, if T is a primitive atomic type, then I is valid against T if its lexical representation is in the lexical space of this primitive atomic type.
Otherwise, I is valid against T if it is valid against all its facets (which means that the lexical representation is in the lexical space of the derived type). Note that, by design, this also implies that it is valid against the base type of T.
Object types match objects.
An object type can be defined by restricting the value space of "object" by specifying a layout (type of the values, required or not, ...). A restriction can also be made with the general type facets "enumeration" and "constraints".
Any object type may be used as a TYSON type annotation.
Against the following object type:
{
"types" : [
{
"name" :"only-foo",
"kind" : "object",
"content" : [
{
"name" : "foo",
"type" : "string",
"required" : true
}
],
"closed" : true
},
{
"name" : "foo-bar-and-arrays",
"kind" : "object",
"content" : [
{
"name" : "foo",
"type" : "string",
"required" : true
},
{
"name" : "bar",
"type" : "boolean"
}
]
}
]
}
The objects { "foo" : "bar" } and { "foo" : "foo" } are valid against the type "only-foo" because the foo pairs are present, and are strings.
The object {} is not because the foo pair is missing.
The object { "foo" : "bar", "bar" : "foo" } is not because no other pair than "foo" is allowed (closed object type).
The objects { "foo" : "bar" } and { "foo" : "bar", "bar" : true, "foobar" : [ 3.14 ] } are valid against the type "foo-bar" because the foo pairs are strings, bar is not required and the object type is open.
The objects {} and { "bar" : "foo" } and { "foo" : "bar", "bar" : "foo" } are not because the foo pair is missing or the bar pair is not a boolean.
5.3. Builtin Object Type
There is one topmost, builtin object type named object, against which all objects are valid.
This topmost type has its content facet as the empty array, and its closed facet as false.
Restriction is done using the general facets, or the following object facets.
The content facet is an array of objects containing the layout definition. By default, it is the empty array.
Each member in content is called a field descriptor. Each field descriptor has the following properties.
name (string): the field name - required (JDST0008
if absent).
type (string or object) - required (JDST0008
if absent): the name of a type (a string) or the type itself (an object) that the value must match. JDST0002
is raised if if is a name that cannot be resolved.
required (boolean) - optional: indicates that the pair is required. Default is false.
default (value) - optional: indicates a default value to be taken the value is missing in the serialized instance. required is then ignored. Adding the default value to a valid instance is performed as part of the annotation process.
unique (boolean) - optional: indicates that the field value must be unique within the parent array, if the object type is used as content of an array type. Validity against unique is handled as part of array validation (see the appropriate section). Default is false.
If the closed facet of the base type is true, it is not allowed to add field descriptors for fields not present in the content facet of base type, or of its transitive closure, otherwise JDST0010 is raised.
The content facet is cumulative, in that the baseType's field descriptors are automatically inherited if absent in the derived type, and the values of the field descriptors are inherited as well if the field descriptor is present in both the base type and the derived type, but a field descriptor value is absent in the derived type.
If a field descriptor in the content facet is overriden, it must be more restrictive, i.e., its type must be a subtype of the type associated to this field by the closest super type which does so. Also, required, if true, cannot be set back to false. If any of these two constraints is not met, JDST0011
is raised.
The value of unique cannot be changed when redefining a field descriptor of the base type.
An object $o is valid against the content facet if the following conditions are met:
For each field descriptor $v such that $v.required is true and $v.default is absent, there must be a pair named $v.name in $o.
For each field descriptor $v in the field descriptor, if $o.($v.name) exists, then $o.($v.name) must be valid against the type $v.type.
The closed facet is a boolean. It specifies whether pairs not specified in the content facet are to be refused. The default is the same as the baseType, in particular, the default is false if baseType is "object".
If the closed facet of the base type is true, it cannot be set back to false, otherwise DST0009 is raised.
All objects are valid against the closed facet if it is set to false. If it is set to true, an object $o is valid against the closed facet if all its fields have corresponding field descriptors in the content facet, or in the content facet of any super type.
Validation of a candidate instance I against an object type T is defined as follows.
If I is not an object, then I is not valid agains T.
If I is an object, it is valid against T if it is valid against both of the facets "content" and "closed" of T. Note that, by design, this also implies that it is valid against the base type of T.
Array types match arrays.
An array type can be defined by restricting the value space of an array base type by specifying a layout (type of the members) or size bounds. A restriction can also be made with the general type facet $enumeration.
Any array type may be used as a TYSON type annotation.
{
"types" : [
{
"name" : "strings",
"kind" : "array",
"content" : "string"
},
{
"name" : "less-than-five-members",
"kind" : "array",
"content" : "string",
"maxLength" : 5
},
{
"name" : "all-less-than-ten",
"kind" : "array",
"content" : "integer"
}
]
}
[ "foo " "bar" ] is valid against the type named "strings" but not [ 1, 2, "foo" ].
[ "foo " "bar" ] is valid against the type named "less-than-five-members" but not [ "foo", "foo", "foo", "foo", "foo", "foo" ].
[ 1, 3, 5 ] is valid against the type named "all-less-than-ten" but not [ 1, 3, 72, null ].
There is one topmost, builtin array type named array, against which all arrays are valid.
Restriction is done using the general facet enumeration, or the following array facets.
JSound supports the following array facets.
These three facets are, by default, inherited from the base type but can be overriden. JDST0007
is raised if one of the facets is redefined by the derived type in a less restrictive way than in its base type.
The content facet is a string or an object. The name of a type (string) or the type itself (an object) that all members must match.
The content facet of the "array" type is absent and all arrays are valid against it.
The minLength facet is an integer. It constraints the number of members in the instance with a lower bound.
The minLength facet of the "array" type is 0 and all arrays are valid against it.
The maxLength facet is an integer. It constraints the number of members in the instance with an upper bound.
The maxLength facet of the "array" type is positive infinity and all arrays are valid against it.
Validation of a candidate instance I against an array type T is defined as follows.
If I is not an array, then I is not valid agains T.
If I is an array, it is valid against T if it is valid against all three facets "content", "minLength" and "maxLength" of T. Note that, by design, this also implies that it is valid against the base type of T.
Furthermore, if the content of the array type is an object type and the "unique" field descriptor is used in the content of that object type, then across all object children of the candidate instance, all compound values associated with fields marked as unique must appear at most once. Structured values are recursively compared. Atomic values are compared in their value space.
The value space of a union type is the union of the value spaces of all its member types.
All union types have directly the topmost "value" as their base type and restrict the value space by specifying the content facet. General facets cannot be used. "value" can be considered a builtin union type with "object", "array" and "atomic" as its member types.
A union type MAY NOT be used as type annotations in TYSON, otherwise an error JDST0012
is raised. A member type of the union type must be used instead.
A consequence of the way union types are defined in a restrictivey way is that a union type will always be expressible as a flat union of non-union types.
{
"types" : [
{
"name" : "string-or-integer-array",
"kind" : "union",
"content" : [ "string", { "$kind" : "array", "$content" : "integer" } ],
},
{
"name" : "just-two",
"kind" : "union",
"content" : [ "string", { "$kind" : "array", "$content" : "integer" } ]
}
]
}
"foo", "bar" and [ 1, 2, 3 ] are valid against the type named "string-or-integer-array" but 3.14 and true are not.
"foo", and [ 1, 2, 3, 4 ] are valid against the type named "just-two" but [ null ] and 3.14 are not.
The specification of member types is done using one (compulsory) content facet, and optionally general facets.
Union types can be derived from another union type by overriding the content facet. JDST0017
is raised if it does hot hold that all members of the overriding content facet are subtypes (directly or indirectly) of a member of the content facet of the base type.
The content facet of the topmost "value" type is implicitly [ "object", "array", "atomic" ]: its value space contains all possible JSON/TYSON values.
The content facet is an array of strings or objects. Each member in the array is the name of a type (string) or the member type itself (an object). A value is valid against this facet if it is valid against any of the types in this list.
It is forbidden for a union type to appear in its own member list. The same is also forbidden transitively, i.e., a union type cannot appear in its transitive membership relation. If such a cycle is detected, then error JDST0018
is raised.
Validation of a candidate instance I against a union type T is defined as follows.
I is valid against T if it is valid against at least one member type (in the content facet of T).
Chapter 9. Schema of Schemas
{
"types" : [
{
"name" : "schema",
"kind" : "object",
"content" : [
{
"name" : "metadata",
"type" : {
"kind" : "object",
"content" : [
{
"name" : "name",
"type" : "string"
},
{
"name" : "previous",
"type" : "string"
},
{
"name" : "date",
"type" : "string"
},
{
"name" : "authors",
"type" : "array"
}
]
}
},
{
"name" : "types",
"type" : {
"kind" : "array",
"content" : "type"
},
"required" : true
}
]
},
{
"name" : "atomic-type-name",
"kind" : "atomic",
"baseType" : "string",
"enumeration" : [ "atomic" ]
},
{
"name" : "object-type-name",
"kind" : "atomic",
"baseType" : "string",
"enumeration" : [ "object" ]
},
{
"name" : "array-type-name",
"kind" : "atomic",
"baseType" : "string",
"enumeration" : [ "array" ]
},
{
"name" : "union-type-name",
"kind" : "atomic",
"baseType" : "string",
"enumeration" : [ "union" ]
},
{
"name" : "type",
"kind" : "object",
"content" : [
{ "name" : "name", "type" : "string", "required" : true, "unique" : true },
{ "name" : "kind", "type" : "string, "required" : true },
{ "name" : "baseType", "type" : "string" }
]
},
{
"name" : "atomic-type",
"kind" : "object",
"baseType" : "type",
"content" : [
{ "name" : "kind", "type" : "atomic-type-name, "required" : true },
{ "name" : "baseType", "type" : "string", "required" : true },
{ "name" : "pattern", "type" : "string" },
{ "name" : "length", "type" : "integer" },
{ "name" : "minLength", "type" : "integer" },
{ "name" : "maxLength", "type" : "integer" },
{ "name" : "totalDigits", "type" : "integer" },
{ "name" : "fractionDigits", "type" : "integer" },
{ "name" : "maxInclusive", "type" : "atomic" },
{ "name" : "maxExclusive", "type" : "atomic" },
{ "name" : "minExclusive", "type" : "atomic" },
{ "name" : "minInclusive", "type" : "atomic" },
{ "name" : "enumeration",
"type" : {
"kind" : "array",
"content" : "atomic"
}
},
{ "name" : "explicitTimezone",
"type" : {
"kind" : "atomic",
"baseType" : "string",
"enumeration" : [ "required", "prohibited", "optional" ]
}
}
}
},
{
"name" : "object-type",
"kind" : "object",
"baseType" : "type",
"content" : [
{ "name" : "kind", "type" : "object-type-name" },
{ "name" : "content", "type" : { "kind" : "array", "content" : "field-descriptor" } },
{ "name" : "value-type", "type" : "type-or-reference" },
{ "name" : "closed", "type" : "boolean" },
{ "name" : "enumeration",
"type" : {
"kind" : "array",
"content" : [ "atomic" ]
}
},
]
},
{
"name" : "field-descriptor",
"kind" : "object",
"baseType" : "type",
"content" : [
{ "name" : "name", "type" : "string", "required" : true, "unique" : true },
{ "name" : "type", "type" : "type-or-reference", "required" : true },
{ "name" : "required", "type" : "boolean" }
{ "name" : "default", "type" : "value" }
},
{
"name" : "array-type",
"kind" : "object",
"baseType" : "type",
"content" : [
{ "name" : "kind", "type" : "array-type-name" },
{ "name" : "content", "type" : "array-content-descriptor" },
{ "name" : "minLength", "type" : "integer" },
{ "name" : "maxLength", "type" : "integer" },
{ "name" : "enumeration",
"type" : {
"kind" : "array",
"content" : [ "atomic" ]
}
}
]
},
{
"name" : "array-content-descriptor",
"kind" : "array",
"content" : "type-or-reference",
"minLength" : 1,
"maxLength" : 1
},
{
"name" : "union-type",
"kind" : "object",
"content" : [
{ "name" : "kind", "type" : "union-type-name" },
{ "name" : "content", "type" : "type-or-reference" }
}
},
{
"name" : "type-or-reference",
"kind" : "union",
"content" : [ "string", "type" ]
}
]
}