|
1006 | 1006 | </section>
|
1007 | 1007 | </section>
|
1008 | 1008 |
|
1009 |
| - <section title='Schema References With "$ref"' anchor="ref"> |
| 1009 | + <section title="Schema References"> |
1010 | 1010 | <t>
|
1011 |
| - The "$ref" keyword can be used to reference a schema which is to be applied to the |
1012 |
| - current instance location. "$ref" is an applicator key word, applying the referred |
1013 |
| - schema to the instance. |
| 1011 | + Several keywords can be used to reference a schema which is to be applied to the |
| 1012 | + 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" |
| 1015 | + is determined. |
1014 | 1016 | </t>
|
1015 | 1017 | <t>
|
1016 |
| - The value of the "$ref" property MUST be a string which is a URI Reference. |
1017 |
| - Resolved against the current URI base, it identifies the URI of a schema to use. |
| 1018 | + As the value of "$ref" and "$recursiveRef" are URI References, this allows |
| 1019 | + the possibility to externalise or divide a schema across multiple files, |
| 1020 | + and provides the ability to validate recursive structures through |
| 1021 | + self-reference. |
1018 | 1022 | </t>
|
1019 | 1023 | <t>
|
1020 |
| - As the value of "$ref" is a URI Reference, this allows the possibility to externalise or |
1021 |
| - divide a schema across multiple files, and provides the ability to validate recursive structures |
1022 |
| - through self-reference. |
1023 |
| - </t> |
1024 |
| - <t> |
1025 |
| - The URI is not a network locator, only an identifier. A schema need not be |
1026 |
| - downloadable from the address if it is a network-addressable URL, and |
1027 |
| - implementations SHOULD NOT assume they should perform a network operation when they |
1028 |
| - encounter a network-addressable URI. |
1029 |
| - </t> |
1030 |
| - <t> |
1031 |
| - A schema MUST NOT be run into an infinite loop against a schema. For example, if two |
1032 |
| - schemas "#alice" and "#bob" both have an "allOf" property that refers to the other, |
1033 |
| - a naive validator might get stuck in an infinite recursive loop trying to validate |
1034 |
| - the instance. |
1035 |
| - Schemas SHOULD NOT make use of infinite recursive nesting like this; the behavior is |
1036 |
| - undefined. |
| 1024 | + The resolved URI produced by these keywords is not necessarily a network |
| 1025 | + locator, only an identifier. A schema need not be downloadable from the |
| 1026 | + address if it is a network-addressable URL, and implementations SHOULD NOT |
| 1027 | + assume they should perform a network operation when they encounter |
| 1028 | + a network-addressable URI. |
1037 | 1029 | </t>
|
| 1030 | + |
| 1031 | + <section title='Direct References with "$ref"' anchor="ref"> |
| 1032 | + <t> |
| 1033 | + The "$ref" keyword is used to reference a statically identified schema. |
| 1034 | + </t> |
| 1035 | + <t> |
| 1036 | + The value of the "$ref" property MUST be a string which is a URI Reference. |
| 1037 | + Resolved against the current URI base, it identifies the URI of a schema |
| 1038 | + to use. |
| 1039 | + </t> |
| 1040 | + </section> |
| 1041 | + |
| 1042 | + <section title='Recursive References with "$recursiveRefe" and "$recursiveAnchor"'> |
| 1043 | + <t> |
| 1044 | + The "$recursiveRef" and "$recursiveAnchor" keywords are used to construct |
| 1045 | + extensible recursive schemas. |
| 1046 | + </t> |
| 1047 | + <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. |
| 1064 | + </t> |
| 1065 | + <figure> |
| 1066 | + <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. |
| 1073 | + </preamble> |
| 1074 | + <artwork> |
| 1075 | +<![CDATA[ |
| 1076 | +{ |
| 1077 | + "$schema": "http://json-schema.org/draft-08/schema#", |
| 1078 | + "$id": "https://example.com/basic", |
| 1079 | + "$comment": "$ref: # referrs here from in this 'basic' file", |
| 1080 | + "properties": { |
| 1081 | + "name": { |
| 1082 | + "type": "string" |
| 1083 | + }, |
| 1084 | + "recursive": { |
| 1085 | + "$ref": "#" |
| 1086 | + } |
| 1087 | + } |
| 1088 | +} |
| 1089 | +
|
| 1090 | +{ |
| 1091 | + "$schema": "http://json-schema.org/draft-08/schema#", |
| 1092 | + "$id": "https://example.com/extension", |
| 1093 | + "$comment": "$ref: # referrs here from in this 'extension' file", |
| 1094 | + "$ref": "basic", |
| 1095 | + "properties": { |
| 1096 | + "things": { |
| 1097 | + "type": "array" |
| 1098 | + "items": { |
| 1099 | + "$ref": "#" |
| 1100 | + } |
| 1101 | + } |
| 1102 | + } |
| 1103 | +} |
| 1104 | +]]> |
| 1105 | + </artwork> |
| 1106 | + <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. |
| 1118 | + </postamble> |
| 1119 | + </figure> |
| 1120 | + <section title='Enabling Recursion with "$recursiveAnchor"'> |
| 1121 | + <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: |
| 1126 | + <list> |
| 1127 | + <t> |
| 1128 | + In our "basic" schema, indicate that the schema author |
| 1129 | + intends for it to be extensible recursively. |
| 1130 | + </t> |
| 1131 | + <t> |
| 1132 | + In our "extension" schema, indicate that it is intended |
| 1133 | + to be a recursive extension. |
| 1134 | + </t> |
| 1135 | + <t> |
| 1136 | + Use a reference keyword that explicitly activates the |
| 1137 | + recursive behavior at the point of reference. |
| 1138 | + </t> |
| 1139 | + </list> |
| 1140 | + These three things together ensure that all schema authors |
| 1141 | + are intentionally constructing a recursive extension, which in |
| 1142 | + turn gives all uses of the regular "$ref" keyword confidence |
| 1143 | + that it only behaves as it appears to, using lexical scoping. |
| 1144 | + </t> |
| 1145 | + <t> |
| 1146 | + The "$recursiveAnchor" keyword is how schema authors indicate |
| 1147 | + that a schema can be extended recursively, and be a recursive |
| 1148 | + schema. This keyword MAY appear in the root schema of a |
| 1149 | + schema document, and MUST NOT appear in a subschema. |
| 1150 | + </t> |
| 1151 | + <t> |
| 1152 | + The value of "$recursiveAnchor" MUST be of type boolean, and |
| 1153 | + MUST be true. The value false is reserved for possible future use. |
| 1154 | + </t> |
| 1155 | + <t> |
| 1156 | + 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. |
| 1162 | + </t> |
| 1163 | + <t> |
| 1164 | + Note that if the schema to which "$recursiveRef" referrs does not |
| 1165 | + contain "$recursiveAnchor" set to true, or if there are no other |
| 1166 | + "$recursiveAnchor" keywords set to true anywhere further back in |
| 1167 | + the dynamic scope, then "$recursiveRef"'s behavior is identical |
| 1168 | + to that of "$ref". |
| 1169 | + </t> |
| 1170 | + <figure> |
| 1171 | + <preamble> |
| 1172 | + With this in mind, we can rewrite the previous example: |
| 1173 | + </preamble> |
| 1174 | + <artwork> |
| 1175 | +<![CDATA[ |
| 1176 | +{ |
| 1177 | + "$schema": "http://json-schema.org/draft-08/schema#", |
| 1178 | + "$id": "https://example.com/basic", |
| 1179 | + "$recursiveAnchor": true, |
| 1180 | +
|
| 1181 | + "properties": { |
| 1182 | + "name": { |
| 1183 | + "type": "string" |
| 1184 | + }, |
| 1185 | + "recursive": { |
| 1186 | + "$recursiveRef": "#" |
| 1187 | + } |
| 1188 | + } |
| 1189 | +} |
| 1190 | +
|
| 1191 | +{ |
| 1192 | + "$schema": "http://json-schema.org/draft-08/schema#", |
| 1193 | + "$id": "https://example.com/extension", |
| 1194 | + "$recursiveAnchor": true, |
| 1195 | +
|
| 1196 | + "$ref": "basic", |
| 1197 | + "properties": { |
| 1198 | + "things": { |
| 1199 | + "type": "array" |
| 1200 | + "items": { |
| 1201 | + "$recursiveRef": "#" |
| 1202 | + } |
| 1203 | + } |
| 1204 | + } |
| 1205 | +} |
| 1206 | +]]> |
| 1207 | + </artwork> |
| 1208 | + <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. |
| 1222 | + </postamble> |
| 1223 | + </figure> |
| 1224 | + </section> |
| 1225 | + </section> |
| 1226 | + |
| 1227 | + <section title="Guarding Against Inifinite Recursion"> |
| 1228 | + <t> |
| 1229 | + A schema MUST NOT be run into an infinite loop against an instance. For |
| 1230 | + example, if two schemas "#alice" and "#bob" both have an "allOf" property |
| 1231 | + that refers to the other, a naive validator might get stuck in an infinite |
| 1232 | + recursive loop trying to validate the instance. Schemas SHOULD NOT make |
| 1233 | + use of infinite recursive nesting like this; the behavior is undefined. |
| 1234 | + </t> |
| 1235 | + </section> |
| 1236 | + |
1038 | 1237 | <section title="Loading a referenced schema">
|
1039 | 1238 | <t>
|
1040 | 1239 | The use of URIs to identify remote schemas does not necessarily mean anything is downloaded,
|
|
0 commit comments