Migrating from 0.8 to 1.0

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.