-
-
Notifications
You must be signed in to change notification settings - Fork 213
Draft 2020-12 release notes #392
Changes from 4 commits
42efd51
a5b735b
b2e26e3
6c2c6ba
9107e6a
4f16ba9
b0ef598
cf36567
dc25d41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,185 @@ | |
title: JSON Schema 2020-12 Release Notes | ||
layout: page | ||
--- | ||
## Changes to items and additionalItems | ||
The keywords used for defining arrays and tuples have been redesigned to help | ||
lower the learning curve for JSON Schema. Since the `items` keyword was used for | ||
both types, we would often see people mistakenly defining a tuple when they | ||
meant to define an array and not understand why only the first item in the array | ||
was validating. | ||
|
||
_NOTE: This page is still being written._ | ||
The `items` and `additionalItems` keywords have been replaced with `prefixItems` | ||
and `items` where `prefixItems` has the same functionality as the | ||
array-of-schemas for of the old `items` and the new `items` keyword has the same | ||
functionality as the old `additionalItems` keyword. | ||
|
||
You can find a minimal changelog at the end of the specification documents themselves. | ||
Although the meaning of `items` has changed, the syntax for defining arrays | ||
remains the same. Only the syntax for defining tuples has changed. The idea is | ||
that an array has items (`items`) and optionally has some positionally defined | ||
items that come before the normal items (`prefixItems`). | ||
|
||
Here are some examples to illustrate the changes. | ||
|
||
### Open tuple | ||
<table> | ||
<tr> | ||
<th>Draft 2019-09</th> | ||
<th>Draft 2020-12</th> | ||
</tr> | ||
<tr> | ||
<td> | ||
<pre>{ | ||
"items": [ | ||
{ "$ref": "#/$defs/foo" }, | ||
{ "$ref": "#/$defs/bar" } | ||
] | ||
}</pre> | ||
</td> | ||
<td> | ||
<pre>{ | ||
"prefixItems": [ | ||
{ "$ref": "#/$defs/foo" }, | ||
{ "$ref": "#/$defs/bar" } | ||
] | ||
}</pre> | ||
</td> | ||
</tr> | ||
</table> | ||
|
||
### Closed tuple | ||
<table> | ||
<tr> | ||
<th>Draft 2019-09</th> | ||
<th>Draft 2020-12</th> | ||
</tr> | ||
<tr> | ||
<td> | ||
<pre>{ | ||
"items": [ | ||
{ "$ref": "#/$defs/foo" }, | ||
{ "$ref": "#/$defs/bar" } | ||
], | ||
"additionalItems": false | ||
}</pre> | ||
</td> | ||
<td> | ||
<pre>{ | ||
"prefixItems": [ | ||
{ "$ref": "#/$defs/foo" }, | ||
{ "$ref": "#/$defs/bar" } | ||
], | ||
"items": false | ||
}</pre> | ||
</td> | ||
</tr> | ||
</table> | ||
|
||
### Tuple with constrained additional items | ||
<table> | ||
<tr> | ||
<th>Draft 2019-09</th> | ||
<th>Draft 2020-12</th> | ||
</tr> | ||
<tr> | ||
<td> | ||
<pre>{ | ||
"items": [ | ||
{ "$ref": "#/$defs/foo" }, | ||
{ "$ref": "#/$defs/bar" } | ||
], | ||
"additionalItems": { "$ref": "#/$defs/baz" } | ||
}</pre> | ||
</td> | ||
<td> | ||
<pre>{ | ||
"prefixItems": [ | ||
{ "$ref": "#/$defs/foo" }, | ||
{ "$ref": "#/$defs/bar" } | ||
], | ||
"items": { "$ref": "#/$defs/baz" } | ||
}</pre> | ||
</td> | ||
</tr> | ||
</table> | ||
|
||
## $dynamicRef and $dynamicAnchor | ||
The `$recursiveRef` and `$recursiveAnchor` keywords were replaced by the more | ||
powerful `$dynamicRef` and `$dynamicAnchor` keywords. `$recursiveRef` and | ||
`$recursiveAnchor` were introduced in the previous draft to solve the problem of | ||
extending recursive schemas. As the "recursive" keywords got some use and we | ||
understood them better, we discovered how we could generalize them to solve even | ||
more types of problems. The name change reflects that these keywords are useful | ||
for more than just extending recursive schemas. | ||
|
||
A `$dynamicAnchor` can be thought of like a normal `$anchor` except that it can | ||
be referenced across schemas rather than just in the schema it was defined in. | ||
You can think of the old `$recursiveAnchor` as working the same way except that | ||
it only allowed you to create an anchor at the root of the schema and the anchor | ||
name is always empty. | ||
|
||
`$dynamicRef` works the same as the old `$recursiveRef` except that fragments | ||
are no longer empty (`"$dynamicRef": "#my-anchor"` instead of `"$recursiveRef": | ||
"#"`) and non-fragment-only URIs are allowed. When a `$dynamicRef` contains a | ||
non-fragment-only URI-Reference, the schema the URI-Reference resolves to is | ||
used as the starting point for dynamic resolution. | ||
Comment on lines
+133
to
+135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "the schema the URI-Reference resolves to is used as the starting point for dynamic resolution" -- I don't think this is right:
(https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.8.2.3.2) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see the problem. What about these two statements are in conflict? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The release notes seem to be saying that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the disconnect might be in the meaning of "dynamic resolution". The way I've used it, it includes the processes of determining whether the fragment is a usable dynamic anchor. If it isn't, the result of dynamic resolution is to do nothing and it's effective behavior is that of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I read this as saying that if the target of the initial URI has a $dynamicAnchor present, then the dynamic scope resolution process can continue. Maybe that entire final sentence can be removed. Implementers need to read the spec fully to understand how to implement this keyword. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that it would work if we remove the sentence, but I think it's better if we can provide some brief explanation of what a non-fragment-only URI-Reference would do after telling people that it's allowed. I'd rather try to improve that sentence. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this document the place for that? (e.g. https://github.com/json-schema-org/json-schema-org.github.io/pull/392/files/dc25d4126cd3f54c770480da5aff584476fc68f8#r643340334) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're just talking about a brief note about what has changed and generally what to expect from that change. That's exactly what this document is for. The comment you reference is about expanding the example to showcase the new functionality. I wouldn't go that far in this case either. |
||
|
||
Here's how you would covert a schema using `$recursiveRef` to use `$dynamicRef`. | ||
|
||
<table> | ||
<tr> | ||
<th>Draft 2019-09</th> | ||
<th>Draft 2020-12</th> | ||
</tr> | ||
<tr> | ||
<td><pre>// tree schema, extensible | ||
{ | ||
"$schema": "https://json-schema.org/draft/2019-09/schema", | ||
"$id": "https://example.com/tree", | ||
"$recursiveAnchor": true, | ||
|
||
"type": "object", | ||
"properties": { | ||
"data": true, | ||
"children": { | ||
"type": "array", | ||
"items": { "$recursiveRef": "#" } | ||
} | ||
} | ||
} | ||
|
||
// strict-tree schema, guards against misspelled properties | ||
{ | ||
"$schema": "https://json-schema.org/draft/2019-09/schema", | ||
"$id": "https://example.com/strict-tree", | ||
"$recursiveAnchor": true, | ||
|
||
"$ref": "tree", | ||
"unevaluatedProperties": false | ||
}</pre></td> | ||
<td><pre>// tree schema, extensible | ||
{ | ||
"$schema": "https://json-schema.org/draft/2020-12/schema", | ||
"$id": "https://example.com/tree", | ||
"$dynamicAnchor": "node", | ||
|
||
"type": "object", | ||
"properties": { | ||
"data": true, | ||
"children": { | ||
"type": "array", | ||
"items": { "$dynamicRef": "#node" } | ||
} | ||
} | ||
} | ||
|
||
// strict-tree schema, guards against misspelled properties | ||
{ | ||
"$schema": "https://json-schema.org/draft/2020-12/schema", | ||
"$id": "https://example.com/strict-tree", | ||
"$dynamicAnchor": "node", | ||
|
||
"$ref": "tree", | ||
"unevaluatedProperties": false | ||
}</pre></td> | ||
</tr> | ||
</table> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another side effect of using named anchors is that you can have more than one in the same schema (and perhaps overlapping). The tree example could be extended to allow for two different types of branches, each of which is recursive and each references the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. That's worth mentioning, but I'd rather keep the example simple. I don't what this to become a tutorial and every time I attempted to give a more complex example, it kept snowballing in that direction. The link to this page is in a section called "Migrating from older drafts", so I think it's reasonable to limit the examples to how to convert a 2019-09 schema to a 2020-12 schema.