Skip to content

Commit 1301082

Browse files
committed
Rework $recursiveRef based on feedback.
Hopefully this version of the description and example are easier to follow. Also replaced "reffered" and "referring" with "referenced" and "referencing", which seems to read a lot more smoothly.
1 parent 319421c commit 1301082

File tree

1 file changed

+101
-82
lines changed

1 file changed

+101
-82
lines changed

jsonschema-core.xml

Lines changed: 101 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,11 @@
205205
The schemas to be applied may be present as subschemas comprising all or
206206
part of the keyword's value. Alternatively, an applicator may refer to
207207
a schema elsewhere in the same schema document, or in a different one.
208-
The mechanism for identifying such referred schemas is defined by the
208+
The mechanism for identifying such referenced schemas is defined by the
209209
keyword.
210210
</t>
211211
<t>
212-
Applicator keywords also define how subschema or referred schema
212+
Applicator keywords also define how subschema or referenced schema
213213
boolean <xref target="assertions">assertion</xref>
214214
results are modified and/or combined to produce the boolean result
215215
of the applicator. Applicators may apply any boolean logic operation
@@ -570,24 +570,24 @@
570570
As with the root schema, a subschema is either an object or a boolean.
571571
</t>
572572
</section>
573-
<section title="Referred and Referring Schemas" anchor="referred">
573+
<section title="Referenced and Referencing Schemas" anchor="referenced">
574574
<t>
575575
As noted in <xref target="applicators" />, an applicator keyword may
576576
refer to a schema to be applied, rather than including it as a
577577
subschema in the applicator's value. In such situations, the
578-
schema being applied is known as the referred schema, while
579-
the schema containing the applicator keyword is the referring schema.
578+
schema being applied is known as the referenced schema, while
579+
the schema containing the applicator keyword is the referencing schema.
580580
</t>
581581
<t>
582582
While root schemas and subschemas are static concepts based on a
583-
schema's position within a schema document, referred and referring
583+
schema's position within a schema document, referenced and referencing
584584
schemas are dynamic. Different pairs of schemas may find themselves
585-
in various referred and referring arrangements during the evaluation
585+
in various referenced and referencing arrangements during the evaluation
586586
of an instance against a schema.
587587
</t>
588588
<t>
589589
For some by-reference applicators, such as
590-
<xref target="ref">"$ref"</xref>, the referred schema can be determined
590+
<xref target="ref">"$ref"</xref>, the referenced schema can be determined
591591
by static analysis of the schema document. Others may take evaluation
592592
context into account, and only be resolvable in the process of evaluating
593593
with an instance.
@@ -740,8 +740,8 @@
740740
allow tools to follow the correct behaviour.
741741
</t>
742742
<t>
743-
Note that the recursive nature of meta-schemas requires re-defining
744-
recursive keywords in the extended meta-schema, as can be seen in
743+
The "$recursiveRef" and "$recursiveAnchor" keywords exists to make
744+
extending recursive schemas such as meta-schemas easier, as can be seen in
745745
the JSON Hyper-Schema meta-schema.
746746
</t>
747747
</section>
@@ -957,8 +957,8 @@
957957
<t>
958958
Several keywords can be used to reference a schema which is to be applied to the
959959
current instance location. "$ref" and "$recursiveRef" are an applicator
960-
keywords, applying the referred schema to the instance. "$recursiveAnchor"
961-
is a helper keyword that controls how the referred schema of "$recursiveRef"
960+
keywords, applying the referenced schema to the instance. "$recursiveAnchor"
961+
is a helper keyword that controls how the referenced schema of "$recursiveRef"
962962
is determined.
963963
</t>
964964
<t>
@@ -986,49 +986,40 @@
986986
</t>
987987
</section>
988988

989-
<section title='Recursive References with "$recursiveRefe" and "$recursiveAnchor"'>
989+
<section title='Recursive References with "$recursiveRef" and "$recursiveAnchor"'>
990990
<t>
991991
The "$recursiveRef" and "$recursiveAnchor" keywords are used to construct
992-
extensible recursive schemas.
992+
extensible recursive schemas. A recursive schema is one that has
993+
a reference to its own root, identified by the empty fragment
994+
URI reference ("#").
993995
</t>
994996
<t>
995-
Intuitively, when using "$ref" or another
996-
similar keyword to combine a recursive schema with another schema (recursive
997-
or otherwise), the goal of the schema author is often to have the
998-
recursion respect that combination. The recursive reference would
999-
ideally always recurse to where the processing of the schema started.
1000-
</t>
1001-
<t>
1002-
But this is not possible with the static behavior of "$ref", wich can
1003-
only refer to the root schema of the current schema document.
1004-
More accurately, it can only refer to one location, and that location
1005-
is constrained by the static rules for resolving URI References.
1006-
</t>
1007-
<t>
1008-
This constraint leads to verbose and error-prone re-definitions of each
1009-
recursive element, as can be seen in the meta-scheme for JSON Hyper-Schema
1010-
in all prior drafts.
997+
Extending a recursive schema with "$ref" alone involves redefining all
998+
recursive references in the source schema to point to the root of the
999+
extension. This produces the correct recursive behavior in the extension,
1000+
which is that all recursion should reference the root of the extension.
10111001
</t>
10121002
<figure>
10131003
<preamble>
1014-
Consider the following two schemas. The first (given the id "basic")
1015-
is an object with one string property and one reference property.
1016-
The reference is recursive, pointing to the root of the current
1017-
schema document. The second schema references the first, and
1018-
also describes a "things" property, which is an array of
1019-
recursive references.
1004+
Consider the following two schemas. The first schema, identified
1005+
as "original" as it is the schema to be extended, describes
1006+
an object with one string property and one recursive reference
1007+
property, "r". The second schema, identified as "extension",
1008+
references the first, and describes an additional things" property,
1009+
which is an array of recursive references.
1010+
It also repeats the description of "r" from the original schema.
10201011
</preamble>
10211012
<artwork>
10221013
<![CDATA[
10231014
{
10241015
"$schema": "http://json-schema.org/draft-08/schema#",
1025-
"$id": "https://example.com/basic",
1026-
"$comment": "$ref: # referrs here from in this 'basic' file",
1016+
"$id": "https://example.com/original",
1017+
10271018
"properties": {
10281019
"name": {
10291020
"type": "string"
10301021
},
1031-
"recursive": {
1022+
"r": {
10321023
"$ref": "#"
10331024
}
10341025
}
@@ -1037,9 +1028,12 @@
10371028
{
10381029
"$schema": "http://json-schema.org/draft-08/schema#",
10391030
"$id": "https://example.com/extension",
1040-
"$comment": "$ref: # referrs here from in this 'extension' file",
1041-
"$ref": "basic",
1031+
1032+
"$ref": "original",
10421033
"properties": {
1034+
"r": {
1035+
"$ref": "#"
1036+
},
10431037
"things": {
10441038
"type": "array"
10451039
"items": {
@@ -1051,32 +1045,39 @@
10511045
]]>
10521046
</artwork>
10531047
<postamble>
1054-
The problem is that the referred targets of the
1055-
<spanx style="verb">"$ref": "#"</spanx>
1056-
references are statically determined. Since the
1057-
<spanx style="verb">"things"</spanx> array is in the
1058-
combined schema, its referred schema is the combined
1059-
schema. But the <spanx style="verb">"recursive"</spanx>
1060-
property in the basic schema still points to the root
1061-
of that basic schema, and therefore will not see the
1062-
description of the <spanx style="verb">"things"</spanx>
1063-
property. What we want is for it to resolve
1064-
to the combined schema as well.
1048+
This apparent duplication is important because
1049+
it resolves to "https://example.com/extension#", meaning that
1050+
for instance validated against the extension schema, the value
1051+
of "r" must be valid according to the extension, and not just the
1052+
original schema as "r" was described there.
10651053
</postamble>
10661054
</figure>
1055+
<t>
1056+
This approach is fine for a single recursive field, but the more
1057+
complicated the original schema, the more redefinitions are necessary
1058+
in the extension. This leads to a verbose and error-prone extension,
1059+
which must be kept synchronized with the original schema if the
1060+
original changes its recursive fields.
1061+
This approach can be seen in the meta-schema for JSON Hyper-Schema
1062+
in all prior drafts.
1063+
</t>
10671064
<section title='Enabling Recursion with "$recursiveAnchor"'>
10681065
<t>
1069-
Since the desired behavior can seem surprising, and unpredictable,
1070-
it is important to use keywords to explicitly control all aspects
1071-
of the behavior. In order to create a recursive reference, we
1072-
must do three things:
1066+
The desired behavior is for the recursive reference, "r", in the
1067+
original schema to resolve to the original schema when that
1068+
is the only schema being used, but to resolve to the extension
1069+
schema when using the extension. Then there would be no need
1070+
to redefine the "r" property, or others like it, in the extension.
1071+
</t>
1072+
<t>
1073+
In order to create a recursive reference, we must do three things:
10731074
<list>
10741075
<t>
1075-
In our "basic" schema, indicate that the schema author
1076+
In our original schema, indicate that the schema author
10761077
intends for it to be extensible recursively.
10771078
</t>
10781079
<t>
1079-
In our "extension" schema, indicate that it is intended
1080+
In our extension schema, indicate that it is intended
10801081
to be a recursive extension.
10811082
</t>
10821083
<t>
@@ -1093,22 +1094,25 @@
10931094
The "$recursiveAnchor" keyword is how schema authors indicate
10941095
that a schema can be extended recursively, and be a recursive
10951096
schema. This keyword MAY appear in the root schema of a
1096-
schema document, and MUST NOT appear in a subschema.
1097+
schema document, and MUST NOT appear in any subschema.
10971098
</t>
10981099
<t>
10991100
The value of "$recursiveAnchor" MUST be of type boolean, and
11001101
MUST be true. The value false is reserved for possible future use.
11011102
</t>
1103+
</section>
1104+
<section title='Dynamically recursive references with "$recursiveRef"'>
11021105
<t>
11031106
The "$recursiveRef" keyword behaves identically to "$ref", except
1104-
that if the referred schema has "$recursiveAnchor" set to true,
1105-
then the implementation MUST check the dyanamic scope to see
1106-
if "$recursiveAnchor" had previously been set. If so, then the
1107-
referred schema is considered to be the outermost (in terms of
1108-
dynamic scope) schema object containing "$recursiveAnchor" set to true.
1107+
that if the referenced schema has "$recursiveAnchor" set to true,
1108+
then the implementation MUST examine the dynamic scope for the
1109+
outermost (first seen) schema document with "$recursiveAnchor"
1110+
set to true. If such a schema document exists, then the target
1111+
of the "$recursiveRef" MUST be set to that document's URI, in
1112+
place of the URI produced by the rules for "$ref".
11091113
</t>
11101114
<t>
1111-
Note that if the schema to which "$recursiveRef" referrs does not
1115+
Note that if the schema referenced by "$recursiveRef" does not
11121116
contain "$recursiveAnchor" set to true, or if there are no other
11131117
"$recursiveAnchor" keywords set to true anywhere further back in
11141118
the dynamic scope, then "$recursiveRef"'s behavior is identical
@@ -1122,14 +1126,14 @@
11221126
<![CDATA[
11231127
{
11241128
"$schema": "http://json-schema.org/draft-08/schema#",
1125-
"$id": "https://example.com/basic",
1129+
"$id": "https://example.com/original",
11261130
"$recursiveAnchor": true,
11271131
11281132
"properties": {
11291133
"name": {
11301134
"type": "string"
11311135
},
1132-
"recursive": {
1136+
"r": {
11331137
"$recursiveRef": "#"
11341138
}
11351139
}
@@ -1140,7 +1144,7 @@
11401144
"$id": "https://example.com/extension",
11411145
"$recursiveAnchor": true,
11421146
1143-
"$ref": "basic",
1147+
"$ref": "original",
11441148
"properties": {
11451149
"things": {
11461150
"type": "array"
@@ -1153,21 +1157,36 @@
11531157
]]>
11541158
</artwork>
11551159
<postamble>
1156-
Now lets consider the evaluation of the "extension" schema.
1157-
Note that the "$ref": "basic" was not changed, as it works
1158-
just fine as a normals static reference. And the
1159-
"$recursiveRef" in the "extended" schema does not behave at
1160-
all differently, because the "$recursiveAnchor" in its
1161-
referred schema is the outermost "$recursiveAnchor" in the
1162-
dynamic scope. However, the "$recursiveRef" in the "basic"
1163-
schema referrs to a "$recursiveAnchor" that is not the
1164-
outermost such keyword in the dynamic scope. That is still
1165-
the "$recursiveAnchor" in the "extension" schema.
1166-
Therefore, when processing starts with the extension
1167-
schema, the "$recursiveRef" in the basic schema actually
1168-
referrs to the "extension" schema's root schema.
1160+
Note that the "r" property no longer appears in the
1161+
extension schema. Instead, all "$ref"s have been changed
1162+
to "$recursiveRef"s, and both schemas have "$recursiveAnchor"
1163+
set to true in their root schema.
11691164
</postamble>
11701165
</figure>
1166+
<t>
1167+
When using the original schema on its own, there is no change
1168+
in behavior. The "$recursiveRef" does lead to a schema where
1169+
"$recursiveAnchor" is set to true, but since the original schema
1170+
is the only schema document in the dynamics scope (it references
1171+
itself, and does not reference any other schema documents), the
1172+
behavior is effectively the same as "$ref".
1173+
</t>
1174+
<t>
1175+
When using the extension schema, the "$recursiveRef" within
1176+
that schema (for the array items within "things") also effectively
1177+
behaves like "$ref". The extension schema is the outermost
1178+
dynamic scope, so the reference target is not changed.
1179+
</t>
1180+
<t>
1181+
In contrast, when using the extension schema, the "$recursiveRef"
1182+
for "r" in the original schema now behaves differently. Its
1183+
initial target is the root schema of the original schema document,
1184+
which has "$recursiveAnchor" set to true. In this case, the
1185+
outermost dynamic scope that also has "$recursiveAnchor" set to
1186+
true is the extension schema. So when using the extensions schema,
1187+
"r"'s reference in the original schema will resolve to
1188+
"https://example.com/extension#", not "https://example.com/original#".
1189+
</t>
11711190
</section>
11721191
</section>
11731192

@@ -1459,7 +1478,7 @@
14591478
The application can use the schema location path to determine which
14601479
values are which. The values in the feature's immediate "enabled"
14611480
property schema are more specific, while the values under the re-usable
1462-
schema that is referred to with "$ref" are more generic. The schema
1481+
schema that is referenced to with "$ref" are more generic. The schema
14631482
location path will show whether each value was found by crossing a
14641483
"$ref" or not.
14651484
</t>

0 commit comments

Comments
 (0)