|
606 | 606 | due to the need to examine all subschemas for annotation collection, including
|
607 | 607 | those that cannot further change the assertion result.
|
608 | 608 | </t>
|
609 |
| - <section title="Lexical Scope and Dynamic Scope"> |
| 609 | + <section title="Lexical Scope and Dynamic Scope" anchor="scopes"> |
610 | 610 | <t>
|
611 | 611 | While most JSON Schema keywords can be evaluated on their own,
|
612 | 612 | or at most need to take into account the values or results of
|
|
1587 | 1587 | </t>
|
1588 | 1588 | </section>
|
1589 | 1589 |
|
1590 |
| - <section title='Recursive References with "$recursiveRef" and "$recursiveAnchor"'> |
| 1590 | + <section title='Recursive References with "$recursiveRef" and "$recursiveAnchor"' |
| 1591 | + anchor="recursive-ref"> |
1591 | 1592 | <t>
|
1592 | 1593 | The "$recursiveRef" and "$recursiveAnchor" keywords are used to construct
|
1593 | 1594 | extensible recursive schemas. A recursive schema is one that has
|
1594 | 1595 | a reference to its own root, identified by the empty fragment
|
1595 | 1596 | URI reference ("#").
|
1596 | 1597 | </t>
|
1597 |
| - <t> |
1598 |
| - Extending a recursive schema with "$ref" alone involves redefining all |
1599 |
| - recursive references in the source schema to point to the root of the |
1600 |
| - extension. This produces the correct recursive behavior in the extension, |
1601 |
| - which is that all recursion should reference the root of the extension. |
1602 |
| - </t> |
1603 |
| - <figure> |
1604 |
| - <preamble> |
1605 |
| - Consider the following two schemas. The first schema, identified |
1606 |
| - as "original" as it is the schema to be extended, describes |
1607 |
| - an object with one string property and one recursive reference |
1608 |
| - property, "r". The second schema, identified as "extension", |
1609 |
| - references the first, and describes an additional "things" property, |
1610 |
| - which is an array of recursive references. |
1611 |
| - It also repeats the description of "r" from the original schema. |
1612 |
| - </preamble> |
1613 |
| - <artwork> |
1614 |
| -<![CDATA[ |
1615 |
| -{ |
1616 |
| - "$schema": "https://json-schema.org/draft/2019-08/schema", |
1617 |
| - "$id": "https://example.com/original", |
1618 |
| -
|
1619 |
| - "properties": { |
1620 |
| - "name": { |
1621 |
| - "type": "string" |
1622 |
| - }, |
1623 |
| - "r": { |
1624 |
| - "$ref": "#" |
1625 |
| - } |
1626 |
| - } |
1627 |
| -} |
1628 |
| -
|
1629 |
| -{ |
1630 |
| - "$schema": "https://json-schema.org/draft/2019-08/schema", |
1631 |
| - "$id": "https://example.com/extension", |
1632 |
| -
|
1633 |
| - "$ref": "original", |
1634 |
| - "properties": { |
1635 |
| - "r": { |
1636 |
| - "$ref": "#" |
1637 |
| - }, |
1638 |
| - "things": { |
1639 |
| - "type": "array" |
1640 |
| - "items": { |
1641 |
| - "$ref": "#" |
1642 |
| - } |
1643 |
| - } |
1644 |
| - } |
1645 |
| -} |
1646 |
| -]]> |
1647 |
| - </artwork> |
1648 |
| - <postamble> |
1649 |
| - This apparent duplication is important because |
1650 |
| - it resolves to "https://example.com/extension#", meaning that |
1651 |
| - for instance validated against the extension schema, the value |
1652 |
| - of "r" must be valid according to the extension, and not just the |
1653 |
| - original schema as "r" was described there. |
1654 |
| - </postamble> |
1655 |
| - </figure> |
1656 |
| - <t> |
1657 |
| - This approach is fine for a single recursive field, but the more |
1658 |
| - complicated the original schema, the more redefinitions are necessary |
1659 |
| - in the extension. This leads to a verbose and error-prone extension, |
1660 |
| - which must be kept synchronized with the original schema if the |
1661 |
| - original changes its recursive fields. |
1662 |
| - This approach can be seen in the meta-schema for JSON Hyper-Schema |
1663 |
| - in all prior drafts. |
1664 |
| - </t> |
1665 |
| - <section title='Enabling Recursion with "$recursiveAnchor"'> |
| 1598 | + <section title='Dynamically recursive references with "$recursiveRef"'> |
| 1599 | + <t> |
| 1600 | + The value of the "$recursiveRef" property MUST be a string which is |
| 1601 | + a URI-reference. It is a by-reference applicator that uses |
| 1602 | + a dynamically calculated base URI to resolve its value. |
| 1603 | + </t> |
| 1604 | + <t> |
| 1605 | + The behavior of this keyword is defined only for the value "#". |
| 1606 | + Implementations MAY choose to consider other values to be errors. |
| 1607 | + <cref> |
| 1608 | + This restriction may be relaxed in the future, but to date only |
| 1609 | + the value "#" has a clear use case. |
| 1610 | + </cref> |
| 1611 | + </t> |
1666 | 1612 | <t>
|
1667 |
| - The desired behavior is for the recursive reference, "r", in the |
1668 |
| - original schema to resolve to the original schema when that |
1669 |
| - is the only schema being used, but to resolve to the extension |
1670 |
| - schema when using the extension. Then there would be no need |
1671 |
| - to redefine the "r" property, or others like it, in the extension. |
| 1613 | + The value of "$recursiveRef" is initially resolved against the |
| 1614 | + current base URI, in the same manner as for "$ref". |
1672 | 1615 | </t>
|
1673 | 1616 | <t>
|
1674 |
| - In order to create a recursive reference, we must do three things: |
1675 |
| - <list> |
1676 |
| - <t> |
1677 |
| - In our original schema, indicate that the schema author |
1678 |
| - intends for it to be extensible recursively. |
1679 |
| - </t> |
1680 |
| - <t> |
1681 |
| - In our extension schema, indicate that it is intended |
1682 |
| - to be a recursive extension. |
1683 |
| - </t> |
1684 |
| - <t> |
1685 |
| - Use a reference keyword that explicitly activates the |
1686 |
| - recursive behavior at the point of reference. |
1687 |
| - </t> |
1688 |
| - </list> |
1689 |
| - These three things together ensure that all schema authors |
1690 |
| - are intentionally constructing a recursive extension, which in |
1691 |
| - turn gives all uses of the regular "$ref" keyword confidence |
1692 |
| - that it only behaves as it appears to, using lexical scoping. |
| 1617 | + The schema identified by the resulting URI is examined for the |
| 1618 | + presence of "$recursiveAnchor", and a new base URI is calculated |
| 1619 | + as described for that keyword in the following section. |
1693 | 1620 | </t>
|
1694 | 1621 | <t>
|
1695 |
| - The "$recursiveAnchor" keyword is how schema authors indicate |
1696 |
| - that a schema can be extended recursively, and be a recursive |
1697 |
| - schema. This keyword MAY appear in the root schema of a |
1698 |
| - schema document, and MUST NOT appear in any subschema. |
| 1622 | + Finally, the value of "$recursiveRef" is resolved against the |
| 1623 | + new base URI determined according to "$recursiveAnchor" producing |
| 1624 | + the final resolved reference URI. |
1699 | 1625 | </t>
|
1700 | 1626 | <t>
|
1701 |
| - The value of "$recursiveAnchor" MUST be of type boolean, and |
1702 |
| - MUST be true. The value false is reserved for possible future use. |
| 1627 | + Note that in the absence of "$recursiveAnchor" (and in some cases |
| 1628 | + when it is present", "$recursiveRef"'s behavior is identical to |
| 1629 | + that of "$ref". |
1703 | 1630 | </t>
|
1704 | 1631 | </section>
|
1705 |
| - <section title='Dynamically recursive references with "$recursiveRef"'> |
| 1632 | + <section title='Enabling Recursion with "$recursiveAnchor"'> |
1706 | 1633 | <t>
|
1707 |
| - The "$recursiveRef" keyword behaves identically to "$ref", except |
1708 |
| - that if the referenced schema has "$recursiveAnchor" set to true, |
1709 |
| - then the implementation MUST examine the dynamic scope for the |
1710 |
| - outermost (first seen) schema document with "$recursiveAnchor" |
1711 |
| - set to true. If such a schema document exists, then the target |
1712 |
| - of the "$recursiveRef" MUST be set to that document's URI, in |
1713 |
| - place of the URI produced by the rules for "$ref". |
| 1634 | + The value of the "$recursiveAnchor" property MUST be a boolean. |
1714 | 1635 | </t>
|
1715 | 1636 | <t>
|
1716 |
| - Note that if the schema referenced by "$recursiveRef" does not |
1717 |
| - contain "$recursiveAnchor" set to true, or if there are no other |
1718 |
| - "$recursiveAnchor" keywords set to true anywhere further back in |
1719 |
| - the dynamic scope, then "$recursiveRef"'s behavior is identical |
1720 |
| - to that of "$ref". |
| 1637 | + "$recursiveAnchor" is used to dynamically identify a base URI |
| 1638 | + at runtime for "$recursiveRef" by marking where such a calculation |
| 1639 | + can start, and where it stops. This keyword MUST NOT affect the |
| 1640 | + base URI of other keywords, unless they are explicitly defined |
| 1641 | + to rely on it. |
| 1642 | + </t> |
| 1643 | + <t> |
| 1644 | + If set to true, then when the containing schema object is used |
| 1645 | + as a dynamic reference target, a new base URI is determined |
| 1646 | + by examining the <xref target="scopes">dynamic scope</xref> for |
| 1647 | + the outermost schema that also contains "$recursiveAnchor" |
| 1648 | + with a value of true. The base URI of that schema is then used |
| 1649 | + as the dynamic base URI. |
1721 | 1650 | </t>
|
1722 |
| - <figure> |
1723 |
| - <preamble> |
1724 |
| - With this in mind, we can rewrite the previous example: |
1725 |
| - </preamble> |
1726 |
| - <artwork> |
1727 |
| -<![CDATA[ |
1728 |
| -{ |
1729 |
| - "$schema": "https://json-schema.org/draft/2019-08/schema", |
1730 |
| - "$id": "https://example.com/original", |
1731 |
| - "$recursiveAnchor": true, |
1732 |
| -
|
1733 |
| - "properties": { |
1734 |
| - "name": { |
1735 |
| - "type": "string" |
1736 |
| - }, |
1737 |
| - "r": { |
1738 |
| - "$recursiveRef": "#" |
1739 |
| - } |
1740 |
| - } |
1741 |
| -} |
1742 |
| -
|
1743 |
| -{ |
1744 |
| - "$schema": "https://json-schema.org/draft/2019-08/schema", |
1745 |
| - "$id": "https://example.com/extension", |
1746 |
| - "$recursiveAnchor": true, |
1747 |
| -
|
1748 |
| - "$ref": "original", |
1749 |
| - "properties": { |
1750 |
| - "things": { |
1751 |
| - "type": "array" |
1752 |
| - "items": { |
1753 |
| - "$recursiveRef": "#" |
1754 |
| - } |
1755 |
| - } |
1756 |
| - } |
1757 |
| -} |
1758 |
| -]]> |
1759 |
| - </artwork> |
1760 |
| - <postamble> |
1761 |
| - Note that the "r" property no longer appears in the |
1762 |
| - extension schema. Instead, all "$ref"s have been changed |
1763 |
| - to "$recursiveRef"s, and both schemas have "$recursiveAnchor" |
1764 |
| - set to true in their root schema. |
1765 |
| - </postamble> |
1766 |
| - </figure> |
1767 | 1651 | <t>
|
1768 |
| - When using the original schema on its own, there is no change |
1769 |
| - in behavior. The "$recursiveRef" does lead to a schema where |
1770 |
| - "$recursiveAnchor" is set to true, but since the original schema |
1771 |
| - is the only schema document in the dynamics scope (it references |
1772 |
| - itself, and does not reference any other schema documents), the |
1773 |
| - behavior is effectively the same as "$ref". |
| 1652 | + If no such schema exists, then the base URI is unchanged. |
1774 | 1653 | </t>
|
1775 | 1654 | <t>
|
1776 |
| - When using the extension schema, the "$recursiveRef" within |
1777 |
| - that schema (for the array items within "things") also effectively |
1778 |
| - behaves like "$ref". The extension schema is the outermost |
1779 |
| - dynamic scope, so the reference target is not changed. |
| 1655 | + If this keyword is set to false, the base URI is unchanged. |
1780 | 1656 | </t>
|
1781 | 1657 | <t>
|
1782 |
| - In contrast, when using the extension schema, the "$recursiveRef" |
1783 |
| - for "r" in the original schema now behaves differently. Its |
1784 |
| - initial target is the root schema of the original schema document, |
1785 |
| - which has "$recursiveAnchor" set to true. In this case, the |
1786 |
| - outermost dynamic scope that also has "$recursiveAnchor" set to |
1787 |
| - true is the extension schema. So when using the extensions schema, |
1788 |
| - "r"'s reference in the original schema will resolve to |
1789 |
| - "https://example.com/extension#", not "https://example.com/original#". |
| 1658 | + Omitting this keyword has the same behavior as a value of false. |
1790 | 1659 | </t>
|
1791 | 1660 | </section>
|
1792 | 1661 | </section>
|
|
0 commit comments