Skip to content
This repository was archived by the owner on Nov 3, 2023. It is now read-only.

Commit 529f915

Browse files
committed
Add section about the challenges with additionalProperties
1 parent 3ecd694 commit 529f915

File tree

2 files changed

+112
-97
lines changed

2 files changed

+112
-97
lines changed

source/reference/combining.rst

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -159,95 +159,6 @@ not a string:
159159
Properties of Schema Composition
160160
--------------------------------
161161

162-
.. _subschemaindependence:
163-
164-
Subschema Independence
165-
''''''''''''''''''''''
166-
167-
It is important to note that the schemas listed in an `allOf`, `anyOf`
168-
or `oneOf` array know nothing of one another. For example, say you had
169-
a schema for an address in a ``$defs`` section, and want to
170-
"extend" it to include an address type:
171-
172-
.. schema_example::
173-
174-
{
175-
"$defs": {
176-
"address": {
177-
"type": "object",
178-
"properties": {
179-
"street_address": { "type": "string" },
180-
"city": { "type": "string" },
181-
"state": { "type": "string" }
182-
},
183-
"required": ["street_address", "city", "state"]
184-
}
185-
},
186-
187-
"allOf": [
188-
{ "$ref": "#/$defs/address" },
189-
{
190-
"properties": {
191-
"type": { "enum": [ "residential", "business" ] }
192-
}
193-
}
194-
]
195-
}
196-
--
197-
{
198-
"street_address": "1600 Pennsylvania Avenue NW",
199-
"city": "Washington",
200-
"state": "DC",
201-
"type": "business"
202-
}
203-
204-
This works, but what if we wanted to restrict the schema so no
205-
additional properties are allowed? One might try adding the
206-
highlighted line below:
207-
208-
.. schema_example::
209-
210-
{
211-
"$defs": {
212-
"address": {
213-
"type": "object",
214-
"properties": {
215-
"street_address": { "type": "string" },
216-
"city": { "type": "string" },
217-
"state": { "type": "string" }
218-
},
219-
"required": ["street_address", "city", "state"]
220-
}
221-
},
222-
223-
"allOf": [
224-
{ "$ref": "#/$defs/address" },
225-
{
226-
"properties": {
227-
"type": { "enum": [ "residential", "business" ] }
228-
}
229-
}
230-
],
231-
232-
*"additionalProperties": false
233-
}
234-
--X
235-
{
236-
"street_address": "1600 Pennsylvania Avenue NW",
237-
"city": "Washington",
238-
"state": "DC",
239-
"type": "business"
240-
}
241-
242-
Unfortunately, now the schema will reject *everything*. This is
243-
because ``additionalProperties`` knows nothing about the properties
244-
declared in the subschemas inside of the `allOf` array.
245-
246-
To many, this is one of the biggest surprises of the combining
247-
operations in JSON schema: it does not behave like inheritance in an
248-
object-oriented language. There are some proposals to address this in
249-
the next version of the JSON schema specification.
250-
251162
.. _illogicalschemas:
252163

253164
Illogical Schemas

source/reference/object.rst

Lines changed: 112 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
.. _object:
55

66
object
7-
------
7+
======
88

99
.. contents:: :local:
1010

@@ -77,7 +77,7 @@ conventionally referred to as a "property".
7777
.. _properties:
7878

7979
Properties
80-
''''''''''
80+
----------
8181

8282
The properties (key-value pairs) on an object are defined using the
8383
``properties`` keyword. The value of ``properties`` is an object,
@@ -127,7 +127,7 @@ address made up of a number, street name and street type:
127127
.. _patternProperties:
128128

129129
Pattern Properties
130-
''''''''''''''''''
130+
------------------
131131

132132
Sometimes you want to say that, given a particular kind of property
133133
name, the value should match a particular schema. That's where
@@ -181,7 +181,7 @@ are ignored.
181181
.. _additionalproperties:
182182

183183
Additional Properties
184-
'''''''''''''''''''''
184+
---------------------
185185

186186
The ``additionalProperties`` keyword is used to control the handling
187187
of extra stuff, that is, properties whose names are not listed in the
@@ -269,6 +269,110 @@ properties (that are neither defined by ``properties`` nor matched by
269269
// It must be a string:
270270
{ "keyword": 42 }
271271

272+
.. index::
273+
single: object; properties; additionalProperties
274+
single: extending
275+
276+
.. _extending:
277+
278+
Extending Closed Schemas
279+
''''''''''''''''''''''''
280+
281+
It's important to note that ``additionalProperties`` only recognizes
282+
properties declared in the same subschema as itself. So,
283+
``additionalProperties`` can restrict you from "extending" a schema
284+
using `combining` keywords such as `allOf`. In the following example,
285+
we can see how the ``additionalProperties`` can cause attempts to
286+
extend the address schema example to fail.
287+
288+
.. schema_example::
289+
290+
{
291+
"allOf": [
292+
{
293+
"type": "object",
294+
"properties": {
295+
"street_address": { "type": "string" },
296+
"city": { "type": "string" },
297+
"state": { "type": "string" }
298+
},
299+
"required": ["street_address", "city", "state"],
300+
"additionalProperties": false
301+
}
302+
],
303+
304+
"properties": {
305+
"type": { "enum": [ "residential", "business" ] }
306+
},
307+
"required": ["type"]
308+
}
309+
--X
310+
// Fails ``additionalProperties``. "type" is considered additional.
311+
{
312+
"street_address": "1600 Pennsylvania Avenue NW",
313+
"city": "Washington",
314+
"state": "DC",
315+
"type": "business"
316+
}
317+
--X
318+
// Fails ``required``. "type" is required.
319+
{
320+
"street_address": "1600 Pennsylvania Avenue NW",
321+
"city": "Washington",
322+
"state": "DC"
323+
}
324+
325+
Because ``additionalProperties`` only recognizes properties declared
326+
in the same subschema, it considers anything other than
327+
"street_address", "city", and "state" to be additional. Combining the
328+
schemas with `allOf` doesn't change that. A workaround you can use is
329+
to move ``additionalProperties`` to the extending schema and redeclare
330+
the properties from the extended schema.
331+
332+
.. schema_example::
333+
334+
{
335+
"allOf": [
336+
{
337+
"type": "object",
338+
"properties": {
339+
"street_address": { "type": "string" },
340+
"city": { "type": "string" },
341+
"state": { "type": "string" }
342+
},
343+
"required": ["street_address", "city", "state"]
344+
}
345+
],
346+
347+
"properties": {
348+
"street_address": true,
349+
"city": true,
350+
"state": true,
351+
"type": { "enum": [ "residential", "business" ] }
352+
},
353+
"required": ["type"],
354+
"additionalProperties": false
355+
}
356+
--
357+
{
358+
"street_address": "1600 Pennsylvania Avenue NW",
359+
"city": "Washington",
360+
"state": "DC",
361+
"type": "business"
362+
}
363+
--X
364+
{
365+
"street_address": "1600 Pennsylvania Avenue NW",
366+
"city": "Washington",
367+
"state": "DC",
368+
"type": "business",
369+
"something that doesn't belong": "hi!"
370+
}
371+
372+
Now the ``additionalProperties`` keyword is able to recognize all the
373+
necessary properties and the schema works as expected. Keep reading to
374+
see how the ``unevaluatedProperties`` keyword solves this problem
375+
without needing to redeclare properties.
272376

273377
.. index::
274378
single: object; properties
@@ -277,7 +381,7 @@ properties (that are neither defined by ``properties`` nor matched by
277381
.. _unevaluatedproperties:
278382

279383
Unevaluated Properties
280-
''''''''''''''''''''''
384+
----------------------
281385

282386
|draft2019-09|
283387

@@ -290,7 +394,7 @@ Documentation Coming Soon
290394
.. _required:
291395

292396
Required Properties
293-
'''''''''''''''''''
397+
-------------------
294398

295399
By default, the properties defined by the ``properties`` keyword are
296400
not required. However, one can provide a list of required properties
@@ -357,7 +461,7 @@ they don't provide their address or telephone number:
357461
.. _propertyNames:
358462

359463
Property names
360-
''''''''''''''
464+
--------------
361465

362466
|draft6|
363467

@@ -396,7 +500,7 @@ schema given to ``propertyNames`` is always at least::
396500
single: maxProperties
397501

398502
Size
399-
''''
503+
----
400504

401505
The number of properties on an object can be restricted using the
402506
``minProperties`` and ``maxProperties`` keywords. Each of these

0 commit comments

Comments
 (0)