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
131 changes: 130 additions & 1 deletion source/reference/array.rst
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,136 @@ Unevaluated Items

|draft2019-09|

Documentation Coming Soon
The ``unevaluatedItems`` keyword applies to any values not evaluated
by an ``items``, ``prefixItems``, or ``contains`` keyword. Just as
``unevaluatedProperties`` affects only **properties** in an object,
``unevaluatedItems`` affects only **items** in an array.
micshar92 marked this conversation as resolved.
Show resolved Hide resolved

.. 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".

For this first example, 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 arguments in some
places, "open" to more arguments when you need it to be.)

.. schema_example::

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

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

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

``items`` evaluates only the items in the initial array. Any items
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
you add when you extend the tuple later *are not evaluated*. When
you extend the tuple, the schema passes validation only if you include
``unevaluatedItems`` like this:
micshar92 marked this conversation as resolved.
Show resolved Hide resolved

.. schema_example::

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

"$ref": "https://example.com/my-tuple",
"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 arguments when
you need it, "open" to more arguments when you need it. A reference to
``/my-tuple#closed`` would disallow more than two items. And because
``unevaluatedItems`` only sees inside its own subschema, if you
want to add an item, add it inside that subschema.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be a description of the first schema rather than the one immediately above this text, which I found confusing as a reader. However, I'm not sure this is the best place to introduce the "half-closed" schema pattern. It's a useful pattern, but trying to explain the pattern at the same time as trying to explain how the keyword works is, I think, too much at once. I suggest that your schemas all be either open or closed and give examples of extending each and why each works or doesn't work.


This means you can also put ``unevaluatedItems`` in a nested tuple.

.. schema_example::

{
"prefixItems": [
{ "type": "string" }
],
"allOf": [
{
"prefixItems": [
true,
{ "type": "number" }
]
}
],
"unevaluatedItems": false
}
--
["foo", 42]
// All the array items are evaluated. The schema passes validation.
--X
["foo", 42, null]
// The third value is unevaluated, so ``unevaluatedItems`` is true,
// not false. The schema fails validation.
micshar92 marked this conversation as resolved.
Show resolved Hide resolved

Note these two restraints, though. First, ``items`` doesn't "see
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
inside" any instances of ``allOf``, ``anyOf``, or ``oneOf`` in the
same subschema. In this next example, ``items`` ignores ``allOf`` and
thus fails to validate.

.. schema_example::

{
"allOf": [{ "prefixItems": [{ "enum": [1, "a"] }] }],
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
"items": { "const": 2 }
}
--X
[1, "a", 2]

micshar92 marked this conversation as resolved.
Show resolved Hide resolved
Second, ``unevaluatedItems`` can't "see inside" cousins (vertically
adjacent properties inside a separate pair of {curly braces} with the
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
same "parent"— ``anyOf``, ``if``, ``not``, or similar). For instance,
in the example below, the ``unevaluatedItems`` doesn't "see inside"
the ``prefixItems`` cousin before it. All instances fail validation
because ``"prefixItems": [ true ]`` matches only length 1 arrays, and
micshar92 marked this conversation as resolved.
Show resolved Hide resolved
``{ "unevaluatedItems": false }`` matches only empty arrays.

.. schema_example::

{
"allOf": [
{ "prefixItems": [true] },
{ "unevaluatedItems": false }
]
}
--X
[1]
micshar92 marked this conversation as resolved.
Show resolved Hide resolved

.. note::
For a tall list 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. We test a lot of use cases
there, including uncommon ones.

Here are some of our examples in the suite:
* ``unevaluatedItems`` nested inside another ``unevaluatedItems``
* ``if/then/else`` statements interacting with ``unevaluatedItems``
* nested ``if/then/else`` statements interacting with ``unevaluatedItems``
* ``unevaluatedItems`` ignoring non-arrays
* ``unevaluatedItems`` interacting with the ``not`` keyword
* and more
micshar92 marked this conversation as resolved.
Show resolved Hide resolved

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