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

Commit

Permalink
unevaluatedItems tutorial seventh draft
Browse files Browse the repository at this point in the history
Seventh draft. Much simplified.

After I removed the reference to the test suite at the end, I toyed with adding some conclusion sentence-- it seems abrupt-- but it was just fluff, so I didn't. Anyway, here it is.
  • Loading branch information
micshar92 authored and jdesrosiers committed Jul 5, 2023
1 parent 4789d5d commit b14d548
Showing 1 changed file with 61 additions and 84 deletions.
145 changes: 61 additions & 84 deletions source/reference/array.rst
Original file line number Diff line number Diff line change
Expand Up @@ -242,130 +242,107 @@ by an ``items``, ``prefixItems``, or ``contains`` keyword. Just as
``unevaluatedItems`` affects only **items** in an array.

.. note::
Watch out! The word "unevaluated" *does not* mean "not evaluated by
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.)
``items`` doesn't "see inside" any instances of ``allOf``, ``anyOf``,
or ``oneOf`` in the same subschema. In this first example, ``items``
ignores ``allOf`` and thus fails to validate.

.. schema_example::

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

"type": "array",
"items": [
true,
{ "type": "boolean" }
],

"$defs": {
"closed": {
"$anchor": "closed",
"$ref": "#"
}
}
"allOf": [{ "prefixItems": [{ "type": "boolean" }, { "type": "string" }] }],
"items": { "const": 2 }
}
--X
[true, "a", 2]

``items`` evaluates only the items in the initial array. Any items
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:
But if you replace ``items`` with ``unevaluatedItems``, then the same
array validates.

.. schema_example::

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

"$ref": "https://example.com/my-tuple",
"prefixItems": [
true,
true,
{ "type": "boolean" }
],
"unevaluatedItems": false
"allOf": [{ "prefixItems": [{ "type": "boolean" }, { "type": "string" }] }],
"unevaluatedItems": { "const": 2 }
}
--
[true, "a", 2]

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.

This means you can also put ``unevaluatedItems`` in a nested tuple.
Like with ``items``, if you set ``unevaluatedItems`` to ``false``, you
can disallow extra items in the array.

.. schema_example::

{
"prefixItems": [
{ "type": "string" }
],
"allOf": [
{
"prefixItems": [
true,
{ "type": "number" }
]
}
{ "type": "string" }, { "type": "number" }
],
"unevaluatedItems": false
}
--
["foo", 42]
// All the array items are evaluated. The schema passes validation.
// All the values 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.
// The schema fails validation because ``"unevaluatedItems": false"``
// specifies no extra values should exist.

Note these two restraints, though. First, ``items`` doesn't "see
inside" any instances of ``allOf``, ``anyOf``, or ``oneOf`` in the
same subschema. In this next example, ``items`` ignores ``allOf`` and
thus fails to validate.
``unevaluatedItems`` is used mainly when extending a tuple. So with
this in mind, you can 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::

{
"allOf": [{ "prefixItems": [{ "enum": [1, "a"] }] }],
"items": { "const": 2 }
"$id": "https://example.com/my-tuple",

"type": "array",
"prefixItems": [
{ "type": "boolean" },
{ "type": "string" }
],
"unevaluatedItems": false,

"$defs": {
"closed": {
"$anchor": "closed",
"$ref": "#"
}
}
}
--X
[1, "a", 2]

Second, ``unevaluatedItems`` can't "see inside" cousins (vertically
adjacent properties inside a separate pair of {curly braces} with the
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
``{ "unevaluatedItems": false }`` matches only empty arrays.
Here the schema is "closed" to two array items. You can then later
use ``$ref`` and add another item like this:

.. schema_example::

{
"allOf": [
{ "prefixItems": [true] },
{ "unevaluatedItems": false }
]
"$id": "https://example.com/my-extended-tuple",

"$ref": "https://example.com/my-tuple",
"prefixItems": [
{ "type": "boolean" },
{ "type": "string" },
{ "type": "number" }
],
"unevaluatedItems": false,

"$defs": {
"extended": {
"$anchor": "extended",
"$ref": "#"
}
}
}
--X
[1]

.. 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
Thus, you would reference ``my-tuple#closed`` when you need only
two array items and reference ``my-tuple#extended`` when you need
three items.

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

0 comments on commit b14d548

Please sign in to comment.