diff --git a/source/basics.rst b/source/basics.rst
index f130aef4..7c01fded 100644
--- a/source/basics.rst
+++ b/source/basics.rst
@@ -25,6 +25,28 @@ completely valid schema that will accept any valid JSON.
--
{ "an": [ "arbitrarily", "nested" ], "data": "structure" }
+|draft6|
+
+You can also use ``true`` in place of the empty object to represent a schema
+that matches anything, or ``false`` for a schema that matches nothing.
+
+.. schema_example::
+
+ true
+ --
+ // This accepts anything, as long as it's valid JSON
+ 42
+ --
+ "I'm a string"
+ --
+ { "an": [ "arbitrarily", "nested" ], "data": "structure" }
+
+.. schema_example::
+
+ false
+ --X
+ "Resistance is futile... This will always fail!!!"
+
The type keyword
----------------
@@ -77,10 +99,17 @@ more information.
Declaring a unique identifier
-----------------------------
-It is also best practice to include an ``id`` property as a unique
+It is also best practice to include an ``$id`` property as a unique
identifier for each schema. For now, just set it to a URL at a domain
you control, for example::
- { "id": "http://yourdomain.com/schemas/myschema.json" }
+ { "$id": "http://yourdomain.com/schemas/myschema.json" }
The details of `id` become more apparent when you start `structuring`.
+
+|draft6|
+
+.. draft_specific::
+
+ --Draft 4
+ In Draft 4, ``$id`` is just ``id`` (without the dollar-sign).
diff --git a/source/conf.py b/source/conf.py
index c7c8468b..0e398358 100644
--- a/source/conf.py
+++ b/source/conf.py
@@ -20,7 +20,7 @@
sys.path.insert(0, os.path.abspath(os.path.dirname('__file__')))
# The standard of JSON Schema to test the examples against
-jsonschema_standard = 4
+jsonschema_standard = 6
rst_prolog = """
.. role:: new
@@ -55,7 +55,7 @@
# General information about the project.
project = u'Understanding JSON Schema'
-copyright = u'2013-{0}, Michael Droettboom, Space Telescope Science Institute'.format(
+copyright = u'2013-2016 Michael Droettboom, Space Telescope Science Institute; © 2016-{0} Michael Droettboom'.format(
datetime.datetime.now().year)
# The version info for the project you're documenting, acts as replacement for
@@ -63,9 +63,9 @@
# built documents.
#
# The short X.Y version.
-version = '1.9alpha'
+version = '6.0'
# The full version, including alpha/beta/rc tags.
-release = '1.9alpha'
+release = '6.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/source/conventions.rst b/source/conventions.rst
index 51f6c79d..175b9a51 100644
--- a/source/conventions.rst
+++ b/source/conventions.rst
@@ -35,6 +35,27 @@ JSON in a few different languages:
For C, you may want to consider using `Jansson
`_ to read and write JSON.
+Draft-specific notes
+--------------------
+
+The JSON Schema standard has been through a number of revisions or "drafts". The
+most important are Draft 6, the most recent at the time of this writing, and
+Draft 4, on which a lot of production software was built, and the draft for
+which an earlier version of this book was written.
+
+The text is written to encourage the use of Draft 6 and gives
+priority to the latest conventions and features, but where it differs from Draft
+4, those differences are highlighted in special call-outs. If you only wish to
+target Draft 6, you can safely ignore those sections.
+
+|draft6|
+
+.. draft_specific::
+
+ --Draft 4
+ This is where anything pertaining to an old draft would be mentioned.
+
+
Examples
--------
diff --git a/source/index.rst b/source/index.rst
index 0154936c..4d71f655 100644
--- a/source/index.rst
+++ b/source/index.rst
@@ -24,7 +24,7 @@ validator---just yet.
.. note::
- This book describes JSON Schema draft 4. The most recent version is draft 7
+ This book describes JSON Schema draft 6. The most recent version is draft 7
--- stay tuned, updates are coming! Earlier and later versions of JSON Schema
are not completely compatible with the format described here.
diff --git a/source/reference/array.rst b/source/reference/array.rst
index 77304abb..46cf48aa 100644
--- a/source/reference/array.rst
+++ b/source/reference/array.rst
@@ -34,14 +34,15 @@ array may be of a different type.
single: array; items
single: items
single: additionalItems
+ single: contains
Items
'''''
By default, the elements of the array may be anything at all.
However, it's often useful to validate the items of the array against
-some schema as well. This is done using the ``items`` and
-``additionalItems`` keywords.
+some schema as well. This is done using the ``items``,
+``additionalItems``, and ``contains`` keywords.
There are two ways in which arrays are generally used in JSON:
@@ -91,6 +92,29 @@ number:
// The empty array is always valid:
[]
+|draft6|
+
+While the ``items`` schema must be valid for **every** item in the array, the
+``contains`` schema only needs to validate against one or more items in the
+array.
+
+.. schema_example::
+
+ {
+ "type": "array",
+ "contains": {
+ "type": "number"
+ }
+ }
+ --
+ // A single "number" is enough to make this pass:
+ ["life", "universe", "everything", 42]
+ --X
+ // But if we have no number, it fails:
+ ["life", "universe", "everything", "forty-two"]
+ --
+ // All numbers is, of course, also okay:
+ [1, 2, 3, 4, 5]
.. index::
single: array; tuple validation
diff --git a/source/reference/generic.rst b/source/reference/generic.rst
index 75dbc17f..ca7a63cd 100644
--- a/source/reference/generic.rst
+++ b/source/reference/generic.rst
@@ -8,21 +8,22 @@ for all JSON types.
single: metadata
single: title
single: description
+ single: examples
.. _metadata:
Metadata
--------
-JSON Schema includes a few keywords, ``title``, ``description`` and
-``default``, that aren't strictly used for validation, but are used to
-describe parts of a schema.
+JSON Schema includes a few keywords, ``title``, ``description``, ``default``, and
+``examples`` that aren't strictly used for validation, but are used to describe
+parts of a schema.
The ``title`` and ``description`` keywords must be strings. A "title"
will preferably be short, whereas a "description" will provide a more
lengthy explanation about the purpose of the data described by the
schema. Neither are required, but they are encouraged for good
-practice.
+practice, and can make your schema "self-documenting".
The ``default`` keyword specifies a default value for an item. JSON
processing tools may use this information to provide a default value
@@ -30,12 +31,23 @@ for a missing key/value pair, though many JSON schema validators
simply ignore the ``default`` keyword. It should validate against the
schema in which it resides, but that isn't required.
+|draft6| The ``examples`` keyword is a place to provide an array of examples
+that validate against the schema. This isn't used for validation, but may help
+with explaining the effect and purpose of the schema to a reader. Each entry
+should validate against the schema in which is resides, but that isn't strictly
+required. There is no need to duplicate the ``default`` value in the
+``examples`` array, since ``default`` will be treated as another example.
+
.. schema_example::
{
"title" : "Match anything",
"description" : "This is a schema that matches anything.",
- "default" : "Default value"
+ "default" : "Default value",
+ "examples" : [
+ "Anything",
+ 4035
+ ]
}
.. index::
@@ -97,3 +109,38 @@ be valid against the enclosing schema:
// This is in the ``enum``, but it's invalid against ``{ "type":
// "string" }``, so it's ultimately invalid:
null
+
+.. index::
+ single: const
+ single: constant values
+
+.. _const:
+
+Constant values
+---------------
+
+|draft6|
+
+The ``const`` keyword is used to restrict a value to a single value.
+
+For example, to if you only support shipping to the United States for export reasons:
+
+.. schema_example::
+
+ {
+ "properties": {
+ "country": {
+ "const": "United States of America"
+ }
+ }
+ }
+ --
+ { "country": "United States of America" }
+ --X
+ { "country": "Canada" }
+
+It should be noted that ``const`` is merely syntactic sugar for an ``enum`` with a single element, therefore the following are equivalent::
+
+ { "const": "United States of America" }
+
+ { "enum": [ "United States of America" ] }
diff --git a/source/reference/numeric.rst b/source/reference/numeric.rst
index fd74e077..391f96b5 100644
--- a/source/reference/numeric.rst
+++ b/source/reference/numeric.rst
@@ -149,46 +149,78 @@ Range
'''''
Ranges of numbers are specified using a combination of the
-``minimum``, ``maximum``, ``exclusiveMinimum`` and
-``exclusiveMaximum`` keywords.
+``minimum`` and ``maximum`` keywords, (or ``exclusiveMinimum`` and
+``exclusiveMaximum`` for expressing exclusive range).
-- ``minimum`` specifies a minimum numeric value.
+If *x* is the value being validated, the following must hold true:
-- ``exclusiveMinimum`` is a boolean. When ``true``, it indicates that
- the range excludes the minimum value, i.e., :math:`x >
- \mathrm{min}`. When ``false`` (or not included), it indicates that
- the range includes the minimum value, i.e., :math:`x \ge
- \mathrm{min}`.
+ - *x* ≥ ``minimum``
+ - *x* > ``exclusiveMinimum``
+ - *x* ≤ ``maximum``
+ - *x* < ``exclusiveMaximum``
-- ``maximum`` specifies a maximum numeric value.
-
-- ``exclusiveMaximum`` is a boolean. When ``true``, it indicates that
- the range excludes the maximum value, i.e., :math:`x <
- \mathrm{max}`. When ``false`` (or not included), it indicates that
- the range includes the maximum value, i.e., :math:`x \le
- \mathrm{max}`.
+While you can specify both of ``minimum`` and ``exclusiveMinimum`` or both of
+``maximum`` and ``exclusiveMinimum``, it doesn't really make sense to do so.
.. schema_example::
{
"type": "number",
"minimum": 0,
- "maximum": 100,
- "exclusiveMaximum": true
+ "exclusiveMaximum": 100
}
--X
// Less than ``minimum``:
-1
--
- // ``exclusiveMinimum`` was not specified, so 0 is included:
+ // ``minimum`` is inclusive, so 0 is valid:
0
--
10
--
99
--X
- // ``exclusiveMaximum`` is ``true``, so 100 is not included:
+ // ``exclusiveMaximum`` is exclusive, so 100 is not valid:
100
--X
// Greater than ``maximum``:
101
+
+.. language_specific::
+
+ --Draft 4
+ In JSON Schema Draft 4, ``exclusiveMinimum`` and ``exclusiveMaximum`` work
+ differently. There they are boolean values, that indicate whether
+ ``minimum`` and ``maximum`` are exclusive of the value. For example:
+
+ - if ``exclusiveMinimum`` is ``false``, *x* ≥ ``minimum``.
+ - if ``exclusiveMinimum`` is ``true``, *x* > ``minimum``.
+
+ This was changed to have better keyword independence.
+
+ Here is an example using the older Draft 4 convention:
+
+ .. schema_example:: 4
+
+ {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 100,
+ "exclusiveMaximum": true
+ }
+ --X
+ // Less than ``minimum``:
+ -1
+ --
+ // ``exclusiveMinimum`` was not specified, so 0 is included:
+ 0
+ --
+ 10
+ --
+ 99
+ --X
+ // ``exclusiveMaximum`` is ``true``, so 100 is not included:
+ 100
+ --X
+ // Greater than ``maximum``:
+ 101
diff --git a/source/reference/object.rst b/source/reference/object.rst
index a1bcefb9..a7d3ba05 100644
--- a/source/reference/object.rst
+++ b/source/reference/object.rst
@@ -193,9 +193,14 @@ 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.
-The ``required`` keyword takes an array of one or more strings. Each
+The ``required`` keyword takes an array of zero or more strings. Each
of these strings must be unique.
+.. draft_specific::
+
+ --Draft 4
+ In Draft 4, ``required`` must contain at least one string.
+
In the following example schema defining a user record, we require
that each user has a name and e-mail address, but we don't mind if
they don't provide their address or telephone number:
@@ -234,6 +239,43 @@ they don't provide their address or telephone number:
"address": "Henley Street, Stratford-upon-Avon, Warwickshire, England",
}
+.. index::
+ single: object; property names
+ single: propertyNames
+
+Property names
+''''''''''''''
+
+|draft6|
+
+The names of properties can be validated against a schema, irrespective of their
+values. This can be useful if you don't want to enforce a specific properties,
+but you want to make sure that the names of those properties follow a specific
+convention. You might, for example, want to enforce that all names are valid
+ASCII tokens so they can be used as attributes in a particular programming
+language.
+
+.. schema_example::
+
+ {
+ "type": "object",
+ "propertyNames": {
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
+ }
+ }
+ --
+ {
+ "_a_proper_token_001": "value"
+ }
+ --X
+ {
+ "001 invalid": "value"
+ }
+
+Since object keys must always be strings anyway, so it is implied that the
+schema given to ``propertyNames`` is always at least::
+
+ { "type": "string" }
.. index::
single: object; size
diff --git a/source/reference/schema.rst b/source/reference/schema.rst
index fe3a73a4..9d002ee8 100644
--- a/source/reference/schema.rst
+++ b/source/reference/schema.rst
@@ -20,37 +20,14 @@ this at the root of your schema::
Advanced
--------
-If you need to declare that your schema was written against a specific
-version of the JSON Schema standard, and not just the latest version,
-you can use one of these predefined values:
-
-- ``http://json-schema.org/schema#``
-
- JSON Schema written against the current version of the
- specification.
-
-- ``http://json-schema.org/hyper-schema#``
-
- JSON Schema hyperschema written against the current version of the
- specification.
+If you need to declare that your schema was written against a specific version
+of the JSON Schema standard, you should include the draft name in the path, for
+example:
+- ``http://json-schema.org/draft-06/schema#``
- ``http://json-schema.org/draft-04/schema#``
- JSON Schema written against this version.
-
-- ``http://json-schema.org/draft-04/hyper-schema#``
-
- JSON Schema hyperschema written against this version.
-
-- ``http://json-schema.org/draft-03/schema#``
-
- JSON Schema written against JSON Schema, draft v3
-
-- ``http://json-schema.org/draft-03/hyper-schema#``
-
- JSON Schema hyperschema written against JSON Schema, draft v3
-
Additionally, if you have extended the JSON Schema language to include
your own custom keywords for validating values, you can use a custom
URI for ``$schema``. It must not be one of the predefined values
-above.
+above, and should probably include a domain name you own.
diff --git a/source/reference/string.rst b/source/reference/string.rst
index cfb62737..ecf4e8d1 100644
--- a/source/reference/string.rst
+++ b/source/reference/string.rst
@@ -129,6 +129,17 @@ exchanging the JSON documents also exchange information about the
custom format types. A JSON Schema validator will ignore any format
type that it does not understand.
+.. index::
+ single: date-time
+ single: email
+ single: hostname
+ single: ipv4
+ single: ipv6
+ single: uri
+ single: uri-reference
+ single: uri-template
+ single: json-pointer
+
Built-in formats
^^^^^^^^^^^^^^^^
@@ -153,3 +164,35 @@ specification.
- ``"uri"``: A universal resource identifier (URI), according to
`RFC3986 `_.
+
+- ``"uri-reference"``: |draft6| A URI Reference (either a URI or a
+ relative-reference), according to `RFC3986, section 4.1
+ `_.
+
+- ``"json-pointer"``: |draft6| A JSON Pointer, according to `RFC6901
+ `_. There is more discussion on the use
+ of JSON Pointer within JSON Schema in `structuring`. Note that this should be
+ used only when the entire string contains only JSON Pointer content, e.g.
+ ``/foo/bar``. JSON Pointer URI fragments, e.g. ``#/foo/bar/`` should use
+ ``"uri"`` or ``"uri-reference"``.
+
+- ``"uri-template"``: |draft6| A URI Template (of any level) according to
+ `RFC6570 `_. If you don't already know
+ what a URI Template is, you probably don't need this value.
+
+URI vs. URI reference
+`````````````````````
+
+If the values in the schema the ability to be relative to a particular source
+path (such as a link from a webpage), it is generally better practice to use
+``"uri-reference"`` rather than ``"uri"``. ``"uri"`` should only be used when
+the path must be absolute.
+
+.. draft_specific::
+
+ --Draft 4
+ Draft 4 only includes ``"uri"``, not ``"uri-reference"``. Therefore, there is
+ some ambiguity around whether ``"uri"`` should accept relative paths.
+
+
+.. TODO: Add some examples for ``format`` here
diff --git a/source/structuring.rst b/source/structuring.rst
index 330f14d9..54649667 100644
--- a/source/structuring.rst
+++ b/source/structuring.rst
@@ -22,16 +22,13 @@ For this example, let's say we want to define a customer record, where
each customer may have both a shipping and a billing address.
Addresses are always the same---they have a street address, city and
state---so we don't want to duplicate that part of the schema
-everywhere we want to store an address. Not only does it make the
+everywhere we want to store an address. Not only would that make the
schema more verbose, but it makes updating it in the future more
-difficult. If our imaginary company were to start international
+difficult. If our imaginary company were to start doing international
business in the future and we wanted to add a country field to all the
addresses, it would be better to do this in a single place rather than
everywhere that addresses are used.
-.. note::
- This is part of the draft 4 spec only, and does not exist in draft 3.
-
So let's start with the schema that defines an address::
{
@@ -72,7 +69,12 @@ refer to the above, we would include::
{ "$ref": "#/definitions/address" }
-The value of ``$ref`` is a string in a format called `JSON Pointer
+This can be used anywhere a schema is expected. You will always use ``$ref`` as
+the only key in an object: any other keys you put there will be ignored by the
+validator.
+
+The value of ``$ref`` is a URI, and the part after ``#`` sign (the
+"fragment" or "named anchor") is in a format called `JSON Pointer
`__.
.. note::
@@ -80,9 +82,9 @@ The value of ``$ref`` is a string in a format called `JSON Pointer
`_ from the XML world, but it is much
simpler.
-The pound symbol (``#``) refers to the current document, and then the
-slash (``/``) separated keys thereafter just traverse the keys in the
-objects in the document. Therefore, in our example
+If you're using a definition from the same document, the ``$ref`` value begins
+with the pound symbol (``#``). Following that, the slash-separated items traverse
+the keys in the objects in the document. Therefore, in our example
``"#/definitions/address"`` means:
1) go to the root of the document
@@ -104,7 +106,7 @@ schema for a customer:
.. schema_example::
{
- "$schema": "http://json-schema.org/draft-04/schema#",
+ "$schema": "http://json-schema.org/draft-06/schema#",
"definitions": {
"address": {
@@ -139,46 +141,193 @@ schema for a customer:
}
}
+.. note::
+
+ Even though the value of a ``$ref`` is a URI, it is not a network locator,
+ only an identifier. This means that the schema doesn't need to be accessible
+ at that URI, but it may be. It is basically up to the validator
+ implementation how external schema URIs will be handled, but one should not
+ assume the validator will fetch network resources indicated in ``$ref``
+ values.
+
+Recursion
+`````````
+
+``$ref`` elements may be used to create recursive schemas that refer to themselves.
+For example, you might have a ``person`` schema that has an array of ``children``, each of which are also ``person`` instances.
+
+.. schema_example::
+
+ {
+ "$schema": "http://json-schema.org/draft-06/schema#",
+
+ "definitions": {
+ "person": {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "children": {
+ "type": "array",
+ * "items": { "$ref": "#/definitions/person" },
+ "default": []
+ }
+ }
+ }
+ },
+
+ "type": "object",
+
+ "properties": {
+ "person": { "$ref": "#/definitions/person" }
+ }
+ }
+ --
+ // A snippet of the British royal family tree
+ {
+ "person": {
+ "name": "Elizabeth",
+ "children": [
+ {
+ "name": "Charles",
+ "children": [
+ {
+ "name": "William",
+ "children": [
+ { "name": "George" },
+ { "name": "Charlotte" }
+ ]
+ },
+ {
+ "name": "Harry"
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+Above, we created a schema that refers to another part of itself, effectively
+creating a "loop" in the validator, which is both allowed and useful. Note,
+however, that a loop of ``$ref`` schemas referring to one another could cause an
+infinite loop in the resolver, and is explicitly disallowed.
+
+.. schema_example::
+
+ {
+ "definitions": {
+ "alice": {
+ "anyOf": [
+ { "$ref": "#/definitions/bob" }
+ ]
+ },
+ "bob": {
+ "anyOf": [
+ { "$ref": "#/definitions/alice" }
+ ]
+ }
+ }
+ }
+
+.. index::
+ single: $id
+
.. _id:
-The id property
----------------
+The $id property
+----------------
-The ``id`` property serves two purposes:
+The ``$id`` property is a URI that serves two purposes:
- It declares a unique identifier for the schema.
-- It declares a base URL against which ``$ref`` URLs are resolved.
+- It declares a base URI against which ``$ref`` URIs are resolved.
-It is best practice that ``id`` is a URL, preferably in a domain that
-you control. For example, if you own the ``foo.bar`` domain, and you
-had a schema for addresses, you may set its ``id`` as follows::
+It is best practice that every top-level schema should set ``$id`` to an
+absolute URI, with a domain that you control. For example, if you own the
+``foo.bar`` domain, and you had a schema for addresses, you may set its ``$id``
+as follows:
+
+.. schema_example::
- "id": "http://foo.bar/schemas/address.json"
+ { "$id": "http://foo.bar/schemas/address.json" }
This provides a unique identifier for the schema, as well as, in most
cases, indicating where it may be downloaded.
-But be aware of the second purpose of the ``id`` property: that it
+But be aware of the second purpose of the ``$id`` property: that it
declares a base URL for relative ``$ref`` URLs elsewhere in the file.
-For example, if you had::
+For example, if you had:
+
+.. schema_example::
{ "$ref": "person.json" }
-in the same file, a JSON schema validation library would fetch
-``person.json`` from ``http://foo.bar/schemas/person.json``, even if
-``address.json`` was loaded from the local filesystem.
+in the same file, a JSON schema validation library that supported network
+fetching would fetch ``person.json`` from
+``http://foo.bar/schemas/person.json``, even if ``address.json`` was loaded from
+somewhere else, such as the local filesystem.
+
+|draft6|
+
+.. draft_specific::
+
+ --Draft 4
+ In Draft 4, ``$id`` is just ``id`` (without the dollar sign).
+
+The ``$id`` property should never be the empty string or an empty fragment
+(``#``), since that doesn't really make sense.
+
+Using $id with $ref
+```````````````````
+
+``$id`` also provides a way to refer to subschema without using JSON Pointer.
+This means you can refer to them by a unique name, rather than by where they
+appear in the JSON tree.
+
+Reusing the address example above, we can add an ``$id`` property to the
+address schema, and refer to it by that instead.
+
+.. schema_example::
+
+ {
+ "$schema": "http://json-schema.org/draft-06/schema#",
+
+ "definitions": {
+ "address": {
+ *"$id": "#address",
+ "type": "object",
+ "properties": {
+ "street_address": { "type": "string" },
+ "city": { "type": "string" },
+ "state": { "type": "string" }
+ },
+ "required": ["street_address", "city", "state"]
+ }
+ },
+
+ "type": "object",
+
+ "properties": {
+ *"billing_address": { "$ref": "#address" },
+ *"shipping_address": { "$ref": "#address" }
+ }
+ }
+
+.. note::
+
+ This functionality isn't currently supported by the Python ``jsonschema``
+ library.
Extending
---------
-The power of ``$ref`` really shines when it is combined with the
+The power of ``$ref`` really shines when it is used with the
combining keywords ``allOf``, ``anyOf`` and ``oneOf`` (see
:ref:`combining`).
-Let's say that for shipping address, we want to know whether the
+Let's say that for a shipping address, we want to know whether the
address is a residential or business address, because the shipping
-method used may depend on that. For the billing address, we don't
+method used may depend on that. For a billing address, we don't
want to store that information, because it's not applicable.
To handle this, we'll update our definition of shipping address::
@@ -209,7 +358,7 @@ Tying this all together,
.. schema_example::
{
- "$schema": "http://json-schema.org/draft-04/schema#",
+ "$schema": "http://json-schema.org/draft-06/schema#",
"definitions": {
"address": {