1- # JSON Schema Proposal: ` propertyDependencies ` Keyword
1+ # JSON Schema: The ` propertyDependencies ` Keyword
22
33## Abstract
44
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.
718
819## Note to Readers
920
10- The issues list for this proposal can be found at
21+ The issues list for this document can be found at
1122< https://github.com/json-schema-org/json-schema-spec/issues?q=is%3Aissue+propertydependencies > .
1223
1324For additional information, see < https://json-schema.org/ > .
@@ -19,40 +30,70 @@ listed on the homepage.
1930
2031## Conventions and Terminology
2132
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.
2435
2536## Overview
2637
2738### 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 ) .
2843
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 ` .
3049
3150### Solution
3251
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.
3455
35- ### Alternatives
56+ ``` jsonschema
57+ {
58+ "propertyDependencies": {
59+ "foo": {
60+ "aaa": { "$ref": "#/$defs/foo-aaa" }
61+ }
62+ }
63+ }
64+ ```
3665
37- <!-- What other options have been considered? (summary, not detailed) -->
66+ The validation result is equivalent to the following schema.
3867
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+ ```
4279
43- ### Examples
44-
45- <!-- How will this feature be used? -->
80+ ### Limitations
4681
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.
4889
49- ### Target for Change
90+ ## A Vocabulary for Applying Subschemas
5091
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 ) .
5495
55- ### New Keyword: ` propertyDependencies ` {#propertydependencies}
96+ ### ` propertyDependencies `
5697
5798This keyword specifies subschemas that are evaluated if the instance is an
5899object and contains a certain property with a certain string value.
@@ -67,8 +108,185 @@ property.
67108
68109Omitting this keyword has the same behavior as an empty object.
69110
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+
70288## Champions
71289
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 | < jdesrosi@gmail.com > | < https://github.com/jdesrosiers > |
0 commit comments