Skip to content

Commit b6f9430

Browse files
committed
Fill in missing sections of propDeps proposal and refactor
1 parent f3e23b2 commit b6f9430

File tree

3 files changed

+263
-44
lines changed

3 files changed

+263
-44
lines changed

build/build.js

+18-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jsonschema-core.md

+2-17
Original file line numberDiff line numberDiff line change
@@ -1655,7 +1655,7 @@ User-Agent: product-name/5.4.1 so-cool-json-schema/1.0.2 curl/7.43.0
16551655
Clients SHOULD be able to make requests with a "From" header so that server
16561656
operators can contact the owner of a potentially misbehaving script.
16571657

1658-
## A Vocabulary for Applying Subschemas
1658+
## A Vocabulary for Applying Subschemas {#applicatorvocab}
16591659

16601660
This section defines a vocabulary of applicator keywords that are RECOMMENDED
16611661
for use as the basis of other vocabularies.
@@ -1793,7 +1793,7 @@ successfully validates against its subschema. Implementations MUST NOT evaluate
17931793
the instance against this keyword, for either validation or annotation
17941794
collection purposes, in such cases.
17951795

1796-
##### `dependentSchemas`
1796+
##### `dependentSchemas` {#dependent-schemas}
17971797

17981798
This keyword specifies subschemas that are evaluated if the instance is an
17991799
object and contains a certain property.
@@ -1807,21 +1807,6 @@ property.
18071807

18081808
Omitting this keyword has the same behavior as an empty object.
18091809

1810-
##### `propertyDependencies`
1811-
1812-
This keyword specifies subschemas that are evaluated if the instance is an
1813-
object and contains a certain property with a certain string value.
1814-
1815-
This keyword's value MUST be an object. Each value in the object MUST be an
1816-
object whose values MUST be valid JSON Schemas.
1817-
1818-
If the outer object key is a property in the instance and the inner object key
1819-
is equal to the value of that property, the entire instance must validate
1820-
against the schema. Its use is dependent on the presence and value of the
1821-
property.
1822-
1823-
Omitting this keyword has the same behavior as an empty object.
1824-
18251810
### Keywords for Applying Subschemas to Child Instances
18261811

18271812
Each of these keywords defines a rule for applying its subschema(s) to child

proposals/propertyDependencies.md

+243-25
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
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

1324
For 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

5798
This keyword specifies subschemas that are evaluated if the instance is an
5899
object and contains a certain property with a certain string value.
@@ -67,8 +108,185 @@ property.
67108

68109
Omitting 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 | <[email protected]> | <https://github.com/jdesrosiers> |

0 commit comments

Comments
 (0)