GithubHelp home page GithubHelp logo

gresau / schemars Goto Github PK

View Code? Open in Web Editor NEW
708.0 708.0 203.0 597 KB

Generate JSON Schema documents from Rust code

Home Page: https://graham.cool/schemars/

License: MIT License

Rust 99.76% Shell 0.24%
json-schema rust serde

schemars's People

Contributors

adamchalmers avatar ahl avatar alex-berger avatar atsukitak avatar davidkna avatar dimbleby avatar gresau avatar jan-auer avatar jirutka avatar luke-biel avatar mutoso avatar mwcampbell avatar obmarg avatar ralpha avatar rokob avatar tamasfe avatar timando avatar trobanga avatar webmaster128 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

schemars's Issues

Support JSON Schema draft 2019-09

  • serialize definitions as $defs (I'm not really sure how to do this easily...) I won't do this, as it would make serialization while preserving backward-compatibility too complicated. Any generated schema using "definitions" would still be valid according to draft 2019-09, "$defs" is practically just a recommendation.
  • set $schema to https://json-schema.org/draft/2019-09/schema
  • allow other attributes alongside $ref (make SchemaGenerator::make_extensible() a no-op)

JsonSchema derive + #[serde(default)] triggers clippy lint in Rust 1.49

Minimal repro:

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
pub struct MyStruct {
    pub my_int: i32,
    pub my_bool: bool,
}

Output when running cargo clippy with Rust 1.48:

work1pass@jareks-mbp-work schemars-test % cargo clippy              
   Compiling proc-macro2 v1.0.24
   Compiling unicode-xid v0.2.1
   Compiling syn v1.0.56
   Compiling serde_derive v1.0.118
   Compiling ryu v1.0.5
   Compiling serde v1.0.118
   Compiling serde_json v1.0.61
   Compiling schemars v0.8.0
    Checking itoa v0.4.7
    Checking dyn-clone v1.0.4
   Compiling quote v1.0.8
   Compiling serde_derive_internals v0.25.0
   Compiling schemars_derive v0.8.0
    Checking schemars-test v0.1.0 (/Users/work1pass/programming/schemars-test)
    Finished dev [unoptimized + debuginfo] target(s) in 17.08s

Output when running cargo clippy with Rust 1.49:

work1pass@jareks-mbp-work schemars-test % cargo clippy
   Compiling proc-macro2 v1.0.24
   Compiling unicode-xid v0.2.1
   Compiling syn v1.0.56
   Compiling serde_derive v1.0.118
   Compiling ryu v1.0.5
   Compiling serde v1.0.118
   Compiling serde_json v1.0.61
    Checking itoa v0.4.7
   Compiling schemars v0.8.0
    Checking dyn-clone v1.0.4
   Compiling quote v1.0.8
   Compiling serde_derive_internals v0.25.0
   Compiling schemars_derive v0.8.0
    Checking schemars-test v0.1.0 (/Users/work1pass/programming/schemars-test)
warning: field assignment outside of initializer for an instance created with Default::default()
 --> src/lib.rs:3:57
  |
3 | #[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
  |                                                         ^^^^^^^^^^
  |
  = note: `#[warn(clippy::field_reassign_with_default)]` on by default
note: consider initializing the variable with `schemars::schema::Metadata { default: JsonSchema, ..Default::default() }` and removing relevant reassignments
 --> src/lib.rs:3:57
  |
3 | #[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
  |                                                         ^^^^^^^^^^
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
  = note: this warning originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

warning: field assignment outside of initializer for an instance created with Default::default()
 --> src/lib.rs:3:57
  |
3 | #[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
  |                                                         ^^^^^^^^^^
  |
note: consider initializing the variable with `schemars::schema::Metadata { default: JsonSchema, ..Default::default() }` and removing relevant reassignments
 --> src/lib.rs:3:57
  |
3 | #[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
  |                                                         ^^^^^^^^^^
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
  = note: this warning originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

warning: 2 warnings emitted

    Finished dev [unoptimized + debuginfo] target(s) in 16.39s

Derive JsonSchema for commonly used types

Hi! This project looks very promising for generating auto documentation for Rocket web servers. I'd like to integrate this into my application, but to do that I need a couple of derivations of JsonSchema that currently don't exists. Mainly, the chrono types for datetimes and tuple structs. Is this something that is on the roadmap, or that you might be able to help me implement myself?

Internally Tagged Enum Definitions

Hey I'm trying to generate typescript types from the generated JSON schema and am running into a bit of a design constraint.

Basically the generated type for an enum that has [#serde(tag="...")] gets turned into an anyOf where the resulting types are directly mentioned without them being given a definition. This is in contrast from for instance using [#serde(tag="t", content="c")] where a new definition is inserted for every type of content.

When generating Typescript the latter is much more convenient since you get a discriminated union where you can still refer to each individual item since they will be named. In contrast the former just gives a giant union blob where it's hard to do things like casting to a specific type.

I would like to figure out a way we can make the JSON schema more verbose and essentially generate definitions for every enum item. Happy to make a PR if someone can point me in the right direction of where to find the relevant code bits.

0.7.0 release

Thanks for this great project. At CosmWasm/cosmwasm#168 we are planning the upgrade to 0.6 or 0.7, but a bit unsure which code branch to use. Is anything missing for a 0.7.0 freeze? Apha versions are online for a couple of months now.

Cheers

impl JsonSchema for serde_json::Value

The current implementation for impl JsonSchema for Value where Value is serde_json::Value does not work well.
https://github.com/GREsau/schemars/blob/master/schemars/src/json_schema_impls/serdejson.rs

Because this is a very special type it might be tricky.
Currently when this value is included in a response it will not set a type.
When a description is given it will set that. But that is about it.
The 'value' variable is the serde_json::Value type in the screenshot below.
Screenshot from 2020-05-25 00-19-49
Because of this UI's have a hard time parsing this.
RapiDoc: (an okay response)
Screenshot from 2020-05-25 00-32-46
Swagger UI: (Does not even acknowledge it, but in schema it is better)
Screenshot from 2020-05-25 00-34-57
Screenshot from 2020-05-25 00-34-05

I know these are issues with UI's and not Schemars. And the Spec does not really give any option for this.
But maybe we can use "allOf", "oneOf", "anyOf" to allow all types ("integer", "number", "string" and "boolean"). This will make sure there is at least something good there.

spurious subschema if there are doc comments

I noticed spurious SubschemaValidation.all_ofs with a single element being included. For example:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "B",
  "type": "object",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "description": "doc comment",
      "allOf": [
        {
          "$ref": "#/definitions/Name"
        }
      ]
    }
  },
  "definitions": {
    "Name": {
      "type": "string"
    }
  }
}

I was able to narrow this down to a simple test case where the presence of a doc comment seems to trigger the behavior:

use schemars::schema_for;
use schemars::JsonSchema;

#[derive(JsonSchema)]
pub struct Name(String);

#[derive(JsonSchema)]
pub struct A {
    /** doc comment */
    pub name: Name,
}

#[derive(JsonSchema)]
pub struct B {
    /// doc comment
    pub name: Name,
}

#[derive(JsonSchema)]
pub struct C {
    // non-doc comment
    pub name: Name,
}

fn main() {
    let schema = schema_for!(A);
    println!("{}", serde_json::to_string_pretty(&schema).unwrap());
    let schema = schema_for!(B);
    println!("{}", serde_json::to_string_pretty(&schema).unwrap());
    let schema = schema_for!(C);
    println!("{}", serde_json::to_string_pretty(&schema).unwrap());
}

... which outputs:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "A",
  "type": "object",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "description": "doc comment",
      "allOf": [
        {
          "$ref": "#/definitions/Name"
        }
      ]
    }
  },
  "definitions": {
    "Name": {
      "type": "string"
    }
  }
}
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "B",
  "type": "object",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "description": "doc comment",
      "allOf": [
        {
          "$ref": "#/definitions/Name"
        }
      ]
    }
  },
  "definitions": {
    "Name": {
      "type": "string"
    }
  }
}
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "C",
  "type": "object",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "$ref": "#/definitions/Name"
    }
  },
  "definitions": {
    "Name": {
      "type": "string"
    }
  }
}

Note that the C does not contain the allOf clause (and has no doc-comment).

I was able to reproduce this on 0.7.6 and 0.8.0-alpha-2.

I'm happy to fix this myself if you'd accept the PR.

enums never have additionalProperties set

Seems like enums never set additionalProperties even if if you denied them using #[schemars(deny_unknown_fields)] or #[serde(deny_unknown_fields)].

My enum:

#[derive(Deserialize, Serialize, JsonSchema)]
///Every event that the game sends to the editor
#[serde(deny_unknown_fields)]
pub enum SendEvents {
    EditRectangle(AddRectangle),
    EditImage(ImageParams)
}
generated json ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "SendEvents", "description": "Every event that the game sends to the editor", "anyOf": [ { "type": "object", "required": [ "EditRectangle" ], "properties": { "EditRectangle": { "$ref": "#/definitions/AddRectangle" } } }, { "type": "object", "required": [ "EditImage" ], "properties": { "EditImage": { "$ref": "#/definitions/ImageParams" } } } ], "definitions": { "AddRectangle": { "type": "object", "required": [ "color", "id", "rectangle" ], "properties": { "color": { "type": "string" }, "id": { "type": "string" }, "rectangle": { "$ref": "#/definitions/Rectangle" } }, "additionalProperties": false }, "ImageParams": { "type": "object", "required": [ "id", "image_path", "location" ], "properties": { "id": { "type": "string" }, "image_path": { "type": "string" }, "location": { "$ref": "#/definitions/Rectangle" } }, "additionalProperties": false }, "Rectangle": { "type": "object", "required": [ "pos", "size" ], "properties": { "pos": { "$ref": "#/definitions/Vector" }, "size": { "$ref": "#/definitions/Vector" } }, "additionalProperties": false }, "Vector": { "type": "object", "required": [ "x", "y" ], "properties": { "x": { "type": "number", "format": "float" }, "y": { "type": "number", "format": "float" } }, "additionalProperties": false } } } ```

the repo where this happens: https://github.com/lenscas/silver_editor/tree/feature/more_forms (the json schema's are generated by a buildscript in /editor, the types that are being used to generate the schema are in /event_types/src/events.rs

I wouldn't mind opening a pull request myself to fix this, however I haven't really made macro's before so not sure if I can fix it myself :(

Either way, thank you for making this crate. It allows me to generate typescript types with ease and simplifying a large chunk from my current project because of it!

Rust type names are not preserved

In case of an exported Rust type, the generated schema does not respect its name.
For example:

#[derive(Serialize, Deserialize, JsonSchema)]
pub struct MyObject<D> {
    pub data: D,
}

pub type MyObjectType = MyObject<String>;

fn main() {
    let alias_schema = schema_for!(MyObjectType);
    println!("schema: \n{:?}", serde_json::to_string_pretty(&alias_schema).unwrap());
}

In this example, the object's name in the schema is MyObject_for_String, would it be possible instead to use MyObjectType as name?

Nevertheless, I am not sure if this can be achieved in Rust.

Rustdoc comments to title/description fields

The clap and structopt crates use the rustdoc comment for describing cli arguments.
I think the same approach can be used to fill jsonschemas title and description fields.

One way of interpreting those is, to use the rustdoc comment for description, expect if the first line starts with a markdown heading character ("#") which would be used for the title instead. Example:

struct foo {
   /// # The title
   /// The description text
   an_int: u16
};

WDYT?

Runtime error when generating a schema for a complex tagged enum

First of all, awesome library! I was dabbling with writing something similar and was very happy that this library exists!

I ran into an issue when trying to generate a schema for a tagged enum. See reproducer below.

use serde::{Serialize, Deserialize};
use schemars::{JsonSchema, schema_for};

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct BValues {
    a: u32,
    b: u32,
}

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type")]
pub enum Foo {
    #[serde(rename = "a")]
    A,
    #[serde(rename = "b")]
    B(BValues),
}


fn main() -> Result<(), Box<dyn std::error::Error>> {
    let schema = schema_for!(Foo)?;
    println!("{}", serde_json::to_string_pretty(&schema)?); // fails
    Ok(())
}

This produces the following error:

Error: JsonSchemaError { msg: "Only schemas with type `object` can be flattened.", schema: Ref(Ref { reference: "#/definitions/BValues" }) }

support for "examples" annotations

is there any support for examples annotation entries available in schemars and how to realize this kind of augmentation with serde resp. docstrings or anything similar?

unfortunately i couldn't find anything about this kind of non-validation oriented auxiliary annotations in the present schemars documentation and examples.

chrono test error

Hi

before run test i added in schemars/Cargo.toml
[dev-dependencies]
chrono = { version = "0.4" }

and removed
[[test]]
name = "chrono"
required-features = ["chrono"]

run (my OS Debian GNU/Linux 10)
cargo test chrono

result

schemars/tests/chrono.rs:9:5
|
9 | date_time: DateTime,
| ^^^^^^^^^ function or associated item not found in chrono::datetime::DateTime<chrono::offset::utc::Utc>
|
= note: the method add_schema_as_property exists but the following trait bounds were not satisfied:
&chrono::datetime::DateTime<chrono::offset::utc::Utc> : schemars::JsonSchema
&mut chrono::datetime::DateTime<chrono::offset::utc::Utc> : schemars::JsonSchema

Feature request: required property level attriubte

Consider the below code:

#[macro_use]
extern crate validator_derive;
extern crate validator;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use schemars::{schema_for, JsonSchema};

#[derive(Debug, Validate, Deserialize, Default, JsonSchema)]
#[serde(default)]
struct Preference {
   /// `name` must be at least 4 characters long
   #[validate(length(min = 4))]
   name: String,
   value: bool,
}

The above would mark name as not required, but due to the validate step, it is.
In order for the validator to work, the Deserialization must happen first, hence the container level #[serde(default)].

I would like a property level attribute to be able to hint to schemars, that a field, despite #[serde(default)] is in fact required.

Unless marking fields required is already possible somehow with schemars.

Schemars not understanding #[serde(serialize_with = "serialize_function")] macro

Trying to serialize a struct like this

pub enum Example {   
 #[serde(serialize_with = "serialize_function")]
    Function(String, functions::QueryFn),
}

I get

error[E0277]: the trait bound `for<'r, 's, 't0> fn(std::vec::Vec<datatype::DataType>, &'r std::collections::HashMap<&'s str, datatype::DataType>, &'t0 aw_datastore::worker::Datastore) -> std::result::Result<datatype::DataType, QueryError>: schemars::JsonSchema` is not satisfied
  --> aw-query/src/datatype.rs:18:27
   |
18 | #[derive(Clone,Serialize, JsonSchema)]
   |                           ^^^^^^^^^^ the trait `schemars::JsonSchema` is not implemented for `for<'r, 's, 't0> fn(std::vec::Vec<datatype::DataType>, &'r std::collections::HashMap<&'s str, datatype::DataType>, &'t0 aw_datastore::worker::Datastore) -> std::result::Result<datatype::DataType, QueryError>`
   |
   = note: required because of the requirements on the impl of `schemars::JsonSchema` for `(std::string::String, for<'r, 's, 't0> fn(std::vec::Vec<datatype::DataType>, &'r std::collections::HashMap<&'s str, datatype::DataType>, &'t0 aw_datastore::worker::Datastore) -> std::result::Result<datatype::DataType, QueryError>)`
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

Improve handling of Markdown comments

I was very pleased to see that schemars already includes code comments in the generated schemas. I mainly use schemars to improve the IDE experience, so this is really great.

This feature comes with a basic Markdown parsing feature: it removes single carriage returns (link to the code: https://github.com/GREsau/schemars/blob/master/schemars_derive/src/attr/doc.rs)

In the project I'm currently working on, the comments use more features of Markdown, including lists, code blocks and link references. These currently do not translate well to text.

As an example, here's a comment: https://github.com/fbecart/zinoma/blob/cc38f93734f0a8d79e12f8b5228fecb178bc68fe/src/config/yaml/schema.rs#L64-L90

This renders as the following JSON schema:

"targets": {
      "description": "A build flow is made of [`targets`]. Each target is a unit of work to perform as part of this build flow.\n\n[`targets`]: struct.Target.html\n\nTargets run in parallel by default. To run targets sequentially, you can define dependencies on other targets using the [`dependencies`] keyword.\n\n[`dependencies`]: struct.Target.html#structfield.dependencies\n\nEach target must have a unique name. The target name must be a string. It should start with an alphanumeric character or `_` and contain only alphanumeric characters, `-`, or `_`.\n\n__Example__\n\n```yaml targets: speak_cow: build: echo 'Moo' speak_dog: build: echo 'Woof!' ```\n\nIn this example:\n\n- `zinoma speak_cow` will print `Moo` - `zinoma speak_dog` will print `Woof!` - `zinoma speak_cow speak_dog` will print both `Moo` and `Woof!`, not necessarily in order.",
      "default": {},
      "type": "object",
      "additionalProperties": {
        "$ref": "#/definitions/Target"
      }
    }

Eventually, VSCode renders the documentation in this way:

Screenshot 2020-05-28 at 16 35 32


I investigated a little bit further. IntelliJ only renders the first line of the "description", so it's usually fine. VSCode renders the description as plain text. It is not able to understand any Markdown or HTML.

Ideally, schemars would parse the comments with an actual Mardown parser and render the content to plain text from there.

I couldn't find any off-the-shelf crate able to convert Markdown to plain text. schemars could probably leverage https://crates.io/crates/comrak or https://crates.io/crates/minimad, but there is work to do in both cases.

Problem using the chrono schemas

I have code like this

use schemars::JsonSchema;

#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
pub struct Event {
    pub id: Option<i64>,
    pub timestamp: DateTime<Utc>,
}

And I get the compiler error "the trait schemars::JsonSchema is not implemented for `chrono::DateTimechrono::Utc". Even though it seems to me like https://github.com/GREsau/schemars/blob/5bf8b3075354a690561a7b81fcd1ed7fd56d87bf/schemars/src/json_schema_impls/chrono.rs pretty clearly implements DateTime among other DateTime combos.

Is there some import I'm missing or anything I should try to debug?

Schemars overwrites declarations if there are structs with the same name

mod a {
    use super::*;

    #[derive(JsonSchema)]
    pub struct Config {
        test: String,
    }
}
mod b {
    use super::*;

    #[derive(JsonSchema)]
    pub struct Config {
        test2: String,
    }
}

#[derive(JsonSchema)]
pub struct Config2 {
    a_cfg: a::Config,
    b_cfg: b::Config,
}

generates the following, invalid schema:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Config2",
  "type": "object",
  "required": [
    "a_cfg",
    "b_cfg"
  ],
  "properties": {
    "a_cfg": {
      "$ref": "#/definitions/Config"
    },
    "b_cfg": {
      "$ref": "#/definitions/Config"
    }
  },
  "definitions": {
    "Config": {
      "type": "object",
      "required": [
        "test"
      ],
      "properties": {
        "test": {
          "type": "string"
        }
      }
    }
  }
}

This is because there are two member structs with the same name, although they sit at different paths. This use case occurs in our systems because some (more top-level modules) export their own Config structs which we then aggregate into one, global config for which we want to generate a schema for.

Is this expected behavior? What can we do to fix this? Would it make sense to extend schemars with support for this use case? It could automatically incorporate, e. g. the parent module name / the module path into the name of the definitions, such that these clashes can be avoided.

support extension attribute

It would be useful to be able to annotate a container with something like #[schemars(extension = "x-foo-bar")] and have those values appear in SchemaObject::extensions. I'd be happy to submit a PR if you'd be interested in it.

Set "default" on generated schemas

Received via email:

Is it possible to get it to output default values for the generated schema, as provide by Default/SmartDefault?

JSON Schema "default" keyword reference: https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.2

Example 1

#[derive(Debug, Default, Deserialize, Serialize, JsonSchema)]
#[serde(default)]
pub struct MyStruct {
    pub my_int: i32,
    pub my_bool: bool,
}

The generated schema for MyStruct should be:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "MyStruct",
  "type": "object",
  "properties": {
    "my_bool": {
      "type": "boolean",
      "default": false
    },
    "my_int": {
      "type": "integer",
      "format": "int32",
      "default": 0
    }
  }
}

Note that we set default on the properties, not on the top-level schema for MyStruct.

Example 2

It gets more complicated when we use #[serde(default)] on fields as well as the struct, e.g.

fn ten_and_true() -> MyStruct2 {
    MyStruct2 {
        my_int: 10,
        my_bool: true,
    }
}

fn six() -> i32 {
    6
}

#[derive(Default, Deserialize, Serialize, JsonSchema, Debug)]
#[serde(default = "ten_and_true")]
pub struct MyStruct2 {
    #[serde(default = "six")]
    pub my_int: i32,
    pub my_bool: bool,
}

The generated schema for MyStruct2 should be:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "MyStruct2",
  "type": "object",
  "properties": {
    "my_bool": {
      "type": "boolean",
      "default": true
    },
    "my_int": {
      "type": "integer",
      "format": "int32",
      "default": 6
    }
  }
}

Example 3

fn ten_and_true() -> MyStruct2 {
    MyStruct2 {
        my_int: 10,
        my_bool: true,
    }
}

fn six() -> i32 {
    6
}

#[derive(Default, Deserialize, Serialize, JsonSchema, Debug)]
#[serde(default)]
pub struct MyStruct {
    pub my_int: i32,
    pub my_bool: bool,
    pub my_struct2: MyStruct2,
}

#[derive(Default, Deserialize, Serialize, JsonSchema, Debug)]
#[serde(default = "ten_and_true")]
pub struct MyStruct2 {
    #[serde(default = "six")]
    pub my_int: i32,
    pub my_bool: bool,
}
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "MyStruct",
  "type": "object",
  "properties": {
    "my_bool": {
      "type": "boolean",
      "default": false
    },
    "my_int": {
      "type": "integer",
      "format": "int32",
      "default": 0
    },
    "my_struct2": {
      "allOf": [{"$ref": "#/definitions/MyStruct2"}],
      "default": {
        "my_bool": false,
        "my_int": 0
      }
    }
  },
  "definitions": {
    "MyStruct2": {
      "type": "object",
      "properties": {
        "my_bool": {
          "type": "boolean",
          "default": true
        },
        "my_int": {
          "type": "integer",
          "format": "int32",
          "default": 6
        }
      }
    }
  }
}

Note that the default for the my_struct2 property has different property defaults (false/0) than the defaults in the MyStruct2 definition (true/6). This is because:

  • Deserializing {} to MyStruct results in MyStruct { my_int: 0, my_bool: false, my_struct2: MyStruct2 { my_int: 0, my_bool: false } }
  • While deserializing {"my_struct2": {}} results in MyStruct { my_int: 0, my_bool: false, my_struct2: MyStruct2 { my_int: 6, my_bool: true } }

Compatibility with Serde_repr

dtolnays Serde_repr allows people to generate more compact json representations of Rust's enums. Currently when schemars generates a schema for an enum annotated with Serde_repr, it generates an incorrect schema.

#[derive(serde_repr::Serialize_repr, schemars::JsonSchema)]
#[repr(u8)]
struct Kind {
    One = 1,
    Two = 2,
    NotThree = 4,
}

The resulting schema looks like:

"Kind": {
  "type": "string",
  "enum": [
    "One",
    "Two",
    "NotThree"
  ]
},

For the person using the documentation this is confusing, since they will read the documentation and expect strings, but they will receive integers. The correct output would be:

"Kind": {
  "type": "string",
  "enum": [
    1,
    2,
    4
  ]
},

`example` for OpenAPI 3.0

Thanks a lot for your amazing work on schemars and okapi!

I am using schemars through rocket_okapi to auto-generate docs for an API that I am writing. For this, I would like to give some meaningful examples for the parameters in my request JSON body, but I seem to have hit a conflict between the JSON Schema Validation specification and the OpenAPI 3.0 specification.

The Schema object in OpenAPI 3.0 expects the example for a Schema object to be placed in an example key containing a single example, as opposed to the examples array of example objects that seems to be currently generated by schemars. OpenAPI 3.0 does not allow examples while JSON Schema Validation doesn't seem to support example.

I have tried to generate an openapi.json using schemars with a documentation like so as advised in the attribute documentation for schemars:

fn schema_example_identifiers() -> Vec<String> {
    vec!["my_id1".to_string(), "my_id2".to_string()]
}

fn schema_example_request() -> EntryRequest {
    EntryRequest {
        ids: schema_example_identifiers(),
    }
}

#[derive(serde::Deserialize, serde::Serialize, JsonSchema)]
#[schemars(example = "schema_example_request")]
struct EntryRequest {
    /// A list of entries from the database
    #[schemars(example = "schema_example_identifiers")]
    ids: Vec<String>,
}

The generated openapi.json does contain "examples" entries for EntryRequest:

"schemas": {
      "EntryRequest": {
        "examples": [
          {
            "ids": [
              "my_id1",
              "my_id2"
            ]
          }
        ],
        "type": "object",
        "required": [
          "ids"
        ],
        "properties": {
          "ids": {
            "description": "A list of entries from the database",
            "examples": [
              [
                "my_id1",
                "my_id2"
              ]
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },

However, the examples don't show up in the Swagger UI. If I copy-paste the openapi.json to Swagger Editor, it tells me that "examples" is not a valid field:

entryrequest_sed

entryrequest

Everything works fine in Swagger Editor / Swagger UI after I rename examples to example and change from an array of example to a single example:

    EntryRequest:
      example:
        ids:
            - my_id1
            - my_id2
      type: object
      required:
        - ids
      properties:
        ids:
          description: A list of entries from the database
          example:
            - my_id1
            - my_id2
          type: array
          items:
            type: string

From trying to understand the schemars and okapi codebases, I can see that examples could additionally get passed into the schema object using the extensions member of okapi::openapi3::Parameter structs, but I have no idea how to do this from within my API.

Could you please advise me how I could get the examples to show up correctly? If you would like to mentor me making a contribution to schemars or okapi to add support for the example field, I would also be interested in helping out.

Support for omitting fields from the schema

I would like schemars to honor serde(skip) and schemars(skip) to be able to omit internal datastructures. Ex:

#[derive(JsonSchema)]
struct Foo {
    #[schemars(skip)]
    bar: String
}

would generate a schema against which the input {} validates.

support #[serde(skip_serializing_if = "path")]

Currently this causes an error like

mismatched types
expected reference &std::option::Option<_>
found reference &<task::Task as schemars::JsonSchema>::json_schema::_SchemarsDefaultSerialize<std::option::Option<std::time::Duration>>

I think it should mark that field as not required.

Thanks in advance.

Stop sorting of schema properties

Currently the schema properties are sorted alphabetical.
This might be desired in some cases. But could you allow an option to use the order of the fields in the structure?

This is handy because my id is now somewhere in the middle and when you have responses with a lot of optional fields this is very confusing. And it does not line up with how the actual response comes back.

Looking at this code (it might not be the right part of the code). You are using a Map that is defined as:

/// The map type used by schemars types.
///
/// Currently a `BTreeMap`, but this may change a different implementation
/// with a similar interface in a future version of schemars.
pub type Map<K, V> = std::collections::BTreeMap<K, V>;

pub properties: Map<String, Schema>,

We can maybe replace that with Vec<(String, Schema)> but then you have to add some searching by key.

The other option is adding some kind of order indicator to Schema. But then Serde Serialize will still not sort the list by that id.

There might also be other options.

So I think the first option is the better one.
I could help with this implementation if you want to.

Documenting enum variants

Currently, doc comments on enum variants are ignored.

/// An enum representing the kind of the thing.
#[derive(schemars::JsonSchema)]
struct Kind {
    /// The kind is one!
    One,
    /// The kind is two!
    Two,
    /// The kind is not three!
    NotThree,
}

The resulting schema looks like:

"Kind": {
  "type": "string",
  "description": "An enum representing the kind of the thing.",
  "enum": [
    "One",
    "Two",
    "NotThree"
  ]
},

I searched the documentation of openapi, but it doesn't seem that they have proper support for documenting enum variants. I therefore suggest that the generated openapi.json should be something along the lines of

"Kind": {
  "type": "string",
  "description": "An enum representing the kind of the thing. \n\n## Variants \nOne: The kind is one! \nTwo: The kind is two! \nNotThree: The kind is not three!",
  "enum": [
    "One",
    "Two",
    "NotThree"
  ]
},

where we just append the variant documentation to the already existing documentation field, seperated by a newline or two and a header.

Enums in Swagger UI

Swagger UI has a bug, ... already for 3 years. Were is handles enum without a typepoorly.
See: swagger-api/swagger-ui#3761
A quick fix for this is adding a type: string item to the schema.

"RegionType": {
  "description": "...",
  "type": "string",   <----Add_this_line
  "enum": [
    "Swamp",
    "Desert",
    "...",
    "Unknown"
  ]
}

Adding the line above will change the output in Swagger UI from:
Screenshot from 2020-05-12 00-24-07
To This:
Screenshot from 2020-05-12 00-24-26

It would be cool if this can be fixed here.
Btw love this crate and the others you made. (I'm also using okapi and rocket_okapi.)

Add optional dependency/handling of url

The https://crates.io/crates/url crate seems to be the common standard through the ecosystem. It would be nice if it could be handled so that we can generate schemas for structs containing URLs.

I would expect this code to compile:

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use url::Url;

#[derive(JsonSchema, Serialize, Deserialize)]
pub struct Foo {
  pub uri: Url
}

though currently it fails with:

   --> src/model/api.rs:104:14
    |
104 |     pub uri: Url,
    |              ^^^ the trait `schemars::JsonSchema` is not implemented for `url::Url`
    |
    = note: required by `schemars::JsonSchema::add_schema_as_property`

Annotation for example values

Adding support for examples.

Currently strings will have an example of:

[
  {
    "list_of_int": [
      0
    ],
    "id": 0,
    "name": "string",
    "full_name": "string"
  }
],

Where strings are always "string" and int is always 0
If we add examples we can give some more info.

Maybe with a syntax like:

#[schemars(example = "This is the example string")]

Used like this:

#[derive(Serialize, Deserialize, JsonSchema)]
pub struct Version {
    #[schemars(example = "1.0.0")]
    pub code: String,
    #[schemars(example = "First release of our crate/application.")]
    pub description: String,
}

This will result in something like:

{
  "code": "1.0.0",
  "description": "First release of our crate/application."
}

I don't know how this can also be done for int, but I think that is less of an issue.
For enums the first value is converted to string and that is used. So that does not have to change I think.

This issue is a repost of: GREsau/okapi#11

#[derive(JsonSchema)] compile errors when fields are dynamically generated

Something like this:

/tmp/b$ cat Cargo.toml

[package]
name = "b"
version = "0.1.0"
authors = ["up9cloud <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
schemars = "0.8"

/tmp/b$ cat src/main.rs

macro_rules! build_struct {
    (
        $id:ident { $($t:tt)* }
    ) => {
        #[derive(Debug, schemars::JsonSchema)]
        pub struct $id {
            x: u8,
            $($t)*
        }
    };
}
build_struct!(A { v: i32, });
fn main() {
   println!("{:?}", A { x: 0, v: 1 });
}

/tmp/b$ cargo run

   Compiling b v0.1.0 (/private/tmp/b)
error[E0425]: cannot find value `gen` in this scope
  --> src/main.rs:12:22
   |
12 | build_struct!(A { v: i32, });
   |                      ^^^ not found in this scope

error[E0425]: cannot find value `schema_object` in this scope
  --> src/main.rs:12:22
   |
12 | build_struct!(A { v: i32, });
   |                      ^^^ not found in this scope

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0425`.
error: could not compile `b`

To learn more, run the command again with --verbose.

If I remove schemars::JsonSchema from derive, it works without errors.

Tag 0.7.0-alpha-2 missing on Github

Is there an unpushed 0.7.0-alpha-2 tag somewhere? It would be great to have the tag for the same commit that was publiched to crates.io. Thanks a lot!

Problem with a declarative macro

I couldn't make JsonSchema derive works with a macro that creates structs and a enum containing these structures. I keep getting an error cannot find value gen in this scope.

I made a gist showing a minimal example where the problem occurs.

If the JsonSchema derive is commented on macro's call, the code works just fine.

Error
error[E0425]: cannot find value `gen` in this scope
  --> src/main.rs:27:24
   |
27 |                   $inner($inner)
   |                          ^^^^^^ not found in this scope
...
36 | / _macro!(
37 | |     #[derive(Serialize, Deserialize, Debug, JsonSchema)]
38 | |     Test {
39 | |         #[derive(Serialize, Deserialize, Debug, JsonSchema)]
...  |
42 | |
43 | | );
   | |__- in this macro invocation
   |
   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

Generated schema seems to be invalid

When I try to use the generated schema to validate data using Ajv I'm getting the following error:

Error: unknown format "uint32" is used in schema at path [...]

The error seems to be related to the integer format attribute. I couldn't find any mention of uint32, uint or anything of sorts in the JsonSchema spec.

Here is an example of the generated schema:

"portal": {
          "type": "integer",
          "format": "uint32",
          "minimum": 0.0
        },

Am I doing something wrong here or is this a bug?

Implement JsonSchema for more standard library types

If Serde implements Serialize/Deserialize on it, then ideally Schemars should implement JsonSchema on it.

  • Result<T,E>
  • Duration
  • SystemTime
  • Ipaddr, Ipv4Addr, Ipv6Addr
  • SocketAddr, SocketAddrV4, SocketAddrV6
  • Path, PathBuf
  • OsStr, OsString
  • Wrapping
  • Reverse
  • AtomicBool
  • AtomicI8, AtomicU8 etc.
  • NonZeroI8, NonZeroU8 etc.
  • rc::Weak, sync::Weak
  • Bound
  • Range, RangeInclusive
  • CStr, CString
  • fmt::Arguments

Is there a way to customize documentation for derive?

First of all, I want to say thank you for this fantastic library!

The Rust types I'm using with schemars already have lots of doc comments. I'd like schemas, via derive(JsonSchema), to skip those doc comments (they are too long). Ideally, I'd like to configure my own comments for just json schema generation. Is this possible?

Thanks!

how to generate json schema from serde_json::value::Value?

i want to generate json schema from dynamic json value.
i don't know whether schemars support this function.

    let data = r#"
        {
            "name": "John Doe",
            "age": 43,
            "phones": [
                "+44 1234567",
                "+44 2345678"
            ]
        }"#;
    let v: Value = serde_json::from_str(data)? ;

Support generating multiple schemas (for serialize vs deserialize) from the same struct

I have a lot of types whose deserialization behavior allows for more types than the serialization does. For example:

// Deserialize is implemented such that any non-string JSON object is stringified
#[derive(Serialize)]
struct LenientString(String);

#[derive(JsonSchema, Serialize, Deserialize)]
struct Foo {
    bar: LenientString
}

Therefore I would like to generate two schemas per type: A schema describing the input that is allowed, and a schema that describes the output that is guaranteed.

Is this kind of feature something you'd be interested in merging? I am on the fence as to whether this is sensible. In some languages I would've created separate input and output types, but as serde allowed me to do everything with one type this is what I ended up with. But I think that adding this feature to schemars is easier than refactoring my existing codebase.

Option<bool> gets required in schema

Hello, and thank you for a great project!

I suspect I've encountered a bug (or I'm holding it wrong), but for some reason optional bools are set as required in definitions.

The version I'm using is 0.7.0-alpha-1

struct MyStruct {
  pub foo: Option<bool>
}

pub fn get_schema() -> String {
    let settings = schemars::gen::SchemaSettings::draft07().with(|s| {
        s.option_nullable = false;
        s.option_add_null_type = false;
    });
    let gen = settings.into_generator();
    let schema = gen.into_root_schema_for::<config::MyStruct>();
    serde_json::to_string_pretty(&schema).unwrap()
}

Generates the following schema:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "MyStruct",
  "type": "object",
  "required": [
    "foo"
  ],
  "properties": {
    "foo": {
      "type": "boolean"
    }
  }
}

If I add #[serde(default)] to foo it removes the requirement.

Would it make sense to add an option to SchemaSettings that says "Don't make Options required". I realize that the logic for this probably is more complicated since we have to take option_nullable and option_add_null_type into account, but having some kind of sensible default for this would make adoption easier.

Again, thanks for your great work. Much appreciated!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.