|
953 | 953 | </section>
|
954 | 954 | </section>
|
955 | 955 |
|
956 |
| - <section title='Schema References With "$ref"' anchor="ref"> |
| 956 | + <section title="Schema References"> |
957 | 957 | <t>
|
958 |
| - The "$ref" keyword can be used to reference a schema which is to be applied to the |
959 |
| - current instance location. "$ref" is an applicator key word, applying the referred |
960 |
| - schema to the instance. |
| 958 | + Several keywords can be used to reference a schema which is to be applied to the |
| 959 | + 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" |
| 962 | + is determined. |
961 | 963 | </t>
|
962 | 964 | <t>
|
963 |
| - The value of the "$ref" property MUST be a string which is a URI Reference. |
964 |
| - Resolved against the current URI base, it identifies the URI of a schema to use. |
| 965 | + As the value of "$ref" and "$recursiveRef" are URI References, this allows |
| 966 | + the possibility to externalise or divide a schema across multiple files, |
| 967 | + and provides the ability to validate recursive structures through |
| 968 | + self-reference. |
965 | 969 | </t>
|
966 | 970 | <t>
|
967 |
| - As the value of "$ref" is a URI Reference, this allows the possibility to externalise or |
968 |
| - divide a schema across multiple files, and provides the ability to validate recursive structures |
969 |
| - through self-reference. |
970 |
| - </t> |
971 |
| - <t> |
972 |
| - The URI is not a network locator, only an identifier. A schema need not be |
973 |
| - downloadable from the address if it is a network-addressable URL, and |
974 |
| - implementations SHOULD NOT assume they should perform a network operation when they |
975 |
| - encounter a network-addressable URI. |
976 |
| - </t> |
977 |
| - <t> |
978 |
| - A schema MUST NOT be run into an infinite loop against a schema. For example, if two |
979 |
| - schemas "#alice" and "#bob" both have an "allOf" property that refers to the other, |
980 |
| - a naive validator might get stuck in an infinite recursive loop trying to validate |
981 |
| - the instance. |
982 |
| - Schemas SHOULD NOT make use of infinite recursive nesting like this; the behavior is |
983 |
| - undefined. |
| 971 | + The resolved URI produced by these keywords is not necessarily a network |
| 972 | + locator, only an identifier. A schema need not be downloadable from the |
| 973 | + address if it is a network-addressable URL, and implementations SHOULD NOT |
| 974 | + assume they should perform a network operation when they encounter |
| 975 | + a network-addressable URI. |
984 | 976 | </t>
|
| 977 | + |
| 978 | + <section title='Direct References with "$ref"' anchor="ref"> |
| 979 | + <t> |
| 980 | + The "$ref" keyword is used to reference a statically identified schema. |
| 981 | + </t> |
| 982 | + <t> |
| 983 | + The value of the "$ref" property MUST be a string which is a URI Reference. |
| 984 | + Resolved against the current URI base, it identifies the URI of a schema |
| 985 | + to use. |
| 986 | + </t> |
| 987 | + </section> |
| 988 | + |
| 989 | + <section title='Recursive References with "$recursiveRefe" and "$recursiveAnchor"'> |
| 990 | + <t> |
| 991 | + The "$recursiveRef" and "$recursiveAnchor" keywords are used to construct |
| 992 | + extensible recursive schemas. |
| 993 | + </t> |
| 994 | + <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. |
| 1011 | + </t> |
| 1012 | + <figure> |
| 1013 | + <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. |
| 1020 | + </preamble> |
| 1021 | + <artwork> |
| 1022 | +<![CDATA[ |
| 1023 | +{ |
| 1024 | + "$schema": "http://json-schema.org/draft-08/schema#", |
| 1025 | + "$id": "https://example.com/basic", |
| 1026 | + "$comment": "$ref: # referrs here from in this 'basic' file", |
| 1027 | + "properties": { |
| 1028 | + "name": { |
| 1029 | + "type": "string" |
| 1030 | + }, |
| 1031 | + "recursive": { |
| 1032 | + "$ref": "#" |
| 1033 | + } |
| 1034 | + } |
| 1035 | +} |
| 1036 | +
|
| 1037 | +{ |
| 1038 | + "$schema": "http://json-schema.org/draft-08/schema#", |
| 1039 | + "$id": "https://example.com/extension", |
| 1040 | + "$comment": "$ref: # referrs here from in this 'extension' file", |
| 1041 | + "$ref": "basic", |
| 1042 | + "properties": { |
| 1043 | + "things": { |
| 1044 | + "type": "array" |
| 1045 | + "items": { |
| 1046 | + "$ref": "#" |
| 1047 | + } |
| 1048 | + } |
| 1049 | + } |
| 1050 | +} |
| 1051 | +]]> |
| 1052 | + </artwork> |
| 1053 | + <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. |
| 1065 | + </postamble> |
| 1066 | + </figure> |
| 1067 | + <section title='Enabling Recursion with "$recursiveAnchor"'> |
| 1068 | + <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: |
| 1073 | + <list> |
| 1074 | + <t> |
| 1075 | + In our "basic" schema, indicate that the schema author |
| 1076 | + intends for it to be extensible recursively. |
| 1077 | + </t> |
| 1078 | + <t> |
| 1079 | + In our "extension" schema, indicate that it is intended |
| 1080 | + to be a recursive extension. |
| 1081 | + </t> |
| 1082 | + <t> |
| 1083 | + Use a reference keyword that explicitly activates the |
| 1084 | + recursive behavior at the point of reference. |
| 1085 | + </t> |
| 1086 | + </list> |
| 1087 | + These three things together ensure that all schema authors |
| 1088 | + are intentionally constructing a recursive extension, which in |
| 1089 | + turn gives all uses of the regular "$ref" keyword confidence |
| 1090 | + that it only behaves as it appears to, using lexical scoping. |
| 1091 | + </t> |
| 1092 | + <t> |
| 1093 | + The "$recursiveAnchor" keyword is how schema authors indicate |
| 1094 | + that a schema can be extended recursively, and be a recursive |
| 1095 | + schema. This keyword MAY appear in the root schema of a |
| 1096 | + schema document, and MUST NOT appear in a subschema. |
| 1097 | + </t> |
| 1098 | + <t> |
| 1099 | + The value of "$recursiveAnchor" MUST be of type boolean, and |
| 1100 | + MUST be true. The value false is reserved for possible future use. |
| 1101 | + </t> |
| 1102 | + <t> |
| 1103 | + 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. |
| 1109 | + </t> |
| 1110 | + <t> |
| 1111 | + Note that if the schema to which "$recursiveRef" referrs does not |
| 1112 | + contain "$recursiveAnchor" set to true, or if there are no other |
| 1113 | + "$recursiveAnchor" keywords set to true anywhere further back in |
| 1114 | + the dynamic scope, then "$recursiveRef"'s behavior is identical |
| 1115 | + to that of "$ref". |
| 1116 | + </t> |
| 1117 | + <figure> |
| 1118 | + <preamble> |
| 1119 | + With this in mind, we can rewrite the previous example: |
| 1120 | + </preamble> |
| 1121 | + <artwork> |
| 1122 | +<![CDATA[ |
| 1123 | +{ |
| 1124 | + "$schema": "http://json-schema.org/draft-08/schema#", |
| 1125 | + "$id": "https://example.com/basic", |
| 1126 | + "$recursiveAnchor": true, |
| 1127 | +
|
| 1128 | + "properties": { |
| 1129 | + "name": { |
| 1130 | + "type": "string" |
| 1131 | + }, |
| 1132 | + "recursive": { |
| 1133 | + "$recursiveRef": "#" |
| 1134 | + } |
| 1135 | + } |
| 1136 | +} |
| 1137 | +
|
| 1138 | +{ |
| 1139 | + "$schema": "http://json-schema.org/draft-08/schema#", |
| 1140 | + "$id": "https://example.com/extension", |
| 1141 | + "$recursiveAnchor": true, |
| 1142 | +
|
| 1143 | + "$ref": "basic", |
| 1144 | + "properties": { |
| 1145 | + "things": { |
| 1146 | + "type": "array" |
| 1147 | + "items": { |
| 1148 | + "$recursiveRef": "#" |
| 1149 | + } |
| 1150 | + } |
| 1151 | + } |
| 1152 | +} |
| 1153 | +]]> |
| 1154 | + </artwork> |
| 1155 | + <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. |
| 1169 | + </postamble> |
| 1170 | + </figure> |
| 1171 | + </section> |
| 1172 | + </section> |
| 1173 | + |
| 1174 | + <section title="Guarding Against Inifinite Recursion"> |
| 1175 | + <t> |
| 1176 | + A schema MUST NOT be run into an infinite loop against an instance. For |
| 1177 | + example, if two schemas "#alice" and "#bob" both have an "allOf" property |
| 1178 | + that refers to the other, a naive validator might get stuck in an infinite |
| 1179 | + recursive loop trying to validate the instance. Schemas SHOULD NOT make |
| 1180 | + use of infinite recursive nesting like this; the behavior is undefined. |
| 1181 | + </t> |
| 1182 | + </section> |
| 1183 | + |
985 | 1184 | <section title="Loading a referenced schema">
|
986 | 1185 | <t>
|
987 | 1186 | The use of URIs to identify remote schemas does not necessarily mean anything is downloaded,
|
|
0 commit comments