Skip to content

Commit 729a601

Browse files
authored
take a consistent stance wrt additionalProperties interpretation (#773)
1 parent 681e311 commit 729a601

File tree

4 files changed

+521
-24
lines changed

4 files changed

+521
-24
lines changed

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ if you can articulate the output you'd ideally like to see.
2020

2121
## JSON Schema → Rust types
2222

23+
JSON Schema is a constraint language designed for validation. As a result, it
24+
is not well-suited--and is often seemingly hostile--to translation into
25+
constructive type systems. It allows for expressions of arbitrary complexity
26+
with an infinity of ways to articulate a given set of constraints. As such,
27+
typify does its best to discern an appropriate interpretation, but it is far
28+
from perfect!
29+
2330
Typify translates JSON Schema types in a few different ways depending on some
2431
basic properties of the schema:
2532

@@ -93,6 +100,29 @@ flattened members, this is one of the weaker areas of code generation.
93100

94101
Issues describing example schemas and desired output are welcome and helpful.
95102

103+
### AdditionalProperties
104+
105+
The `additionalProperties` constraint lets a schema define what *non-specified*
106+
properties are permitted. A value of `false` means that no other properties are
107+
permitted (this is expressed in Rust with the `#[serde(deny_unknown_fields)]`
108+
annotation). The absence of `additionalProperties` or a value of `true` are
109+
equivalent constructions that mean that any other property is permitted.
110+
111+
Without other properties, an object that permits additional properties will be
112+
represented as a map type. In conjunction with other properties, typify employs
113+
a heuristic interpretation. Absent or with a value of `true`, additional
114+
properties are ignored. If, however, `additionalProperties` has another value
115+
(i.e. a schema), the generated type will have a map field annotated with
116+
`#[serde(flatten)]`.
117+
118+
Note that this is true of **any** schema value for `additionalProperties` that
119+
is not a boolean. This includes values that would be equivalent with regard to
120+
validation such as the schema `{}` or `{ "not": false }` or any of the other
121+
infinity of equivalent schemas. One can therefore construct a `struct` with
122+
named properties **and** a flattened map of additional properties by using a
123+
value for `additionalProperties` that is equivalent to `true` or absent with
124+
regard to validation, by using some e.g. `{}`.
125+
96126
## Rust -> Schema -> Rust
97127

98128
Schemas derived from Rust types may include an extension that provides

typify-impl/src/structs.rs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -83,30 +83,12 @@ impl TypeSpace {
8383
Some(a) if a.as_ref() == &Schema::Bool(false) => true,
8484

8585
// We have a permissive schema so all additional properties are
86-
// allowed (None is equivalent to the permissive schema).
87-
Some(a)
88-
if matches!(
89-
a.as_ref(),
90-
Schema::Bool(true)
91-
| Schema::Object(SchemaObject {
92-
metadata: _,
93-
instance_type: None,
94-
format: None,
95-
enum_values: None,
96-
const_value: None,
97-
subschemas: None,
98-
number: None,
99-
string: None,
100-
array: None,
101-
object: None,
102-
reference: None,
103-
extensions: _,
104-
})
105-
) =>
106-
{
107-
false
108-
}
109-
86+
// allowed (None is equivalent to the permissive schema). This is
87+
// so common that it would be distracting to represent them with a
88+
// flattened struct so instead we just ignore them. One can cause
89+
// a flattened struct to be generated by using an equivalently
90+
// permissive schema such as {}.
91+
Some(a) if a.as_ref() == &Schema::Bool(true) => false,
11092
None => false,
11193

11294
// Only particular additional properties are allowed. Note that

typify/tests/schemas/more_types.json

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"$comment": "dumping ground for various types",
3+
"$schema": "http://json-schema.org/draft-07/schema#",
4+
"definitions": {
5+
"ObjectWithNoExtra": {
6+
"properties": {
7+
"foo": {
8+
"type": "string"
9+
}
10+
},
11+
"required": [
12+
"foo"
13+
],
14+
"additionalProperties": false
15+
},
16+
"ObjectWithOkExtra": {
17+
"properties": {
18+
"foo": {
19+
"type": "string"
20+
}
21+
},
22+
"required": [
23+
"foo"
24+
]
25+
},
26+
"ObjectWithYesExtra": {
27+
"properties": {
28+
"foo": {
29+
"type": "string"
30+
}
31+
},
32+
"required": [
33+
"foo"
34+
],
35+
"additionalProperties": true
36+
},
37+
"ObjectWithWhichExtra": {
38+
"properties": {
39+
"foo": {
40+
"type": "string"
41+
}
42+
},
43+
"required": [
44+
"foo"
45+
],
46+
"additionalProperties": {}
47+
},
48+
"ObjectWithStringExtra": {
49+
"properties": {
50+
"foo": {
51+
"type": "string"
52+
}
53+
},
54+
"required": [
55+
"foo"
56+
],
57+
"additionalProperties": {
58+
"type": "string"
59+
}
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)