Migrating from 0.8 to 1.0 (or to 0.9)

Schemars 1.0 is still under development, and further changes may be introduced.

Optional dependencies

All optional dependencies are now suffixed by their version:

  • chrono is now chrono04
  • either is now either1
  • smallvec is now smallvec1
  • url is now url2
  • bytes is now bytes1
  • rust_decimal is now rust_decimal1
  • smol_str is now smol_str02
  • semver is now semver1
  • enumset, indexmap, uuid08, arrayvec05 and bigdecimal03 have been removed
  • indexmap2, arrayvec07 and bigdecimal04 are unchanged

Schema is now a wrapper around serde_json::Value

Schema is now defined as a wrapper around a serde_json::Value (which must be a Value::Bool or Value::Object), rather than a struct with a field for each JSON schema keyword (with some intermediary types). Schema is now available as schemars::Schema instead of schemars::schema::Schema, and all other types that were in the schemars::schema module have now been removed. Functions that previously returned a RootSchema now just return a Schema.

A new macro json_schema!(...) is available to easily create new instances of Schema, which functions similarly to the serde_json::json!(...) macro.

Here's how you might create and modify a Schema in schemars v0.8:

use schemars::schema::{InstanceType, ObjectValidation, Schema, SchemaObject};
use schemars::Map;

// Create a Schema for an object with property `foo`
let schema_object = SchemaObject {
    instance_type: Some(InstanceType::Object.into()),
    object: Some(Box::new(ObjectValidation {
        properties: Map::from_iter([("foo".to_owned(), true.into())]),
        ..Default::default()
    })),
    ..Default::default()
};
let schema: Schema = schema_object.into();

// Make the `foo` property required
let mut schema_object = schema.into_object();
let obj = schema_object.object();
obj.required.insert("foo".to_owned());

And the same thing in v1.0:

use schemars::{json_schema, Schema};

// Create a Schema for an object with property `foo`
let mut schema: Schema = json_schema!({
    "type": "object",
    "properties": {
        "foo": true
    }
});

// Make the `foo` property required
schema
    .ensure_object()
    .entry("required")
    .or_insert(serde_json::Value::Array(Vec::new()))
    .as_array_mut()
    .expect("`required` should be an array")
    .push("foo".into());

visit::Visitor replaced with transform::Transform

The visit module and Visitor trait have been replace with transform and Transform respectively. Accordingly, these items have been renamed:

  • SchemaSettings::visitors -> SchemaSettings::transforms
  • SchemaSettings::with_visitor -> SchemaSettings::with_transform
  • SchemaGenerator::visitors_mut -> SchemaGenerator::transforms_mut
  • GenVisitor -> GenTransform
  • Visitor::visit_schema -> Transform::transform
    • visit_schema_object and visit_root_schema methods have been removed
  • visit::visit_schema -> transform::transform_subschemas
    • visit_schema_object and visit_root_schema functions have been removed

So if you had defined this Visitor in schemars 0.8:

use schemars::schema::SchemaObject;
use schemars::visit::{visit_schema_object, Visitor};

pub struct MyVisitor;

impl Visitor for MyVisitor {
    fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
        // First, make our change to this schema
        schema
            .extensions
            .insert("my_property".to_string(), serde_json::json!("hello world"));

        // Then delegate to default implementation to visit any subschemas
        visit_schema_object(self, schema);
    }
}

let mut schema = schemars::schema_for!(str);
MyVisitor.visit_root_schema(&mut schema);

Then the equivalent Transform in schemars 1.0 would be:

use schemars::transform::{transform_subschemas, Transform};
use schemars::Schema;

pub struct MyTransform;

impl Transform for MyTransform {
    fn transform(&mut self, schema: &mut Schema) {
        // First, make our change to this schema
        schema.insert("my_property".to_string(), serde_json::json!("hello world"));

        // Then apply the transform to any subschemas
        transform_subschemas(self, schema);
    }
}

let mut schema = schemars::schema_for!(str);
MyTransform.transform(&mut schema);

Also, since Transform is now implemented for functions that take a single &mut Schema argument, you could also define it as a function instead of a struct:

fn my_transform(schema: &mut Schema) {
    // First, make our change to this schema
    schema.insert("my_property".to_string(), serde_json::json!("hello world"));

    // Then apply the transform to any subschemas
    transform_subschemas(&mut my_transform, schema);
}

let mut schema = schemars::schema_for!(str);
my_transform(&mut schema);
// Or equivalently:
// my_transform.transform(&mut schema);

Finally, you can also use the RecursiveTransform newtype to convert a non-recursive Transform (i.e. one that does not transform subschemas) into a recursive one, like so:

fn my_transform2(schema: &mut Schema) {
    schema.insert("my_property".to_string(), serde_json::json!("hello world"));
}

let mut schema = schemars::schema_for!(str);
RecursiveTransform(my_transform2).transform(&mut schema);

Changes to #[validate(...)] attributes

Since adding support for #[validate(...)] attributes, the Validator crate has made several changes to its supported attributes. Accordingly, Schemars 1.0 has updated its handling of #[validate(...)] attributes to match the latest version (currently 0.18.1) of the Validator crate - this removes some attributes, and changes the syntax of others:

  • The #[validate(phone)]/#[schemars(phone)] attribute is removed. If you want the old behaviour of setting the "format" property on the generated schema, you can use #[schemars(extend("format = "phone"))] instead.

  • The #[validate(required_nested)]/#[schemars(required_nested)] attribute is removed. If you want the old behaviour, you can use #[schemars(required)] instead.

  • The #[validate(regex = "...")]/#[schemars(regex = "...")] attribute can no longer use name = "value" syntax. Instead, you can use:

    • #[validate(regex(path = ...)]
    • #[schemars(regex(pattern = ...)]
    • #[schemars(pattern(...)] (Garde-style)
  • Similarly, the #[validate(contains = "...")]/#[schemars(contains = "...")] attribute can no longer use name = "value" syntax. Instead, you can use:

    • #[validate(contains(pattern = ...))]
    • #[schemars(contains(pattern = ...))]
    • #[schemars(contains(...))] (Garde-style)

As an alternative option, Schemars 1.0 also adds support for #[garde(...)] attributes used with the Garde crate, along with equivalent #[schemars(...)] attributes. See the documentation for a list of all supported attributes.