Optional properties are not nullable, contrary to OCPI 2.3.0 §2.8
The schemas model optionality only through required. Because they declare openapi: 3.1.0 (JSON Schema 2020-12), an optional property such as type: string accepts a string or absence — but not null.
OCPI 2.3.0 §2.8 (Cardinality) defines optional fields as permitting both encodings:
? — An optional object. If not set, it might be null, or the field might be omitted. When the field is set to null or omitted and it has a default value, the value is the default value.
* — A list of zero or more objects. If empty, it might be null, [] or the field might be omitted.
Since §2.8 defines the cardinality symbols used in every object table in the specification, this governs every optional field in every module. A receiver must accept null, and a sender may legitimately emit it.
The result is that validating a conformant OCPI payload against these schemas produces false errors.
Reproduction
Against ocpi/2.3.0/components/schema.yaml on main, using the 2020-12 dialect that OpenAPI 3.1 mandates:
| Payload |
BusinessDetails |
Per §2.8 |
Validator |
{"name": "Acme", "website": null} |
optional string as null |
valid |
✗ None is not of type 'string' |
{"name": "Acme", "logo": null} |
optional $ref as null |
valid |
✗ None is not of type 'object' |
{"before_taxes": 1.0, "taxes": null} |
optional array as null (Price) |
valid |
✗ None is not of type 'array' |
{"url": …, "width": null} |
optional integer as null (Image) |
valid |
✗ None is not of type 'integer' |
This repository already documents null as permitted
The descriptions affirm what the types reject, which suggests the restriction is unintended rather than a deliberate tightening:
ocpi/2.3.0/components/schema.yaml — OCPIErrorResponse.data: "Content is not strictly defined by OCPI and may be null."
ocpi/2.2.1/modules/locations/sender-interface.yaml — 200 response: "Fields that are not specified may be considered as null values."
The same wording appears in the normative document's module GET descriptions, and §2.1.4 states that on PUT, omitted optional fields "revert to their default value which is either specified in the protocol or NULL."
Scope
334 optional properties across 11 files in ocpi/2.3.0, in three shapes:
| Shape |
Count |
Suggested encoding |
scalar (string/number/integer/boolean) |
188 |
type: [string, "null"] |
array |
75 |
type: [array, "null"] |
$ref |
70 |
anyOf: [$ref, {type: "null"}] |
untyped (OCPIResponse.data) |
1 |
no change — already unconstrained |
ocpi/2.2.1 has the same pattern and would need the same treatment.
Questions before doing the bulk change
- Is this the intended direction? An alternative reading is that these schemas should describe the recommended encoding (omit rather than null) and stay strict. That would contradict §2.8's "must accept both" requirement for receivers, but it is a defensible editorial choice and worth settling before 334 properties change.
anyOf or oneOf for the $ref case? Both validate correctly here since the referenced schemas never admit null. I've used anyOf as the more conservative option, but will follow your preference.
- How would you like it split? One PR per module, one per version, or a single sweep?
I have a reference implementation ready, covering components/schema.yaml only — 9 properties, all three shapes — so the pattern can be reviewed before it is applied broadly:
main...wolffseb:fix/nullable-optional-properties — 1 file, 11 lines.
It applies the encodings in the table above. Validating payloads against that file with the 2020-12 dialect gives 12/12 passing afterwards versus 8/12 before, where four of the checks are guard rails confirming that required fields, wrong types and malformed $ref values are all still rejected. npm run build:ocpi:2.3.0 completes successfully.
I wasn't able to open it as a pull request — CreatePullRequest returns a permissions error for my account, and I notice the repository has no PRs to date, so I assume they aren't open to outside contributors yet. Happy to submit it in whatever form suits you, and to do the remaining modules once the approach is agreed.
Optional properties are not nullable, contrary to OCPI 2.3.0 §2.8
The schemas model optionality only through
required. Because they declareopenapi: 3.1.0(JSON Schema 2020-12), an optional property such astype: stringaccepts a string or absence — but notnull.OCPI 2.3.0 §2.8 (Cardinality) defines optional fields as permitting both encodings:
Since §2.8 defines the cardinality symbols used in every object table in the specification, this governs every optional field in every module. A receiver must accept
null, and a sender may legitimately emit it.The result is that validating a conformant OCPI payload against these schemas produces false errors.
Reproduction
Against
ocpi/2.3.0/components/schema.yamlonmain, using the 2020-12 dialect that OpenAPI 3.1 mandates:BusinessDetails{"name": "Acme", "website": null}None is not of type 'string'{"name": "Acme", "logo": null}$refas nullNone is not of type 'object'{"before_taxes": 1.0, "taxes": null}Price)None is not of type 'array'{"url": …, "width": null}Image)None is not of type 'integer'This repository already documents
nullas permittedThe descriptions affirm what the types reject, which suggests the restriction is unintended rather than a deliberate tightening:
ocpi/2.3.0/components/schema.yaml—OCPIErrorResponse.data: "Content is not strictly defined by OCPI and may be null."ocpi/2.2.1/modules/locations/sender-interface.yaml— 200 response: "Fields that are not specified may be considered as null values."The same wording appears in the normative document's module GET descriptions, and §2.1.4 states that on PUT, omitted optional fields "revert to their default value which is either specified in the protocol or NULL."
Scope
334 optional properties across 11 files in
ocpi/2.3.0, in three shapes:string/number/integer/boolean)type: [string, "null"]arraytype: [array, "null"]$refanyOf: [$ref, {type: "null"}]OCPIResponse.data)ocpi/2.2.1has the same pattern and would need the same treatment.Questions before doing the bulk change
anyOforoneOffor the$refcase? Both validate correctly here since the referenced schemas never admitnull. I've usedanyOfas the more conservative option, but will follow your preference.I have a reference implementation ready, covering
components/schema.yamlonly — 9 properties, all three shapes — so the pattern can be reviewed before it is applied broadly:main...wolffseb:fix/nullable-optional-properties— 1 file, 11 lines.It applies the encodings in the table above. Validating payloads against that file with the 2020-12 dialect gives 12/12 passing afterwards versus 8/12 before, where four of the checks are guard rails confirming that required fields, wrong types and malformed
$refvalues are all still rejected.npm run build:ocpi:2.3.0completes successfully.I wasn't able to open it as a pull request —
CreatePullRequestreturns a permissions error for my account, and I notice the repository has no PRs to date, so I assume they aren't open to outside contributors yet. Happy to submit it in whatever form suits you, and to do the remaining modules once the approach is agreed.