Skip to content

Commit 062b037

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 3b538ff commit 062b037

File tree

1 file changed

+98
-80
lines changed

1 file changed

+98
-80
lines changed

jsonschema-core.xml

Lines changed: 98 additions & 80 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
@@ -626,20 +626,19 @@
626626
and used with caution when defining additional keywords.
627627
</t>
628628
</section>
629-
<section title="Referred and Referring Schemas" anchor="referred">
629+
<section title="Referenced and Referencing Schemas" anchor="referenced">
630630
<t>
631631
As noted in <xref target="applicators" />, an applicator keyword may
632632
refer to a schema to be applied, rather than including it as a
633633
subschema in the applicator's value. In such situations, the
634-
schema being applied is known as the referred (or referenced) schema,
635-
while the schema containing the applicator keyword is the referring
636-
(or referencing) schema.
634+
schema being applied is known as the referenced schema, while
635+
the schema containing the applicator keyword is the referencing schema.
637636
</t>
638637
<t>
639638
While root schemas and subschemas are static concepts based on a
640-
schema's position within a schema document, referred and referring
639+
schema's position within a schema document, referenced and referencing
641640
schemas are dynamic. Different pairs of schemas may find themselves
642-
in various referred and referring arrangements during the evaluation
641+
in various referenced and referencing arrangements during the evaluation
643642
of an instance against a schema.
644643
</t>
645644
<t>
@@ -1010,8 +1009,8 @@
10101009
<t>
10111010
Several keywords can be used to reference a schema which is to be applied to the
10121011
current instance location. "$ref" and "$recursiveRef" are an applicator
1013-
keywords, applying the referred schema to the instance. "$recursiveAnchor"
1014-
is a helper keyword that controls how the referred schema of "$recursiveRef"
1012+
keywords, applying the referenced schema to the instance. "$recursiveAnchor"
1013+
is a helper keyword that controls how the referenced schema of "$recursiveRef"
10151014
is determined.
10161015
</t>
10171016
<t>
@@ -1039,49 +1038,40 @@
10391038
</t>
10401039
</section>
10411040

1042-
<section title='Recursive References with "$recursiveRefe" and "$recursiveAnchor"'>
1041+
<section title='Recursive References with "$recursiveRef" and "$recursiveAnchor"'>
10431042
<t>
10441043
The "$recursiveRef" and "$recursiveAnchor" keywords are used to construct
1045-
extensible recursive schemas.
1044+
extensible recursive schemas. A recursive schema is one that has
1045+
a reference to its own root, identified by the empty fragment
1046+
URI reference ("#").
10461047
</t>
10471048
<t>
1048-
Intuitively, when using "$ref" or another
1049-
similar keyword to combine a recursive schema with another schema (recursive
1050-
or otherwise), the goal of the schema author is often to have the
1051-
recursion respect that combination. The recursive reference would
1052-
ideally always recurse to where the processing of the schema started.
1053-
</t>
1054-
<t>
1055-
But this is not possible with the static behavior of "$ref", wich can
1056-
only refer to the root schema of the current schema document.
1057-
More accurately, it can only refer to one location, and that location
1058-
is constrained by the static rules for resolving URI References.
1059-
</t>
1060-
<t>
1061-
This constraint leads to verbose and error-prone re-definitions of each
1062-
recursive element, as can be seen in the meta-scheme for JSON Hyper-Schema
1063-
in all prior drafts.
1049+
Extending a recursive schema with "$ref" alone involves redefining all
1050+
recursive references in the source schema to point to the root of the
1051+
extension. This produces the correct recursive behavior in the extension,
1052+
which is that all recursion should reference the root of the extension.
10641053
</t>
10651054
<figure>
10661055
<preamble>
1067-
Consider the following two schemas. The first (given the id "basic")
1068-
is an object with one string property and one reference property.
1069-
The reference is recursive, pointing to the root of the current
1070-
schema document. The second schema references the first, and
1071-
also describes a "things" property, which is an array of
1072-
recursive references.
1056+
Consider the following two schemas. The first schema, identified
1057+
as "original" as it is the schema to be extended, describes
1058+
an object with one string property and one recursive reference
1059+
property, "r". The second schema, identified as "extension",
1060+
references the first, and describes an additional things" property,
1061+
which is an array of recursive references.
1062+
It also repeats the description of "r" from the original schema.
10731063
</preamble>
10741064
<artwork>
10751065
<![CDATA[
10761066
{
10771067
"$schema": "http://json-schema.org/draft-08/schema#",
1078-
"$id": "https://example.com/basic",
1079-
"$comment": "$ref: # referrs here from in this 'basic' file",
1068+
"$id": "https://example.com/original",
1069+
10801070
"properties": {
10811071
"name": {
10821072
"type": "string"
10831073
},
1084-
"recursive": {
1074+
"r": {
10851075
"$ref": "#"
10861076
}
10871077
}
@@ -1090,9 +1080,12 @@
10901080
{
10911081
"$schema": "http://json-schema.org/draft-08/schema#",
10921082
"$id": "https://example.com/extension",
1093-
"$comment": "$ref: # referrs here from in this 'extension' file",
1094-
"$ref": "basic",
1083+
1084+
"$ref": "original",
10951085
"properties": {
1086+
"r": {
1087+
"$ref": "#"
1088+
},
10961089
"things": {
10971090
"type": "array"
10981091
"items": {
@@ -1104,32 +1097,39 @@
11041097
]]>
11051098
</artwork>
11061099
<postamble>
1107-
The problem is that the referred targets of the
1108-
<spanx style="verb">"$ref": "#"</spanx>
1109-
references are statically determined. Since the
1110-
<spanx style="verb">"things"</spanx> array is in the
1111-
combined schema, its referred schema is the combined
1112-
schema. But the <spanx style="verb">"recursive"</spanx>
1113-
property in the basic schema still points to the root
1114-
of that basic schema, and therefore will not see the
1115-
description of the <spanx style="verb">"things"</spanx>
1116-
property. What we want is for it to resolve
1117-
to the combined schema as well.
1100+
This apparent duplication is important because
1101+
it resolves to "https://example.com/extension#", meaning that
1102+
for instance validated against the extension schema, the value
1103+
of "r" must be valid according to the extension, and not just the
1104+
original schema as "r" was described there.
11181105
</postamble>
11191106
</figure>
1107+
<t>
1108+
This approach is fine for a single recursive field, but the more
1109+
complicated the original schema, the more redefinitions are necessary
1110+
in the extension. This leads to a verbose and error-prone extension,
1111+
which must be kept synchronized with the original schema if the
1112+
original changes its recursive fields.
1113+
This approach can be seen in the meta-schema for JSON Hyper-Schema
1114+
in all prior drafts.
1115+
</t>
11201116
<section title='Enabling Recursion with "$recursiveAnchor"'>
11211117
<t>
1122-
Since the desired behavior can seem surprising, and unpredictable,
1123-
it is important to use keywords to explicitly control all aspects
1124-
of the behavior. In order to create a recursive reference, we
1125-
must do three things:
1118+
The desired behavior is for the recursive reference, "r", in the
1119+
original schema to resolve to the original schema when that
1120+
is the only schema being used, but to resolve to the extension
1121+
schema when using the extension. Then there would be no need
1122+
to redefine the "r" property, or others like it, in the extension.
1123+
</t>
1124+
<t>
1125+
In order to create a recursive reference, we must do three things:
11261126
<list>
11271127
<t>
1128-
In our "basic" schema, indicate that the schema author
1128+
In our original schema, indicate that the schema author
11291129
intends for it to be extensible recursively.
11301130
</t>
11311131
<t>
1132-
In our "extension" schema, indicate that it is intended
1132+
In our extension schema, indicate that it is intended
11331133
to be a recursive extension.
11341134
</t>
11351135
<t>
@@ -1146,22 +1146,25 @@
11461146
The "$recursiveAnchor" keyword is how schema authors indicate
11471147
that a schema can be extended recursively, and be a recursive
11481148
schema. This keyword MAY appear in the root schema of a
1149-
schema document, and MUST NOT appear in a subschema.
1149+
schema document, and MUST NOT appear in any subschema.
11501150
</t>
11511151
<t>
11521152
The value of "$recursiveAnchor" MUST be of type boolean, and
11531153
MUST be true. The value false is reserved for possible future use.
11541154
</t>
1155+
</section>
1156+
<section title='Dynamically recursive references with "$recursiveRef"'>
11551157
<t>
11561158
The "$recursiveRef" keyword behaves identically to "$ref", except
1157-
that if the referred schema has "$recursiveAnchor" set to true,
1158-
then the implementation MUST check the dyanamic scope to see
1159-
if "$recursiveAnchor" had previously been set. If so, then the
1160-
referred schema is considered to be the outermost (in terms of
1161-
dynamic scope) schema object containing "$recursiveAnchor" set to true.
1159+
that if the referenced schema has "$recursiveAnchor" set to true,
1160+
then the implementation MUST examine the dynamic scope for the
1161+
outermost (first seen) schema document with "$recursiveAnchor"
1162+
set to true. If such a schema document exists, then the target
1163+
of the "$recursiveRef" MUST be set to that document's URI, in
1164+
place of the URI produced by the rules for "$ref".
11621165
</t>
11631166
<t>
1164-
Note that if the schema to which "$recursiveRef" referrs does not
1167+
Note that if the schema referenced by "$recursiveRef" does not
11651168
contain "$recursiveAnchor" set to true, or if there are no other
11661169
"$recursiveAnchor" keywords set to true anywhere further back in
11671170
the dynamic scope, then "$recursiveRef"'s behavior is identical
@@ -1175,14 +1178,14 @@
11751178
<![CDATA[
11761179
{
11771180
"$schema": "http://json-schema.org/draft-08/schema#",
1178-
"$id": "https://example.com/basic",
1181+
"$id": "https://example.com/original",
11791182
"$recursiveAnchor": true,
11801183
11811184
"properties": {
11821185
"name": {
11831186
"type": "string"
11841187
},
1185-
"recursive": {
1188+
"r": {
11861189
"$recursiveRef": "#"
11871190
}
11881191
}
@@ -1193,7 +1196,7 @@
11931196
"$id": "https://example.com/extension",
11941197
"$recursiveAnchor": true,
11951198
1196-
"$ref": "basic",
1199+
"$ref": "original",
11971200
"properties": {
11981201
"things": {
11991202
"type": "array"
@@ -1206,21 +1209,36 @@
12061209
]]>
12071210
</artwork>
12081211
<postamble>
1209-
Now lets consider the evaluation of the "extension" schema.
1210-
Note that the "$ref": "basic" was not changed, as it works
1211-
just fine as a normals static reference. And the
1212-
"$recursiveRef" in the "extended" schema does not behave at
1213-
all differently, because the "$recursiveAnchor" in its
1214-
referred schema is the outermost "$recursiveAnchor" in the
1215-
dynamic scope. However, the "$recursiveRef" in the "basic"
1216-
schema referrs to a "$recursiveAnchor" that is not the
1217-
outermost such keyword in the dynamic scope. That is still
1218-
the "$recursiveAnchor" in the "extension" schema.
1219-
Therefore, when processing starts with the extension
1220-
schema, the "$recursiveRef" in the basic schema actually
1221-
referrs to the "extension" schema's root schema.
1212+
Note that the "r" property no longer appears in the
1213+
extension schema. Instead, all "$ref"s have been changed
1214+
to "$recursiveRef"s, and both schemas have "$recursiveAnchor"
1215+
set to true in their root schema.
12221216
</postamble>
12231217
</figure>
1218+
<t>
1219+
When using the original schema on its own, there is no change
1220+
in behavior. The "$recursiveRef" does lead to a schema where
1221+
"$recursiveAnchor" is set to true, but since the original schema
1222+
is the only schema document in the dynamics scope (it references
1223+
itself, and does not reference any other schema documents), the
1224+
behavior is effectively the same as "$ref".
1225+
</t>
1226+
<t>
1227+
When using the extension schema, the "$recursiveRef" within
1228+
that schema (for the array items within "things") also effectively
1229+
behaves like "$ref". The extension schema is the outermost
1230+
dynamic scope, so the reference target is not changed.
1231+
</t>
1232+
<t>
1233+
In contrast, when using the extension schema, the "$recursiveRef"
1234+
for "r" in the original schema now behaves differently. Its
1235+
initial target is the root schema of the original schema document,
1236+
which has "$recursiveAnchor" set to true. In this case, the
1237+
outermost dynamic scope that also has "$recursiveAnchor" set to
1238+
true is the extension schema. So when using the extensions schema,
1239+
"r"'s reference in the original schema will resolve to
1240+
"https://example.com/extension#", not "https://example.com/original#".
1241+
</t>
12241242
</section>
12251243
</section>
12261244

@@ -1512,7 +1530,7 @@
15121530
The application can use the schema location path to determine which
15131531
values are which. The values in the feature's immediate "enabled"
15141532
property schema are more specific, while the values under the re-usable
1515-
schema that is referred to with "$ref" are more generic. The schema
1533+
schema that is referenced to with "$ref" are more generic. The schema
15161534
location path will show whether each value was found by crossing a
15171535
"$ref" or not.
15181536
</t>

0 commit comments

Comments
 (0)