-
-
Notifications
You must be signed in to change notification settings - Fork 239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FEEDBACK WANTED - API proposal - supporting multiple JSON schema versions, breaking changes to Schema
definition
#242
Comments
I like this idea of (roughly) Your insight that this divorces the output type from the weird differences of various JSON schema revisions is extremely compelling. This even opens the door for a generic "schema" representation that can have a One could imagine schema descriptions even unrelated to JSON schema... though perhaps that's a bridge too far. In addition, I think it makes a ton of sense to have the structural use of JSON schema types live in a different crate -- the design goals are distinct and not necessarily well-aligned. One question: do you definitely want to use This does seem to complicate hand-written Cool stuff; would be happy to contribute; I've already been planning a 2020-12 JSON Schema crate for an OpenAPI v3.1 compatible version of https://crates.io/crates/openapiv3 |
Hello, The changes all make sense, as my main (only) use case for the library is to produce programmatically the For the ability to produce extra schema outputs, I feel like that having the option to specify in the I’d like the ability to also output the data to yaml format if possible, it would be useful at least for my use case I think. But the more I think about it, the more I think that I might just want a macro that creates an I think all the specific getters/setters like |
If schemars does't use
That mechanism you suggest is almost exactly the same as in the proposal, except that the proposal standardises on 2020-12 instead of 2019-09!
As above,
I believe that's already possible, both in schemars 0.8 and with the proposed change. As long as
Another handy thing you would be able to do with this proposal is convert a let s1: schemars::Schema = schema_for!(SomeStruct);
let s2: openapiv3::Schema = serde_json::from_value(s1.into()).unwrap(); |
This proposal has now been implemented on the v1 branch, and released to crates.io as 1.0.0-alpha.1 - please try it out and let me know what you think! |
Disclaimer: I haven't used First off, Is my understanding correct that with this change, |
I have no plans to widen the scope of the project to support any schemas beyond JSON Schema. The only way that would be possible with schemars would be to use schemars to generate a JSON schema, and then convert that JSON schema to the desired schema type. |
Closing as this is implemented in 1.0.0-alpha.1 (and beyond) |
Sorry for the late reply + on a closed issue, i've only started to use I'm using I must be the only one that actually use this part of |
In Project Status and Road to 1.0, one of the requirements for schemars 1.0 was "handling of different JSON schema versions/dialects (and how to handle future JSON schema versions)". Handling future versions of JSON schema in a non-breaking way would have been difficult-to-impossible a few years ago, but should be achievable now that the latest version of JSON Schema is expected to be stable - see https://json-schema.org/blog/posts/future-of-json-schema and https://json-schema.org/blog/posts/the-last-breaking-change.
This API proposal shows how the way a JSON Schema is modelled within schemars may be changed to support any arbitrary version of JSON schema without future breaking changes (although this change itself would be breaking).
The World Today
Currently, schemars has a few types that define a schema which are mostly based on the draft-07 version of JSON Schema - most prominently (simplified for brevity):
Fun fact: the multiple
#[serde(flatten)]
'd fields were an optimisation to try to save a little memory for the common case of simple schemas with only one or two properties. This was probably a premature optimisation that needlessly complicated everything!Strongly-typing the schema likes this makes it easier for both schemars and its consumers to keep schemas in a valid state. However, it also causes some problems, particularly around supporting multiple versions of JSON Schema, e.g.
definitions
instead of$defs
which has been preferred since 2019-09items
keyword is defined as aSingleOrVec<Schema>
(i.e. it can be a single schema or an array of schemas), but since 2020-12 it can only be a single schema$schema
is only defined in the top-most schema (RootSchema
), but can leagally appear in subschemas e.g. when bundling subschemas with different$schema
sInstanceType
is defined as an exhaustive enum, even though vocabularies may define their own types (likeinteger
, which schemars supports despite it not being defined by JSON Schema)Some of these problems could be solved by one-time breaking change to schemars that completely drops support for older versions of JSON schema (and swagger/openapi). Alternatively, we could change these structs to match JSON Schema 2020-12, while semi-supporting old versions by using the
extensions
map to set a value if it does not conform to the 2020-12 types. But even then, supporting future versions of JSON Schema may introduce new problems: non-breaking changes like adding a new keyword may be difficult to support in schemars in a non-breaking fashion, unless pretty much every struct is annotated with#[non_exhaustive]
, which would make constructing schemas much more difficult - and that still wouldn't be sufficient to support other potential non-breaking changes (e.g. if an existing keyword was updated to allow additional data types).Proposed Change
As anyone who knows my feelings on JS vs TS can attest, I am a vehement proponent of strict typing - but I think that going forward, schemars should no longer define a
Schema
type with a list of fields corresponding to JSON Schema keywords. More concretely, I propose that schemars defineSchema
as simply:where the inner
Value
is either aValue::Object
orValue::Bool
. Then, properties of the schema (assuming it's an object, not a bool) can be any arbitary JSON-compatible value. The innerValue
is notpub
, so is not actually part of the public API.Note that this would be conceptually similar to:
An advantage of the enum instead of the newtype struct would be that it makes invalid states (e.g. trying to use a number as a schema) unrepresentable in the type system. The main reason I'm proposing a newtype struct instead is to allow converting a
&Value
/&mut Value
to a&Schema
/&mut Schema
(probably via ref-cast), which would be useful in a number of scenarios including implementing visitors - this is why the struct has#[repr(transparent)]
. It should be impossible to construct aSchema
from aValue
that is neither a bool nor an object (hence the inner value field not beingpub
), and any functions exposed by schemars that construct aSchema
must uphold this invariant - so e.g.Schema
would implementTryFrom<Value>
rather thanFrom<Value>
.While this would be a fairly major breaking change for any consumers of schemars who construct and/or manipulate schemas, the vast majority of consumers who just
#[derive(JsonSchema)]
, generate a schema for their types and serialise it to JSON would not be affected by this proposed change. And conveniently for me, the vast majority of schemars's tests can be left largely as they are!Notable traits and functions on a
Schema
would include:For convenience, schemars could also export a macro similar to serde_json's
json!()
that constructs aSchema
while ensuring it's passed an object or bool:Note that such a macro would probably not validate that all properties are well-defined JSON Schema keywords, e.g.
json_schema!({ "foobar": 123 })
would be allowed. Bear in mind an equivalent schema can be already constructed today due to the existingextensions
field.Further possibilities
In lieu of fields,
Schema
could also have getter/setter/mutator functions to aid processing and manipulating schemas. Then if new keywords are added to JSON Schema, corresponding functions could be added to schemars as a non-breaking change. Defining these would be fairly straightforward for "simple" properties like strings or numbers:But for more complex properties that may require in-place mutation, this may require functions like
xyz_mut(&mut self) -> Option<&mut XYZ>
which would require schemars to define new types to wrap the underlying&mut Value
. It may also be useful to define an entry-like API instead of (or as well as) thexyz_mut
functions. Either way, such methods are not part of this proposal, but could be added later as a non-breaking change. Until/unless that happens, the main way to manipulate schema properties would be with either theget
/get_mut
/set
methods proposed above, or getting a mut reference to the schema's underlyingMap
.How different JSON Schema versions would be supported
When implementing
JsonSchema
on a type, it's currently not clear which version of JSON schema should be produced - schemars currently assumes that the generated schema is compatible with draft 2019-09 (which the currentSchema
/SchemaObject
definition is mostly-compatible with), but this isn't documented anywhere. So I propose these high-level guidelines for determing which version of JSON schema to produce:json_schema()
function may check the requestedmeta_schema
(available on the settings of theSchemaGenerator
passed in as an argument) to determine which type/version of JSON schema has been requested, and generate the schema according to that versionSchemaGenerator
will transform the schema to the originally requested version using something like theVisitor
s that exist today. If the requested version is newer than 2020-12 (i.e. a future version) then there should be no work required, assuming that all future versions are indeed backward-compatibleOpen questions:
meta_schema
sufficient, or shouldSchemaSettings
also/instead have some sort of "compatibility mode", e.g. to support custom meta schemas that are based on a specific version of JSON schema?The text was updated successfully, but these errors were encountered: