Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

add rough docs for unevaluatedItems #205

Merged
merged 9 commits into from
Jul 5, 2023
195 changes: 194 additions & 1 deletion source/reference/array.rst
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,200 @@ Unevaluated Items

|draft2019-09|

Documentation Coming Soon
The ``unevaluatedItems`` keyword selects any data types not evaluated
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
by an ``items``, ``prefixItems``, or `contains` keyword. Just as
jdesrosiers marked this conversation as resolved.
Show resolved Hide resolved
unevaluated"properties" affect only "properties" in an object, only
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
"item"-related keywords affect unevaluated"items".

It also applies inside valid subschemas with these keywords:
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
- ``allOf``
- ``anyOf``
- ``oneOf``
- ``not``
- ``if``
- ``then``
- ``else``

The main reason to use this keyword is to extend an array with extra
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
arguments.

For this first example, we'll use ``unevaluatedItems`` to select any
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
unexpected strings.

.. schema_example::

{
"items": {"type": "number"},
"unevaluatedItems": {"type": "string"}
}
--X
// If any strings appear, then the schema doesn't validate. There are no unevaluated items in that case.
{ 99, "waffles" }
--
// But it passes so long as JSON finds all entries in an ``items``, ``prefixItems``, or ``contains``. There *are* unevaluated items in that case.
{ 99, 0, 3.14159 }

.. note::
Watch out! The word "unevaluated" *does not* mean "not evaluated by
``items``, ``prefixItems``, or ``contains``." "Unevaluated" means
"not successfully evaluated", or "doesn't evaluate to true".
jdesrosiers marked this conversation as resolved.
Show resolved Hide resolved

You can also set ``unevaluatedItems`` as a boolean.

.. schema_example::

{
"description": "unevaluatedItems with nested tuple",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"prefixItems": [
{ "type": "string" }
],
"allOf": [
{
"prefixItems": [
true,
{ "type": "number" }
]
}
],
"unevaluatedItems": false
},
"tests": [
{
"description": "with no unevaluated items",
"data": ["foo", 42],
"valid": true
},
{
"description": "with unevaluated items",
"data": ["foo", 42, null],
"valid": false
}
]
}

In the first test, all the "data" items are evaluated, but in the
second test, the ``null`` value is a type not specified by
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
``prefixItems``. It's therefore valid and ``true`` that
``unevaluatedItems`` returns ``false`` in the first test, and invalid
and ``false`` in the second test. In other words, it is valid that no
unevaluated items exist until something not matching the string/number
pattern shows up.

You can also select ``unevaluatedItems`` when and only when an ``if``
statement runs.

.. schema_example::

{
"description": "unevaluatedItems can see annotations from if even without then and else",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"if": {
"prefixItems": [{"const": "a"}]
},
"unevaluatedItems": false
},
"tests": [
{
"description": "valid in case if is evaluated",
"data": [ "a" ],
"valid": true
},
{
"description": "invalid in case if is evaluated",
"data": [ "b" ],
"valid": false
}
]
}

And an important note: ``unevaluatedItems`` can't see inside cousins
(a vertically adjacent item inside a separate pair of {curly braces}
with the same "parent"— ``anyOf``, ``if``, ``not``, or similar). Such
an instance always fails evaluation.

.. schema_example::

{
"description": "unevaluatedItems can't see inside cousins",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"allOf": [
{
"prefixItems": [ true ]
},
{ "unevaluatedItems": false }
]
},
"tests": [
{
"description": "always fails",
"data": [ 1 ],
"valid": false
}
]
}

Finally, here's an example of ``unevaluatedItems`` if you're
`structuring`. Let's make a "half-closed" schema: something useful
when you want to keep the first two arguments, but also add more in
certain situations. ("Closed" to two in some places, "open" to more
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
in others.)

.. schema_example::

{
"$id": "https://example.com/my-tuple",

"type": "array",
"prefixItems": [
true,
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
{ "type": "boolean" }
],

"$defs": {
"closed": {
"$anchor": "closed",
"$ref": "#",
"unevaluatedItems": false
}
}
}

Then we can extend the tuple:

.. schema_example::

{
"$id": "https://example.com/my-extended-tuple",

"$ref": "/my-tuple",
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
"prefixItems": [
true,
true,
{ "type": "boolean" }
],
"unevaluatedItems": false
}

With this, you can use ``$ref`` to reference the first two
``prefixItems`` and keep the schema "closed" to two when necessary,
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
"open" to more when necessary. A reference to ``/my-tuple#closed``
would disallow more than two items, when you need it to.

.. note::
For a tower of more examples, read our `unevaluatedItems Test Suite <https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/main/tests/draft2020-12/unevaluatedItems.json>`_ on GitHub.
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
We test a lot of use cases there, including uncommon ones. Do any
of these apply to your schema?
- using ``unevaluatedItems`` as a schema
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
- ``unevaluatedItems`` nested inside another ``unevaluatedItems``
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
- ``if/then/else`` statements with ``unevaluatedItems``
- multiples nested ``if``/``then``s
- multiple nested instances of ``contains``
- ignoring non-array types
- ``not``
micshar92 marked this conversation as resolved.
Show resolved Hide resolved

.. index::
single: array; contains
Expand Down