@@ -36,11 +36,14 @@ occurences of [`properties`]({{< ref "2020-12/applicator/properties" >}}),
36
36
[ ` patternProperties ` ] ({{< ref "2020-12/applicator/patternproperties" >}}),
37
37
[ ` additionalProperties ` ] ({{< ref "2020-12/applicator/additionalproperties"
38
38
> }}), and [ ` unevaluatedProperties ` ] ({{< ref
39
- "2020-12/unevaluated/unevaluatedproperties" > }}) itself as long as the evaluate
40
- path that led to [ ` unevaluatedProperties ` ] ({{< ref
41
- "2020-12/unevaluated/unevaluatedproperties" >}}) is a prefix of the evaluate
39
+ "2020-12/unevaluated/unevaluatedproperties" > }}) itself, as long as the
40
+ evaluate path that led to [ ` unevaluatedProperties ` ] ({{< ref
41
+ "2020-12/unevaluated/unevaluatedproperties" >}}) is a _ prefix _ of the evaluate
42
42
path of the others.
43
43
44
+ Given its evaluation-dependent nature, this keyword is evaluated after every
45
+ other keyword from every other vocabulary.
46
+
44
47
{{<best-practice >}}
45
48
46
49
There are two common use cases for this keyword, both for reducing duplication:
@@ -50,12 +53,12 @@ There are two common use cases for this keyword, both for reducing duplication:
50
53
keywords behind conditional logic without duplicating the
51
54
[ ` additionalProperties ` ] ({{< ref "2020-12/applicator/additionalproperties"
52
55
> }}) keyword in every possible branch. (2) Re-using
53
- the [`properties`]({{< ref "2020-12/applicator/properties" > }}) and
54
- [`patternProperties`]({{< ref "2020-12/applicator/patternproperties " > }})
55
- keywords, or the [ ` additionalProperties ` ] ({{< ref
56
- "2020-12/applicator/additionalproperties" >}}) keyword, as helpers while
57
- specialising the helpers with other related keywords as needed in specific
58
- locations without having to inline the entire helper.
56
+ subschemas that consist of the [ ` properties ` ] ({{< ref
57
+ "2020-12/applicator/properties " >}}) and [ ` patternProperties ` ] ({{< ref
58
+ "2020-12/applicator/patternproperties" >}}) keywords, or the
59
+ [ ` additionalProperties ` ] ({{< ref "2020-12/applicator/additionalproperties" >}})
60
+ keyword, as helpers while specialising the helpers with other related keywords
61
+ as needed in specific locations without having to inline the entire helper.
59
62
60
63
{{</best-practice >}}
61
64
@@ -78,230 +81,147 @@ dependencies, leading to schemas that are simpler to evaluate.
78
81
79
82
## Examples
80
83
81
- {{<schema ` Schema with 'unevaluatedProperties' set to boolean true ` >}}
84
+ {{<schema ` A schema that conditionally constrains object instances to define certain properties, with string additional properties in both cases ` >}}
82
85
{
83
86
"$schema": "https://json-schema.org/draft/2020-12/schema ",
84
- "unevaluatedProperties": true
87
+ "if": { "maxProperties": 2 },
88
+ "then": { "properties": { "foo": true } },
89
+ "else": { "patternProperties": { "^@": true } },
90
+ "unevaluatedProperties": { "type": "string" }
85
91
}
86
92
{{</schema >}}
87
93
88
- {{<instance-pass ` All object instances pass against the true schema ` >}}
89
- { "foo": "bar", "baz": 33 }
94
+ {{<instance-pass ` An object value that defines a "foo" property and other string properties is valid ` >}}
95
+ { "foo": 1, "bar": "baz" }
90
96
{{</instance-pass >}}
91
97
92
- {{<instance-pass ` 'unevaluatedProperties' does not have any effect on instances other than an object ` >}}
93
- "John Doe"
98
+ {{<instance-annotation >}}
99
+ { "keyword": "/then/properties", "instance": "", "value": [ "foo" ] }
100
+ { "keyword": "/unevaluatedProperties", "instance": "", "value": [ "bar" ] }
101
+ {{</instance-annotation >}}
102
+
103
+ {{<instance-pass ` An object value that defines multiple properties that start with "@" and other string properties is valid ` >}}
104
+ { "@foo ": 1, "@bar ": 2, "baz": "qux" }
94
105
{{</instance-pass >}}
95
106
96
107
{{<instance-annotation >}}
97
- { "keyword": "/unevaluatedProperties", "instance": "", "value": [ "foo", "baz" ] }
108
+ { "keyword": "/else/patternProperties", "instance": "", "value": [ "@foo ", "@bar " ] }
109
+ { "keyword": "/unevaluatedProperties", "instance": "", "value": [ "baz" ] }
98
110
{{</instance-annotation >}}
99
111
100
- * Here, no properties are defined in the above schema. Consequently, all properties in an object instance are considered unevaluated, and the ` unevaluatedProperties ` subschema applies to them. Since the subschema here is a boolean true, an instance with unevaluated properties, regardless of their value, is considered valid.
101
-
102
- {{<schema ` Schema with 'unevaluatedProperties' set to boolean false ` >}}
103
- {
104
- "$schema": "https://json-schema.org/draft/2020-12/schema ",
105
- "unevaluatedProperties": false
106
- }
107
- {{</schema >}}
112
+ {{<instance-fail ` An object value that defines a "foo" property and other non-string properties is invalid ` >}}
113
+ { "foo": 1, "bar": 2 }
114
+ {{</instance-fail >}}
108
115
109
- {{<instance-fail ` All object instances fail against the false schema ` >}}
110
- { "foo": " bar" }
116
+ {{<instance-fail ` An object value that defines multiple properties that start with "@" and other non-string properties is invalid ` >}}
117
+ { "@ foo ": 1, " @ bar ": 2, "baz": 3 }
111
118
{{</instance-fail >}}
112
119
113
- {{<instance-pass ` 'unevaluatedProperties' does not have any effect on instances other than an object ` >}}
114
- [ "John", 46, false ]
120
+ {{<instance-pass ` An empty object value is valid ` >}}
121
+ {}
115
122
{{</instance-pass >}}
116
123
117
- {{<schema ` Schema with 'unevaluatedProperties', 'properties', and 'patternProperties', with unevaluatedProperties set to boolean false ` >}}
124
+ {{<instance-pass ` A non-object value is valid ` >}}
125
+ "Hello World"
126
+ {{</instance-pass >}}
127
+
128
+ {{<schema ` A schema that constraints object instances to only allow extension keywords that start with "@" using a helper ` >}}
118
129
{
119
130
"$schema": "https://json-schema.org/draft/2020-12/schema ",
120
- "properties": {
121
- "foo": { "type": "string" }
122
- },
123
- "patternProperties": {
124
- "^b": { "type": "number" }
125
- },
126
- "unevaluatedProperties": false
131
+ "properties": { "foo": true },
132
+ "$ref": "#/$defs/allow-extensions",
133
+ "unevaluatedProperties": false,
134
+ "$defs": {
135
+ "allow-extensions": {
136
+ "patternProperties": { "^@": true }
137
+ }
138
+ }
127
139
}
128
140
{{</schema >}}
129
141
130
- {{<instance-fail ` An instance with unevaluated properties is invalid ` >}}
131
- { "foo": "foo", "bar": 36, "fooBar": false }
132
- {{</instance-fail >}}
133
-
134
- {{<instance-pass ` An instance with no unevaluated properties is valid ` >}}
135
- { "foo": "foo", "bar": 36 }
142
+ {{<instance-pass ` An object value that only defines a "foo" property is valid ` >}}
143
+ { "foo": 1 }
136
144
{{</instance-pass >}}
137
145
138
146
{{<instance-annotation >}}
139
147
{ "keyword": "/properties", "instance": "", "value": [ "foo" ] }
140
- { "keyword": "/patternProperties", "instance": "", "value": [ "bar" ] }
141
148
{{</instance-annotation >}}
142
149
143
- * For the first instance, the annotation result of ` properties ` is [ "foo" ] , and the annotation result of ` patternProperties ` is [ "bar" ] . However, the 'fooBar' property remains unevaluated, so the ` unevaluatedProperties ` subschema applies to it. This subschema fails (as any instance against a false schema is always invalid), leading to the failure of the entire schema.
144
- * For the second instance, the annotation result of ` properties ` is [ "foo" ] , and the annotation result of ` patternProperties ` is [ "bar" ] . No properties remain unevaluated; hence, the instance is considered valid.
145
-
146
- {{<schema ` Schema with 'unevaluatedProperties', 'properties', and 'patternProperties', with unevaluatedProperties set to an object subschema ` >}}
147
- {
148
- "$schema": "https://json-schema.org/draft/2020-12/schema ",
149
- "properties": {
150
- "foo": { "type": "string" }
151
- },
152
- "patternProperties": {
153
- "^b": { "type": "number" }
154
- },
155
- "unevaluatedProperties": { "type": "boolean" }
156
- }
157
- {{</schema >}}
158
-
159
- {{<instance-pass ` An instance with no unevaluated properties is valid ` >}}
160
- { "foo": "foo", "bar": 36 }
150
+ {{<instance-pass ` An object value that only defines a "foo" property and other properties that start with "@" is valid ` >}}
151
+ { "foo": 1, "@bar ": 2, "@baz ": 3 }
161
152
{{</instance-pass >}}
162
153
163
154
{{<instance-annotation >}}
164
155
{ "keyword": "/properties", "instance": "", "value": [ "foo" ] }
165
- { "keyword": "/patternProperties", "instance": "", "value": [ "bar" ] }
156
+ { "keyword": "/$defs/allow-extensions/ patternProperties", "instance": "", "value": [ "@ bar ", " @ baz " ] }
166
157
{{</instance-annotation >}}
167
158
168
- {{<instance-pass ` An instance with unevaluated properties that conform to the 'unevaluatedProperties' subschema is valid ` >}}
169
- { "foo": "foo" , "bar": 36 , "fooBar ": false }
159
+ {{<instance-pass ` An object value that only defines properties that start with "@" is valid ` >}}
160
+ { "@ foo ": 1 , "@ bar ": 2 , "@ baz ": 3 }
170
161
{{</instance-pass >}}
171
162
172
163
{{<instance-annotation >}}
173
- { "keyword": "/properties", "instance": "", "value": [ "foo" ] }
174
- { "keyword": "/patternProperties", "instance": "", "value": [ "bar" ] }
175
- { "keyword": "/unevaluatedProperties", "instance": "", "value": [ "fooBar" ] }
164
+ { "keyword": "/$defs/allow-extensions/patternProperties", "instance": "", "value": [ "@foo ", "@bar ", "@baz " ] }
176
165
{{</instance-annotation >}}
177
166
178
- {{<instance-fail ` An instance with unevaluated properties that do not conform to the 'unevaluatedProperties' subschema is invalid ` >}}
179
- { "foo": "foo" , "bar": 36, "fooBar": "string" }
167
+ {{<instance-fail ` An object value that only defines a "foo" property and other properties that do not start with "@" is invalid ` >}}
168
+ { "foo": 1 , "bar": 2 }
180
169
{{</instance-fail >}}
181
- * For the first instance, there are no unevaluated properties.
182
- * For the second instance, 'fooBar' is unevaluated, and the ` unevaluatedProperties ` subschema applies to it. 'fooBar' conforms to this subschema, and hence the instance is valid. The annotations produced by applicators are: ` properties ` → [ "foo" ] , ` patternProperties ` → [ "bar" ] , and ` unevaluatedProperties ` → [ "fooBar" ] .
183
170
184
- {{<schema ` Schema with 'unevaluatedProperties' and 'allOf' keyword ` >}}
185
- {
186
- "$schema": "https://json-schema.org/draft/2020-12/schema ",
187
- "properties": {
188
- "foo": { "type": "string" }
189
- },
190
- "allOf": [
191
- {
192
- "patternProperties": {
193
- "^b": { "type": "number" }
194
- }
195
- }
196
- ] ,
197
- "unevaluatedProperties": { "type": "boolean" }
198
- }
199
- {{</schema >}}
200
-
201
- {{<instance-pass ` An instance with unevaluated properties that conform to the 'unevaluatedProperties' subschema is valid ` >}}
202
- { "foo": "foo", "bar": 36, "fooBar": false }
171
+ {{<instance-pass ` An empty object value is valid ` >}}
172
+ {}
203
173
{{</instance-pass >}}
204
174
205
- {{<instance-annotation >}}
206
- { "keyword": "/properties", "instance": "", "value": [ "foo" ] }
207
- { "keyword": "/allOf/0/patternProperties", "instance": "", "value": [ "foo" ] }
208
- { "keyword": "/unevaluatedProperties", "instance": "", "value": [ "fooBar" ] }
209
- {{</instance-annotation >}}
210
-
211
- {{<instance-fail ` An instance with unevaluated properties that do not conform to the 'unevaluatedProperties' subschema is invalid ` >}}
212
- { "foo": "foo", "bar": 36, "fooBar": "string" }
213
- {{</instance-fail >}}
214
- For the above two instances, the annotation result of ` properties ` is [ "foo" ] , and the annotation result of nested ` patternProperties ` is [ "bar" ] . The ` unevaluatedProperties ` recognizes the annotations from ` properties ` as well as ` patternProperties ` (as it can see through adjacent and nested applicators as only the produced annotations matter, not the schema structure) and ensures that 'fooBar' remains unevaluated and its subschema applies to 'fooBar'.
215
- * The first instance passes as it conforms to the unevaluated subschema.
216
- * The second instance fails as it does not conform to the unevaluated subschema.
175
+ {{<instance-pass ` A non-object value is valid ` >}}
176
+ "Hello World"
177
+ {{</instance-pass >}}
217
178
218
- {{<schema ` Schema with 'unevaluatedProperties' and 'allOf' keyword ` >}}
179
+ {{<schema ` A schema that constraints object instances to not define any properties, as both object keywords are cousins ` >}}
219
180
{
220
181
"$schema": "https://json-schema.org/draft/2020-12/schema ",
221
- "properties": {
222
- "foo": { "type": "string" }
223
- },
224
182
"allOf": [
225
- {
226
- "additionalProperties": true
227
- }
228
- ] ,
229
- "unevaluatedProperties": false
183
+ { "properties": { "foo": true } },
184
+ { "unevaluatedProperties": false }
185
+ ]
230
186
}
231
187
{{</schema >}}
232
188
233
- {{<instance-pass ` An instance with no unevaluated properties is valid ` >}}
234
- { "foo": "foo" }
235
- {{</instance-pass >}}
189
+ {{<instance-fail ` An object value that only defines a "foo" property is invalid as the schema prohibits unevaluated properties ` >}}
190
+ { "foo": 1 }
191
+ {{</instance-fail >}}
236
192
237
- {{<instance-annotation >}}
238
- { "keyword": "/properties", "instance": "", "value": [ "foo" ] },
239
- { "keyword": "/allOf/0/additionalProperties", "instance": "", "value": [ "foo" ] }
240
- {{</instance-annotation >}}
193
+ {{<instance-fail ` An object value that defines any other property is invalid as the schema prohibits unevaluated properties ` >}}
194
+ { "bar": 2 }
195
+ {{</instance-fail >}}
241
196
242
- {{<instance-pass ` An instance with no unevaluated properties is valid ` >}}
243
- { "foo": "foo", "bar": "bar" }
197
+ {{<instance-pass ` An empty object value is valid ` >}}
198
+ {}
244
199
{{</instance-pass >}}
245
200
246
- {{<instance-annotation >}}
247
- { "keyword": "/properties", "instance": "", "value": [ "foo" ] }
248
- { "keyword": "/allOf/0/additionalProperties", "instance": "", "value": [ "foo", "bar" ] }
249
- {{</instance-annotation >}}
250
-
251
- * In the first case, there are no unevaluated properties.
252
- * In the second case, the nested ` { additionalProperties: true } ` evaluated all the remaining properties. So there's nothing left unevaluated.
201
+ {{<instance-pass ` A non-object value is valid ` >}}
202
+ "Hello World"
203
+ {{</instance-pass >}}
253
204
254
- {{<schema ` Schema with 'unevaluatedProperties' and '#ref' keyword ` >}}
205
+ {{<schema ` A schema that constraints object instances to define arbitrary properties ` >}}
255
206
{
256
207
"$schema": "https://json-schema.org/draft/2020-12/schema ",
257
- "properties": {
258
- "foo": { "type": "string" }
259
- },
260
- "$ref": "#/$defs/bar",
261
- "unevaluatedProperties": false,
262
- "$defs": {
263
- "bar": {
264
- "properties": {
265
- "bar": { "type": "string" }
266
- }
267
- }
268
- }
208
+ "allOf": [ { "unevaluatedProperties": true } ] ,
209
+ "unevaluatedProperties": false
269
210
}
270
211
{{</schema >}}
271
212
272
- {{<instance-pass ` An instance with no unevaluated properties is valid ` >}}
273
- { "foo": "foo" , "bar": "bar" }
213
+ {{<instance-pass ` An object value that defines any property is valid as the nested applicator takes precedence ` >}}
214
+ { "foo": 1 , "bar": 2, "baz": 3 }
274
215
{{</instance-pass >}}
275
216
276
217
{{<instance-annotation >}}
277
- { "keyword": "/properties", "instance": "", "value": [ "foo" ] }
278
- { "keyword": "/$ref/properties", "instance": "", "value": [ "bar" ] }
218
+ { "keyword": "/allOf/0/unevaluatedProperties", "instance": "", "value": [ "foo", "bar", "baz" ] }
279
219
{{</instance-annotation >}}
280
220
281
- {{<instance-fail ` An instance with unevaluated properties is invalid ` >}}
282
- { "foo": "foo", "bar": "bar", "baz": "baz" }
283
- {{</instance-fail >}}
284
-
285
- {{<schema ` Schema with nested 'unevaluatedProperties' keyword ` >}}
286
- {
287
- "$schema": "https://json-schema.org/draft/2020-12/schema ",
288
- "properties": {
289
- "foo": { "type": "string" }
290
- },
291
- "allOf": [
292
- {
293
- "unevaluatedProperties": true
294
- }
295
- ] ,
296
- "unevaluatedProperties": false
297
- }
298
- {{</schema >}}
299
-
300
- {{<instance-pass ` No properties remain unevaluated for the top-level 'unevaluatedProperties' ` >}}
301
- { "foo": "foo", "bar": 101 }
221
+ {{<instance-pass ` An empty object value is valid ` >}}
222
+ {}
302
223
{{</instance-pass >}}
303
224
304
- {{<instance-annotation >}}
305
- { "keyword": "/properties", "instance": "", "value": [ "foo" ] }
306
- { "keyword": "/allOf/0/unevaluatedProperties", "instance": "", "value": [ "foo", "bar" ] }
307
- {{</instance-annotation >}}
225
+ {{<instance-pass ` A non-object value is valid ` >}}
226
+ "Hello World"
227
+ {{</instance-pass >}}
0 commit comments