/json-types

Primary LanguageJavaScript

JSON X-Type

JSON X-Type is a data type format for describing JSON-like structures in a simple and natural way. Any valid JSON could be validated against a JSON X-Type definition.

Reserved Keywords

Keyword Description Usage
string String type. key, value
number Number type. value
boolean Boolean type. value
null The null value. The string "null" value doesn't have a special meaning. value
undefined Value is not set (the corresponding key is not present). value
array Array generic. key
any Any value (not validated). value
$and Refers to the intersection of an array members (🔗). key
$descriptions Object with descriptions of the fields at the same level. key
$ref Reference to another JSON X-Type (🔗). key
$schema A literal JSON Schema definition (🔗). key

The list could be extended with other $-prefixed keywords. So it's a good idea to escape any values that start with $ using the $literal prefix.

Prefixes

Prefixes are used to modify what's going after them. Prefixes and the actual values are separated by a colon.

Prefix Description Usage
$literal Escapes a literal value (🔗). key, value

Objects

Object literals describe object-like schemas:

{
  "name": "string",
  "age": "number"
}

The example above defines an object with only two properties, both of which are required.

Also, it is possible to use the string type as a key to describe records:

{
  "string": "any"
}

Arrays

Array literals allow defining multiple available options, one of which is applicable:

["string", "undefined"]

Types Combining

It is possible to combine several types into one using the $and keyword:

{
  "$and": [{"foo": "string"}, {"bar": "number"}]
}

A result is an object that contains all of the properties of all the items:

{
  "foo": "string",
  "bar": "number"
}

A TypeScript analogy of the $and operator is the following:

type And<U> = (U extends any ? (k: U) => void : never) extends (
  k: infer I
) => void
  ? I
  : never

type Combined = And<{foo: string} | {bar: number}> // {"foo": "string"} & {"bar": "number"} ≡ {"foo": "string", "bar": "number"}

Effectively, it applies the AND relation between the array members, replacing the XOR relation.

Note that it doesn't make sense to combine primitive types or objects that have common properties with different types:

{
  "$and": [{"foo": "string"}, {"foo": "number"}]
}

The example above results in foo being both string and number, effectively equivalent to TypeScript's never type.

The $schema properties also cannot be combined using the $and notation.

Impossible combinations should result in the undefined type.

Literals Escaping

Whenever there is a need to use a literal string value instead of a reserved keyword, it needs to be prepended with the $literal prefix:

{
  "$literal:string": "boolean"
}

This will check for an object with the string key of a boolean value, e.g.:

{
  "string": true
}

References

It is possible to refer to other JSON X-Types using the JSON Pointer syntax:

{
  "foo": {"$ref": "#/path/to/field"}
}

A reference must be resolved relative to the file it appears in. The $ref expression must be replaced with the value of the field it refers to. Any other properties passed alongside the $ref must be ignored.

Alternatively, the $ref keyword could be used as a prefix which is easier to write and read (but the support is up to the implementation):

{
  "foo": "$ref:#/path/to/field"
}

If a reference is not resolved, it should be treated as any.

Literal Schemas

If there's something that cannot be expressed in terms of JSON X-Type, it should go under this key:

{
  "$schema": {
    "type": "string",
    "contentMediaType": "application/jwt",
    "contentSchema": {"type": "array"}
  }
}

Types Extending

Possible extensions described here