1
- # JSON Schema Proposal: ` propertyDependencies ` Keyword
1
+ # JSON Schema: The ` propertyDependencies ` Keyword
2
2
3
3
## Abstract
4
4
5
- This document proposes a change to the JSON Schema Core specification and
6
- Applicator vocabulary by adding the ` propertyDependencies ` keyword.
5
+ The ` propertyDependencies ` keyword is a more friendly way to select between two
6
+ or more schemas to validate an instance against than is currently supported by
7
+ JSON Schema.
8
+
9
+ ## Status
10
+
11
+ ** Current Status** : PROPOSAL
12
+
13
+ TODO: We should have a short standard blurb outlining the stages involved in a
14
+ feature making its way to stable status.
15
+
16
+ TODO: Link to a document that describes the proposal => stable process in
17
+ detail.
7
18
8
19
## Note to Readers
9
20
10
- The issues list for this proposal can be found at
21
+ The issues list for this document can be found at
11
22
< https://github.com/json-schema-org/json-schema-spec/issues?q=is%3Aissue+propertydependencies > .
12
23
13
24
For additional information, see < https://json-schema.org/ > .
@@ -19,40 +30,70 @@ listed on the homepage.
19
30
20
31
## Conventions and Terminology
21
32
22
- All conventions and terms used and defined by the JSON Schema Core specification
23
- also apply to this document.
33
+ All conventions and terms used and defined by the [ JSON Schema Core
34
+ specification ] ( ../jsonschema-core.html ) also apply to this document.
24
35
25
36
## Overview
26
37
27
38
### Problem Statement
39
+ A common need in JSON Schema is to select between one schema or another to
40
+ validate an instance based on the value of some property in the JSON instance.
41
+ There are a several patterns people use to accomplish this, but they all have
42
+ significant [ problems] ( #problems ) .
28
43
29
- <!-- What problem exists that needs solving? -->
44
+ OpenAPI solves this problem with the ` discriminator ` keyword. However, their
45
+ approach is more oriented toward code generation concerns, is poorly specified
46
+ when it comes to validation, and is couple to OpenAPI concepts that don't exist
47
+ is JSON Schema. Therefore, it's necessary to define something new rather than
48
+ adopt ` discriminator ` .
30
49
31
50
### Solution
32
51
33
- <!-- What is the solution? -->
52
+ The ` dependentSchemas ` keyword is very close to what is needed except it checks
53
+ for the presence of a property rather than it's value. The chosen solution is to
54
+ build on that concept to solve this problem.
34
55
35
- ### Alternatives
56
+ ``` jsonschema
57
+ {
58
+ "propertyDependencies": {
59
+ "foo": {
60
+ "aaa": { "$ref": "#/$defs/foo-aaa" }
61
+ }
62
+ }
63
+ }
64
+ ```
36
65
37
- <!-- What other options have been considered? (summary, not detailed) -->
66
+ The validation result is equivalent to the following schema.
38
67
39
- ### Limitations
40
-
41
- <!-- Are there any limitations inherent to the proposal? -->
68
+ ``` jsonschema
69
+ {
70
+ "if": {
71
+ "properties": {
72
+ "foo": { "const": "aaa" }
73
+ },
74
+ "required": ["foo"]
75
+ },
76
+ "then": { "$ref": "#/$defs/foo-aaa" }
77
+ }
78
+ ```
42
79
43
- ### Examples
44
-
45
- <!-- How will this feature be used? -->
80
+ ### Limitations
46
81
47
- ## Proposal
82
+ The problem of choosing an alternative based on a property value could apply for
83
+ a value of any JSON type, but ` propertyDependencies ` only solves this problem
84
+ when the value is a string. One of the main goals of this keyword is to define
85
+ something that's intuitive enough and easy enough to use that people will
86
+ actually use it rather than fallback to ` oneOf ` because it's simple. Achieving
87
+ those goals means that some trade-offs need to be made. {{alternatives}} lists
88
+ some alternatives that we considered.
48
89
49
- ### Target for Change
90
+ ## A Vocabulary for Applying Subschemas
50
91
51
- This proposal will add the {{propertydependencies}} section contained herein as
52
- a subsection of JSON Schema Core, section 10.2.2 "Keywords for Applying
53
- Subschemas Conditionally."
92
+ This document adds the ` propertyDependencies ` keyword to the
93
+ ` https://json-schema.org/vocab/applicator ` [ applicator
94
+ vocabulary ] ( ../jsonschema-core.html#applicatorvocab ) .
54
95
55
- ### New Keyword: ` propertyDependencies ` {#propertydependencies}
96
+ ### ` propertyDependencies `
56
97
57
98
This keyword specifies subschemas that are evaluated if the instance is an
58
99
object and contains a certain property with a certain string value.
@@ -67,8 +108,185 @@ property.
67
108
68
109
Omitting this keyword has the same behavior as an empty object.
69
110
111
+ ## [ Appendix] Problems With Existing Patterns {#problems}
112
+
113
+ ### ` oneOf ` /` anyOf `
114
+
115
+ The pattern of using ` oneOf ` to describe a choice between two schemas has become
116
+ ubiquitous.
117
+
118
+ ``` jsonschema
119
+ {
120
+ "oneOf": [
121
+ { "$ref": "#/$defs/aaa" },
122
+ { "$ref": "#/$defs/bbb" }
123
+ ]
124
+ }
125
+ ```
126
+
127
+ However, this pattern has several shortcomings. The main problem is that it
128
+ tends to produce confusing error messages. Some implementations employ
129
+ heuristics to guess the user's intent and provide better messaging, but that's
130
+ not wide-spread or consistent behavior, nor is it expected or required from
131
+ implementations.
132
+
133
+ This pattern is also inefficient. Generally, there is a single value in the
134
+ object that determines which alternative to chose, but the ` oneOf ` pattern has
135
+ no way to specify what that value is and therefore needs to evaluate the entire
136
+ schema. This is made worse in that every alternative needs to be fully validated
137
+ to ensure that only one of the alternative passes and all the others fail. This
138
+ last problem can be avoided by using ` anyOf ` instead, but that pattern is much
139
+ less used.
140
+
141
+ ### ` if ` /` then `
142
+
143
+ We can describe this kind of constraint more efficiently and with with better
144
+ error messaging by using ` if ` /` then ` . This allows the user to explicitly specify
145
+ the constraint to be used to select which alternative the schema should be used
146
+ to validate the schema. However, this pattern has problems of it's own. It's
147
+ verbose, error prone, and not particularly intuitive, which leads most people to
148
+ avoid it.
149
+
150
+ ``` jsonschema
151
+ {
152
+ "allOf": [
153
+ {
154
+ "if": {
155
+ "properties": {
156
+ "foo": { "const": "aaa" }
157
+ },
158
+ "required": ["foo"]
159
+ },
160
+ "then": { "$ref": "#/$defs/foo-aaa" }
161
+ },
162
+ {
163
+ "if": {
164
+ "properties": {
165
+ "foo": { "const": "bbb" }
166
+ },
167
+ "required": ["foo"]
168
+ },
169
+ "then": { "$ref": "#/$defs/foo-bbb" }
170
+ }
171
+ ]
172
+ }
173
+ ```
174
+
175
+ ## [ Appendix] Alternatives Considered {#alternatives}
176
+
177
+ Here are some alternatives that were considered that support all value types.
178
+ All examples have the same validation behavior as the examples above.
179
+
180
+ This version uses an array of objects. Each object is a collection of the
181
+ variables needed to express a property dependency. This doesn't fit the style of
182
+ JSON Schema. There aren't any keywords remotely like this. It's also still too
183
+ verbose. It's a little more intuitive than ` if ` /` then ` and definitely less error
184
+ prone.
185
+
186
+ ``` jsonschema
187
+ {
188
+ "propertyDependencies": [
189
+ {
190
+ "propertyName": "foo",
191
+ "propertySchema": { "const": "aaa" },
192
+ "apply": { "$ref": "#/$defs/foo-aaa" }
193
+ },
194
+ {
195
+ "propertyName": "foo",
196
+ "propertySchema": { "const": "bbb" },
197
+ "apply": { "$ref": "#/$defs/foo-bbb" }
198
+ }
199
+ ]
200
+ }
201
+ ```
202
+
203
+ A slight variation on that example is to make it a map of keyword to dependency
204
+ object. It's still too verbose.
205
+
206
+ ``` jsonschema
207
+ {
208
+ "propertyDependencies": {
209
+ "foo": [
210
+ {
211
+ "propertySchema": { "const": "aaa" },
212
+ "apply": { "$ref": "#/$defs/foo-aaa" }
213
+ },
214
+ {
215
+ "propertySchema": { "const": "bbb" },
216
+ "apply": { "$ref": "#/$defs/foo-bbb" }
217
+ }
218
+ ]
219
+ }
220
+ }
221
+ ```
222
+
223
+ This one is a little more consistent with the JSON Schema style (poor keyword
224
+ naming aside), but otherwise has all the same problems as the other examples.
225
+
226
+ ``` jsonschema
227
+ {
228
+ "allOf": [
229
+ {
230
+ "propertyDependencyName": "foo",
231
+ "propertyDependencySchema": { "const": "aaa" },
232
+ "propertyDependencyApply": { "$ref": "#/$defs/foo-aaa" }
233
+ },
234
+ {
235
+ "propertyDependencyName": "foo",
236
+ "propertyDependencySchema": { "const": "bbb" },
237
+ "propertyDependencyApply": { "$ref": "#/$defs/foo-bbb" }
238
+ }
239
+ ]
240
+ }
241
+ ```
242
+
243
+ This one is a variation of ` if ` that combines ` if ` , ` properties ` , and ` required `
244
+ to reduce boilerplate. It's also essentially a variation of the previous example
245
+ with better names. This avoids to error proneness problem, but it's still too
246
+ verbose.
247
+
248
+ ``` jsonschema
249
+ {
250
+ "allOf": [
251
+ {
252
+ "ifProperties": {
253
+ "foo": { "const": "aaa" }
254
+ },
255
+ "then": { "$ref": "#/$defs/foo-aaa" }
256
+ },
257
+ {
258
+ "ifProperties": {
259
+ "foo": { "const": "bbb" }
260
+ },
261
+ "then": { "$ref": "#/$defs/foo-aaa" }
262
+ }
263
+ ]
264
+ }
265
+ ```
266
+
267
+ All of the previous alternatives use a schema as the discriminator. This
268
+ alternative is a little less powerful in that it can only match on exact values,
269
+ but it successfully addresses the problems we're concerned about with the
270
+ current approaches. The only issue with this alternative is that it's not as
271
+ intuitive as the chosen solution.
272
+
273
+ ``` jsonschema
274
+ {
275
+ "propertyDepenencies": {
276
+ "foo": [
277
+ ["aaa", { "$ref": "#/$defs/foo-aaa" }],
278
+ ["bbb", { "$ref": "#/$defs/foo-bbb" }]
279
+ ]
280
+ }
281
+ }
282
+ ```
283
+
284
+ ## [ Appendix] Change Log
285
+
286
+ * [ October 2023] Created
287
+
70
288
## Champions
71
289
72
- | Champion | Company | Email | URI |
73
- | ----------------------------| ---------| ------------------------- | ----------------------------------|
74
- | Jason Desrosiers | Postman | <tbd > | < https://github.com/jdesrosiers > |
290
+ | Champion | Company | Email | URI |
291
+ | ----------------------------| ---------| ----------------------| ----------------------------------|
292
+ | Jason Desrosiers
| Postman
| < [email protected] > | < https://github.com/jdesrosiers > |
0 commit comments