Repository: GREsau/schemars
Pull request #289 (2024-05-11 19:52:14)

Define `Schema` as a newtype around `serde_json::Value`
Implements #242

Still to do (not necessarily all as part of this PR):

- ~Fix `JsonSchema` implementations for types in third-party crates~
- Remove now-unnecessary `preserve_order` feature flag
- Where we currently use a single-valued `enum`, instead use `const` (check which schema versions support `const` - may require a new visitor to change `const` to `enum`)
- Fix deriving `JsonSchema` on large types, ensure compile times are OK
- Reduce usages of `json!`/`json_schema!` for better performance, particularly in schemars_derive
- Review doc comments
- Update doctests (causing CI to fail)


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d37634e..7c72306 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,7 +17,7 @@ jobs:
           - nightly
         include:
           - rust: 1.60.0
-            test_features: "--features impl_json_schema"
+            test_features: ""
             allow_failure: false
           - rust: stable
             test_features: "--all-features"
diff --git a/Cargo.lock b/Cargo.lock
index 516f979..757013a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,12 +2,6 @@
 # It is not intended for manual editing.
 version = 3
 
-[[package]]
-name = "arrayvec"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
-
 [[package]]
 name = "arrayvec"
 version = "0.7.4"
@@ -29,17 +23,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "bigdecimal"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa"
-dependencies = [
- "num-bigint",
- "num-integer",
- "num-traits",
-]
-
 [[package]]
 name = "bigdecimal"
 version = "0.4.2"
@@ -168,12 +151,6 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
-[[package]]
-name = "hashbrown"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
 [[package]]
 name = "hashbrown"
 version = "0.14.2"
@@ -196,17 +173,6 @@ dependencies = [
  "unicode-normalization",
 ]
 
-[[package]]
-name = "indexmap"
-version = "1.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
-dependencies = [
- "autocfg",
- "hashbrown 0.12.3",
- "serde",
-]
-
 [[package]]
 name = "indexmap"
 version = "2.0.2"
@@ -214,8 +180,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
 dependencies = [
  "equivalent",
- "hashbrown 0.14.2",
- "serde",
+ "hashbrown",
 ]
 
 [[package]]
@@ -284,29 +249,49 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.69"
+version = "1.0.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.33"
+version = "1.0.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
 dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "ref-cast"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f"
+dependencies = [
+ "ref-cast-impl",
+]
+
+[[package]]
+name = "ref-cast-impl"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "rust_decimal"
 version = "1.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd"
 dependencies = [
- "arrayvec 0.7.4",
+ "arrayvec",
  "num-traits",
 ]
 
@@ -320,18 +305,16 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
 name = "schemars"
 version = "0.8.19"
 dependencies = [
- "arrayvec 0.5.2",
- "arrayvec 0.7.4",
- "bigdecimal 0.3.1",
- "bigdecimal 0.4.2",
+ "arrayvec",
+ "bigdecimal",
  "bytes",
  "chrono",
  "dyn-clone",
  "either",
  "enumset",
- "indexmap 1.9.3",
- "indexmap 2.0.2",
+ "indexmap",
  "pretty_assertions",
+ "ref-cast",
  "rust_decimal",
  "schemars_derive",
  "semver",
@@ -341,8 +324,7 @@ dependencies = [
  "smol_str",
  "trybuild",
  "url",
- "uuid 0.8.2",
- "uuid 1.5.0",
+ "uuid",
 ]
 
 [[package]]
@@ -361,9 +343,6 @@ name = "semver"
 version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
-dependencies = [
- "serde",
-]
 
 [[package]]
 name = "serde"
@@ -415,18 +394,15 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
 
 [[package]]
 name = "smol_str"
-version = "0.1.24"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fad6c857cbab2627dcf01ec85a623ca4e7dcb5691cbaa3d7fb7653671f0d09c9"
-dependencies = [
- "serde",
-]
+checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49"
 
 [[package]]
 name = "syn"
-version = "2.0.38"
+version = "2.0.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
+checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -504,12 +480,6 @@ dependencies = [
  "percent-encoding",
 ]
 
-[[package]]
-name = "uuid"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
-
 [[package]]
 name = "uuid"
 version = "1.5.0"
diff --git a/schemars/Cargo.toml b/schemars/Cargo.toml
index 579e8c0..47dc643 100644
--- a/schemars/Cargo.toml
+++ b/schemars/Cargo.toml
@@ -15,117 +15,91 @@ rust-version = "1.60"
 
 [dependencies]
 schemars_derive = { version = "=0.8.19", optional = true, path = "../schemars_derive" }
-serde = { version = "1.0", features = ["derive"] }
+serde = "1.0"
 serde_json = "1.0.25"
 dyn-clone = "1.0"
+ref-cast = "1.0.22"
 
-chrono = { version = "0.4", default-features = false, optional = true }
-indexmap = { version = "1.2", features = ["serde-1"], optional = true }
-indexmap2 = { version = "2.0", features = ["serde"], optional = true, package = "indexmap" }
-either = { version = "1.3", default-features = false, optional = true }
-uuid08 = { version = "0.8", default-features = false, optional = true, package = "uuid" }
+# optional dependencies
+chrono04 = { version = "0.4", default-features = false, optional = true, package = "chrono" }
+indexmap2 = { version = "2.0", default-features = false, optional = true, package = "indexmap" }
+either1 = { version = "1.3", default-features = false, optional = true,  package = "either" }
 uuid1 = { version = "1.0", default-features = false, optional = true, package = "uuid" }
-smallvec = { version = "1.0", optional = true }
-arrayvec05 = { version = "0.5", default-features = false, optional = true, package = "arrayvec" }
+smallvec1 = { version = "1.0", default-features = false, optional = true, package = "smallvec" }
 arrayvec07 = { version = "0.7", default-features = false, optional = true, package = "arrayvec" }
-url = { version = "2.0", default-features = false, optional = true }
-bytes = { version = "1.0", optional = true }
-rust_decimal = { version = "1", default-features = false, optional = true }
-bigdecimal03 = { version = "0.3", default-features = false, optional = true, package = "bigdecimal" }
+url2 = { version = "2.0", default-features = false, optional = true, package = "url" }
+bytes1 = { version = "1.0", default-features = false, optional = true, package = "bytes" }
+rust_decimal1 = { version = "1", default-features = false, optional = true, package = "rust_decimal"}
 bigdecimal04 = { version = "0.4", default-features = false, optional = true, package = "bigdecimal" }
-enumset = { version = "1.0", optional = true }
-smol_str = { version = "0.1.17", optional = true }
-semver = { version = "1.0.9", features = ["serde"], optional = true }
+enumset1 = { version = "1.0", default-features = false, optional = true, package = "enumset" }
+smol_str02 = { version = "0.2.1", default-features = false, optional = true, package = "smol_str" }
+semver1 = { version = "1.0.9", default-features = false, optional = true, package = "semver" }
 
 [dev-dependencies]
 pretty_assertions = "1.2.1"
 trybuild = "1.0"
+serde = { version = "1.0", features = ["derive"] }
 
 [features]
 default = ["derive"]
 
 derive = ["schemars_derive"]
 
-# Use a different representation for the map type of Schemars.
-# This allows data to be read into a Value and written back to a JSON string
-# while preserving the order of map keys in the input.
-preserve_order = ["indexmap"]
-
-impl_json_schema = ["derive"]
-# derive_json_schema will be removed in a later version
-derive_json_schema = ["impl_json_schema"]
-
-# `uuid` feature contains `uuid08` only for back-compat - will be changed to include uuid 1.0 instead in a later version
-uuid = ["uuid08"]
-# `arrayvec` feature without version suffix is included only for back-compat - will be removed in a later version
-arrayvec = ["arrayvec05"]
-indexmap1 = ["indexmap"]
-
 raw_value = ["serde_json/raw_value"]
-# `bigdecimal` feature without version suffix is included only for back-compat - will be removed in a later version
-bigdecimal = ["bigdecimal03"]
 
 ui_test = []
 
 [[test]]
-name = "chrono"
-required-features = ["chrono"]
+name = "ui"
+required-features = ["ui_test"]
 
 [[test]]
-name = "indexmap"
-required-features = ["indexmap"]
+name = "chrono"
+required-features = ["chrono04"]
 
 [[test]]
-name = "indexmap2"
+name = "indexmap"
 required-features = ["indexmap2"]
 
 [[test]]
 name = "either"
-required-features = ["either"]
+required-features = ["either1"]
 
 [[test]]
 name = "uuid"
-required-features = ["uuid08", "uuid1"]
+required-features = ["uuid1"]
 
 [[test]]
 name = "smallvec"
-required-features = ["smallvec"]
+required-features = ["smallvec1"]
 
 [[test]]
 name = "bytes"
-required-features = ["bytes"]
+required-features = ["bytes1"]
 
 [[test]]
 name = "arrayvec"
-required-features = ["arrayvec05", "arrayvec07"]
-
-[[test]]
-name = "schema_for_schema"
-required-features = ["impl_json_schema"]
-
-[[test]]
-name = "ui"
-required-features = ["ui_test"]
+required-features = ["arrayvec07"]
 
 [[test]]
 name = "url"
-required-features = ["url"]
+required-features = ["url2"]
 
 [[test]]
 name = "enumset"
-required-features = ["enumset"]
+required-features = ["enumset1"]
 
 [[test]]
 name = "smol_str"
-required-features = ["smol_str"]
+required-features = ["smol_str02"]
 
 [[test]]
 name = "semver"
-required-features = ["semver"]
+required-features = ["semver1"]
 
 [[test]]
 name = "decimal"
-required-features = ["rust_decimal", "bigdecimal03", "bigdecimal04"]
+required-features = ["rust_decimal1", "bigdecimal04"]
 
 [package.metadata.docs.rs]
 all-features = true
diff --git a/schemars/examples/custom_serialization.rs b/schemars/examples/custom_serialization.rs
index 53c78fa..c119ea5 100644
--- a/schemars/examples/custom_serialization.rs
+++ b/schemars/examples/custom_serialization.rs
@@ -1,4 +1,4 @@
-use schemars::schema::{Schema, SchemaObject};
+use schemars::Schema;
 use schemars::{gen::SchemaGenerator, schema_for, JsonSchema};
 use serde::{Deserialize, Serialize};
 
@@ -21,9 +21,11 @@ pub struct MyStruct {
 }
 
 fn make_custom_schema(gen: &mut SchemaGenerator) -> Schema {
-    let mut schema: SchemaObject = <String>::json_schema(gen).into();
-    schema.format = Some("boolean".to_owned());
-    schema.into()
+    let mut schema = String::json_schema(gen);
+    schema
+        .ensure_object()
+        .insert("format".into(), "boolean".into());
+    schema
 }
 
 fn eight() -> i32 {
diff --git a/schemars/src/_private.rs b/schemars/src/_private.rs
index c61ffc1..d4ac4a7 100644
--- a/schemars/src/_private.rs
+++ b/schemars/src/_private.rs
@@ -1,7 +1,8 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::{InstanceType, ObjectValidation, Schema, SchemaObject};
-use crate::{JsonSchema, Map, Set};
+use crate::JsonSchema;
+use crate::Schema;
 use serde::Serialize;
+use serde_json::Map;
 use serde_json::Value;
 
 // Helper for generating schemas for flattened `Option` fields.
@@ -12,12 +13,8 @@ pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
     let mut schema = T::_schemars_private_non_optional_json_schema(gen);
 
     if T::_schemars_private_is_option() && !required {
-        if let Schema::Object(SchemaObject {
-            object: Some(ref mut object_validation),
-            ..
-        }) = schema
-        {
-            object_validation.required.clear();
+        if let Some(object) = schema.as_object_mut() {
+            object.remove("required");
         }
     }
 
@@ -57,38 +54,22 @@ impl<T: Serialize> MaybeSerializeWrapper<T> {
 
 /// Create a schema for a unit enum
 pub fn new_unit_enum(variant: &str) -> Schema {
-    Schema::Object(SchemaObject {
-        instance_type: Some(InstanceType::String.into()),
-        enum_values: Some(vec![variant.into()]),
-        ..SchemaObject::default()
+    // TODO switch from single-valued "enum" to "const"
+    json_schema!({
+        "type": "string",
+        "enum": [variant],
     })
 }
 
 /// Create a schema for an externally tagged enum
 pub fn new_externally_tagged_enum(variant: &str, sub_schema: Schema) -> Schema {
-    Schema::Object(SchemaObject {
-        instance_type: Some(InstanceType::Object.into()),
-        object: Some(Box::new(ObjectValidation {
-            properties: {
-                let mut props = Map::new();
-                props.insert(variant.to_owned(), sub_schema);
-                props
-            },
-            required: {
-                let mut required = Set::new();
-                required.insert(variant.to_owned());
-                required
-            },
-            // Externally tagged variants must prohibit additional
-            // properties irrespective of the disposition of
-            // `deny_unknown_fields`. If additional properties were allowed
-            // one could easily construct an object that validated against
-            // multiple variants since here it's the properties rather than
-            // the values of a property that distingish between variants.
-            additional_properties: Some(Box::new(false.into())),
-            ..Default::default()
-        })),
-        ..SchemaObject::default()
+    json_schema!({
+        "type": "object",
+        "properties": {
+            variant: sub_schema
+        },
+        "required": [variant],
+        "additionalProperties": false,
     })
 }
 
@@ -98,74 +79,87 @@ pub fn new_internally_tagged_enum(
     variant: &str,
     deny_unknown_fields: bool,
 ) -> Schema {
-    let tag_schema = Schema::Object(SchemaObject {
-        instance_type: Some(InstanceType::String.into()),
-        enum_values: Some(vec![variant.into()]),
-        ..Default::default()
+    // TODO switch from single-valued "enum" to "const"
+    let mut schema = json_schema!({
+        "type": "object",
+        "properties": {
+            tag_name: {
+                "type": "string",
+                "enum": [variant],
+            }
+        },
+        "required": [tag_name],
     });
-    Schema::Object(SchemaObject {
-        instance_type: Some(InstanceType::Object.into()),
-        object: Some(Box::new(ObjectValidation {
-            properties: {
-                let mut props = Map::new();
-                props.insert(tag_name.to_owned(), tag_schema);
-                props
-            },
-            required: {
-                let mut required = Set::new();
-                required.insert(tag_name.to_owned());
-                required
-            },
-            additional_properties: deny_unknown_fields.then(|| Box::new(false.into())),
-            ..Default::default()
-        })),
-        ..SchemaObject::default()
-    })
+
+    if deny_unknown_fields {
+        schema
+            .as_object_mut()
+            .unwrap()
+            .insert("additionalProperties".into(), false.into());
+    }
+
+    schema
 }
 
 pub fn insert_object_property<T: ?Sized + JsonSchema>(
-    obj: &mut ObjectValidation,
+    schema: &mut Schema,
     key: &str,
     has_default: bool,
     required: bool,
-    schema: Schema,
+    sub_schema: Schema,
 ) {
-    obj.properties.insert(key.to_owned(), schema);
+    let obj = schema.ensure_object();
+    if let Some(properties) = obj
+        .entry("properties")
+        .or_insert(Value::Object(Map::new()))
+        .as_object_mut()
+    {
+        properties.insert(key.to_owned(), sub_schema.into());
+    }
+
     if required || !(has_default || T::_schemars_private_is_option()) {
-        obj.required.insert(key.to_owned());
+        if let Some(req) = obj
+            .entry("required")
+            .or_insert(Value::Array(Vec::new()))
+            .as_array_mut()
+        {
+            req.push(key.into());
+        }
     }
 }
 
-pub mod metadata {
-    use crate::Schema;
-    use serde_json::Value;
-
-    macro_rules! add_metadata_fn {
-        ($method:ident, $name:ident, $ty:ty) => {
-            pub fn $method(schema: Schema, $name: impl Into<$ty>) -> Schema {
-                let value = $name.into();
-                if value == <$ty>::default() {
-                    schema
-                } else {
-                    let mut schema_obj = schema.into_object();
-                    schema_obj.metadata().$name = value.into();
-                    Schema::Object(schema_obj)
-                }
+pub fn insert_validation_property(
+    schema: &mut Schema,
+    required_type: &str,
+    key: &str,
+    value: impl Into<Value>,
+) {
+    if schema.has_type(required_type) || (required_type == "number" && schema.has_type("integer")) {
+        schema.ensure_object().insert(key.to_owned(), value.into());
+    }
+}
+
+pub fn append_required(schema: &mut Schema, key: &str) {
+    if schema.has_type("object") {
+        if let Value::Array(array) = schema
+            .ensure_object()
+            .entry("required")
+            .or_insert(Value::Array(Vec::new()))
+        {
+            let value = Value::from(key);
+            if !array.contains(&value) {
+                array.push(value);
             }
-        };
+        }
     }
+}
 
-    add_metadata_fn!(add_description, description, String);
-    add_metadata_fn!(add_id, id, String);
-    add_metadata_fn!(add_title, title, String);
-    add_metadata_fn!(add_deprecated, deprecated, bool);
-    add_metadata_fn!(add_read_only, read_only, bool);
-    add_metadata_fn!(add_write_only, write_only, bool);
-    add_metadata_fn!(add_default, default, Value);
-
-    pub fn add_examples<I: IntoIterator<Item = Value>>(schema: Schema, examples: I) -> Schema {
-        let mut schema_obj = schema.into_object();
-        schema_obj.metadata().examples.extend(examples);
-        Schema::Object(schema_obj)
+pub fn apply_inner_validation(schema: &mut Schema, f: fn(&mut Schema) -> ()) {
+    if let Some(inner_schema) = schema
+        .as_object_mut()
+        .and_then(|o| o.get_mut("items"))
+        .and_then(|i| <&mut Schema>::try_from(i).ok())
+    {
+        f(inner_schema);
     }
 }
diff --git a/schemars/src/flatten.rs b/schemars/src/flatten.rs
index e55cb7f..fc66b74 100644
--- a/schemars/src/flatten.rs
+++ b/schemars/src/flatten.rs
@@ -1,180 +1,111 @@
-use crate::schema::*;
-use crate::{Map, Set};
+use serde_json::map::Entry;
+use serde_json::Value;
+
+use crate::Schema;
 
 impl Schema {
     /// This function is only public for use by schemars_derive.
     ///
     /// It should not be considered part of the public API.
     #[doc(hidden)]
-    pub fn flatten(self, other: Self) -> Schema {
-        if is_null_type(&self) {
-            return other;
-        } else if is_null_type(&other) {
+    pub fn flatten(mut self, other: Self) -> Schema {
+        // This special null-type-schema handling is here for backward-compatibility, but needs reviewing.
+        // I think it's only needed to make internally-tagged enum unit variants behave correctly, but that
+        // should be handled entirely within schemars_derive.
+        if other
+            .as_object()
+            .and_then(|o| o.get("type"))
+            .and_then(|t| t.as_str())
+            == Some("null")
+        {
             return self;
         }
-        let s1: SchemaObject = self.into();
-        let s2: SchemaObject = other.into();
-        Schema::Object(s1.merge(s2))
-    }
-}
 
-pub(crate) trait Merge: Sized {
-    fn merge(self, other: Self) -> Self;
-}
-
-macro_rules! impl_merge {
-    ($ty:ident { merge: $($merge_field:ident)*, or: $($or_field:ident)*, }) => {
-        impl Merge for $ty {
-            fn merge(self, other: Self) -> Self {
-                $ty {
-                    $($merge_field: self.$merge_field.merge(other.$merge_field),)*
-                    $($or_field: self.$or_field.or(other.$or_field),)*
+        if let Value::Object(mut obj2) = other.to_value() {
+            let obj1 = self.ensure_object();
+
+            let ap2 = obj2.remove("additionalProperties");
+            if let Entry::Occupied(mut ap1) = obj1.entry("additionalProperties") {
+                match ap2 {
+                    Some(ap2) => {
+                        flatten_additional_properties(ap1.get_mut(), ap2);
+                    }
+                    None => {
+                        ap1.remove();
+                    }
                 }
             }
-        }
-    };
-    ($ty:ident { or: $($or_field:ident)*, }) => {
-        impl_merge!( $ty { merge: , or: $($or_field)*, });
-    };
-}
-
-// For ObjectValidation::additional_properties.
-impl Merge for Option<Box<Schema>> {
-    fn merge(self, other: Self) -> Self {
-        match (self.map(|x| *x), other.map(|x| *x)) {
-            // Perfer permissive schemas.
-            (Some(Schema::Bool(true)), _) => Some(Box::new(true.into())),
-            (_, Some(Schema::Bool(true))) => Some(Box::new(true.into())),
-            (None, _) => None,
-            (_, None) => None,
 
-            // Merge if we have two non-trivial schemas.
-            (Some(Schema::Object(s1)), Some(Schema::Object(s2))) => {
-                Some(Box::new(Schema::Object(s1.merge(s2))))
+            for (key, value2) in obj2 {
+                match obj1.entry(key) {
+                    Entry::Vacant(vacant) => {
+                        vacant.insert(value2);
+                    }
+                    Entry::Occupied(mut occupied) => {
+                        match occupied.key().as_str() {
+                            // This special "type" handling can probably be removed once the enum variant `with`/`schema_with` behaviour is fixed
+                            "type" => match (occupied.get_mut(), value2) {
+                                (Value::Array(a1), Value::Array(mut a2)) => {
+                                    a2.retain(|v2| !a1.contains(v2));
+                                    a1.extend(a2);
+                                }
+                                (v1, Value::Array(mut a2)) => {
+                                    if !a2.contains(v1) {
+                                        a2.push(std::mem::take(v1));
+                                        *occupied.get_mut() = Value::Array(a2);
+                                    }
+                                }
+                                (Value::Array(a1), v2) => {
+                                    if !a1.contains(&v2) {
+                                        a1.push(v2);
+                                    }
+                                }
+                                (v1, v2) => {
+                                    if v1 != &v2 {
+                                        *occupied.get_mut() =
+                                            Value::Array(vec![std::mem::take(v1), v2]);
+                                    }
+                                }
+                            },
+                            "required" => {
+                                if let Value::Array(a1) = occupied.into_mut() {
+                                    if let Value::Array(a2) = value2 {
+                                        a1.extend(a2);
+                                    }
+                                }
+                            }
+                            "properties" | "patternProperties" => {
+                                if let Value::Object(o1) = occupied.into_mut() {
+                                    if let Value::Object(o2) = value2 {
+                                        o1.extend(o2);
+                                    }
+                                }
+                            }
+                            _ => {
+                                // leave the original value as it is (don't modify `self`)
+                            }
+                        };
+                    }
+                }
             }
-
-            // Perfer the more permissive schema.
-            (Some(s1 @ Schema::Object(_)), Some(Schema::Bool(false))) => Some(Box::new(s1)),
-            (Some(Schema::Bool(false)), Some(s2 @ Schema::Object(_))) => Some(Box::new(s2)),
-
-            // Default to the null schema.
-            (Some(Schema::Bool(false)), Some(Schema::Bool(false))) => Some(Box::new(false.into())),
-        }
-    }
-}
-
-impl_merge!(SchemaObject {
-    merge: extensions instance_type enum_values
-        metadata subschemas number string array object,
-    or: format const_value reference,
-});
-
-impl Merge for Metadata {
-    fn merge(self, other: Self) -> Self {
-        Metadata {
-            id: self.id.or(other.id),
-            title: self.title.or(other.title),
-            description: self.description.or(other.description),
-            default: self.default.or(other.default),
-            deprecated: self.deprecated || other.deprecated,
-            read_only: self.read_only || other.read_only,
-            write_only: self.write_only || other.write_only,
-            examples: self.examples.merge(other.examples),
-        }
-    }
-}
-
-impl_merge!(SubschemaValidation {
-    or: all_of any_of one_of not if_schema then_schema else_schema,
-});
-
-impl_merge!(NumberValidation {
-    or: multiple_of maximum exclusive_maximum minimum exclusive_minimum,
-});
-
-impl_merge!(StringValidation {
-    or: max_length min_length pattern,
-});
-
-impl_merge!(ArrayValidation {
-    or: items additional_items max_items min_items unique_items contains,
-});
-
-impl_merge!(ObjectValidation {
-    merge: required properties pattern_properties additional_properties,
-    or: max_properties min_properties property_names,
-});
-
-impl<T: Merge> Merge for Option<T> {
-    fn merge(self, other: Self) -> Self {
-        match (self, other) {
-            (Some(x), Some(y)) => Some(x.merge(y)),
-            (None, y) => y,
-            (x, None) => x,
         }
-    }
-}
-
-impl<T: Merge> Merge for Box<T> {
-    fn merge(mut self, other: Self) -> Self {
-        *self = (*self).merge(*other);
-        self
-    }
-}
-
-impl<T> Merge for Vec<T> {
-    fn merge(mut self, other: Self) -> Self {
-        self.extend(other);
-        self
-    }
-}
 
-impl<K, V> Merge for Map<K, V>
-where
-    K: std::hash::Hash + Eq + Ord,
-{
-    fn merge(mut self, other: Self) -> Self {
-        self.extend(other);
         self
     }
 }
 
-impl<T: Ord> Merge for Set<T> {
-    fn merge(mut self, other: Self) -> Self {
-        self.extend(other);
-        self
-    }
-}
-
-impl Merge for SingleOrVec<InstanceType> {
-    fn merge(self, other: Self) -> Self {
-        if self == other {
-            return self;
+// TODO validate behaviour when flattening a normal struct into a struct with deny_unknown_fields
+fn flatten_additional_properties(v1: &mut Value, v2: Value) {
+    match (v1, v2) {
+        (v1, Value::Bool(true)) => {
+            *v1 = Value::Bool(true);
         }
-        let mut vec = match (self, other) {
-            (SingleOrVec::Vec(v1), SingleOrVec::Vec(v2)) => v1.merge(v2),
-            (SingleOrVec::Vec(mut v), SingleOrVec::Single(s))
-            | (SingleOrVec::Single(s), SingleOrVec::Vec(mut v)) => {
-                v.push(*s);
-                v
-            }
-            (SingleOrVec::Single(s1), SingleOrVec::Single(s2)) => vec![*s1, *s2],
-        };
-        vec.sort();
-        vec.dedup();
-        SingleOrVec::Vec(vec)
+        (v1 @ Value::Bool(false), v2) => {
+            *v1 = v2;
+        }
+        (Value::Object(o1), Value::Object(o2)) => {
+            o1.extend(o2);
+        }
+        _ => {}
     }
 }
-
-fn is_null_type(schema: &Schema) -> bool {
-    let s = match schema {
-        Schema::Object(s) => s,
-        _ => return false,
-    };
-    let instance_type = match &s.instance_type {
-        Some(SingleOrVec::Single(t)) => t,
-        _ => return false,
-    };
-
-    **instance_type == InstanceType::Null
-}
diff --git a/schemars/src/gen.rs b/schemars/src/gen.rs
index a31839a..d19f80b 100644
--- a/schemars/src/gen.rs
+++ b/schemars/src/gen.rs
@@ -7,12 +7,12 @@ There are two main types in this module:
 * [`SchemaGenerator`], which manages the generation of a schema document.
 */
 
-use crate::schema::*;
-use crate::{visit::*, JsonSchema, Map};
+use crate::Schema;
+use crate::{visit::*, JsonSchema};
 use dyn_clone::DynClone;
 use serde::Serialize;
 use std::borrow::Cow;
-use std::collections::HashMap;
+use std::collections::{BTreeMap, HashMap};
 use std::{any::Any, collections::HashSet, fmt::Debug};
 
 /// Settings to customize how Schemas are generated.
@@ -76,7 +76,7 @@ impl SchemaSettings {
             option_add_null_type: true,
             definitions_path: "#/definitions/".to_owned(),
             meta_schema: Some("https://json-schema.org/draft/2019-09/schema".to_owned()),
-            visitors: Vec::default(),
+            visitors: Vec::new(),
             inline_subschemas: false,
         }
     }
@@ -96,9 +96,7 @@ impl SchemaSettings {
                 Box::new(ReplaceBoolSchemas {
                     skip_additional_properties: true,
                 }),
-                Box::new(SetSingleExample {
-                    retain_examples: false,
-                }),
+                Box::new(SetSingleExample),
             ],
             inline_subschemas: false,
         }
@@ -150,7 +148,7 @@ impl SchemaSettings {
 #[derive(Debug, Default)]
 pub struct SchemaGenerator {
     settings: SchemaSettings,
-    definitions: Map<String, Schema>,
+    definitions: BTreeMap<String, Schema>,
     pending_schema_ids: HashSet<Cow<'static, str>>,
     schema_id_to_name: HashMap<Cow<'static, str>, String>,
     used_schema_names: HashSet<String>,
@@ -198,19 +196,6 @@ impl SchemaGenerator {
         &self.settings
     }
 
-    #[deprecated = "This method no longer has any effect."]
-    pub fn make_extensible(&self, _schema: &mut SchemaObject) {}
-
-    #[deprecated = "Use `Schema::Bool(true)` instead"]
-    pub fn schema_for_any(&self) -> Schema {
-        Schema::Bool(true)
-    }
-
-    #[deprecated = "Use `Schema::Bool(false)` instead"]
-    pub fn schema_for_none(&self) -> Schema {
-        Schema::Bool(false)
-    }
-
     /// Generates a JSON Schema for the type `T`, and returns either the schema itself or a `$ref` schema referencing `T`'s schema.
     ///
     /// If `T` is [referenceable](JsonSchema::is_referenceable), this will add `T`'s schema to this generator's definitions, and
@@ -262,7 +247,7 @@ impl SchemaGenerator {
         name: String,
         id: Cow<'static, str>,
     ) {
-        let dummy = Schema::Bool(false);
+        let dummy = false.into();
         // insert into definitions BEFORE calling json_schema to avoid infinite recursion
         self.definitions.insert(name.clone(), dummy);
 
@@ -273,26 +258,26 @@ impl SchemaGenerator {
 
     /// Borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated.
     ///
-    /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas
+    /// The keys of the returned `BTreeMap` are the [schema names](JsonSchema::schema_name), and the values are the schemas
     /// themselves.
-    pub fn definitions(&self) -> &Map<String, Schema> {
+    pub fn definitions(&self) -> &BTreeMap<String, Schema> {
         &self.definitions
     }
 
     /// Mutably borrows the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated.
     ///
-    /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas
+    /// The keys of the returned `BTreeMap` are the [schema names](JsonSchema::schema_name), and the values are the schemas
     /// themselves.
-    pub fn definitions_mut(&mut self) -> &mut Map<String, Schema> {
+    pub fn definitions_mut(&mut self) -> &mut BTreeMap<String, Schema> {
         &mut self.definitions
     }
 
     /// Returns the collection of all [referenceable](JsonSchema::is_referenceable) schemas that have been generated,
     /// leaving an empty map in its place.
     ///
-    /// The keys of the returned `Map` are the [schema names](JsonSchema::schema_name), and the values are the schemas
+    /// The keys of the returned `BTreeMap` are the [schema names](JsonSchema::schema_name), and the values are the schemas
     /// themselves.
-    pub fn take_definitions(&mut self) -> Map<String, Schema> {
+    pub fn take_definitions(&mut self) -> BTreeMap<String, Schema> {
         std::mem::take(&mut self.definitions)
     }
 
@@ -306,40 +291,72 @@ impl SchemaGenerator {
     /// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
     /// add them to the `SchemaGenerator`'s schema definitions and include them in the returned `SchemaObject`'s
     /// [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
-    pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> RootSchema {
-        let mut schema = self.json_schema_internal::<T>(T::schema_id()).into_object();
-        schema.metadata().title.get_or_insert_with(T::schema_name);
-        let mut root = RootSchema {
-            meta_schema: self.settings.meta_schema.clone(),
-            definitions: self.definitions.clone(),
-            schema,
-        };
+    pub fn root_schema_for<T: ?Sized + JsonSchema>(&mut self) -> Schema {
+        let mut schema = self.json_schema_internal::<T>(T::schema_id());
+
+        let object = schema.ensure_object();
+
+        object
+            .entry("title")
+            .or_insert_with(|| T::schema_name().into());
+
+        if let Some(meta_schema) = self.settings.meta_schema.as_deref() {
+            object.insert("$schema".into(), meta_schema.into());
+        }
+
+        if !self.definitions.is_empty() {
+            object.insert(
+                "definitions".into(),
+                serde_json::Value::Object(
+                    self.definitions
+                        .iter()
+                        .map(|(k, v)| (k.clone(), v.clone().into()))
+                        .collect(),
+                ),
+            );
+        }
 
         for visitor in &mut self.settings.visitors {
-            visitor.visit_root_schema(&mut root)
+            visitor.visit_schema(&mut schema);
         }
 
-        root
+        schema
     }
 
     /// Consumes `self` and generates a root JSON Schema for the type `T`.
     ///
     /// If `T`'s schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will
     /// include them in the returned `SchemaObject`'s [`definitions`](../schema/struct.Metadata.html#structfield.definitions)
-    pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> RootSchema {
-        let mut schema = self.json_schema_internal::<T>(T::schema_id()).into_object();
-        schema.metadata().title.get_or_insert_with(T::schema_name);
-        let mut root = RootSchema {
-            meta_schema: self.settings.meta_schema,
-            definitions: self.definitions,
-            schema,
-        };
+    pub fn into_root_schema_for<T: ?Sized + JsonSchema>(mut self) -> Schema {
+        let mut schema = self.json_schema_internal::<T>(T::schema_id());
+
+        let object = schema.ensure_object();
+
+        object
+            .entry("title")
+            .or_insert_with(|| T::schema_name().into());
+
+        if let Some(meta_schema) = self.settings.meta_schema {
+            object.insert("$schema".into(), meta_schema.into());
+        }
+
+        if !self.definitions.is_empty() {
+            object.insert(
+                "definitions".into(),
+                serde_json::Value::Object(
+                    self.definitions
+                        .into_iter()
+                        .map(|(k, v)| (k, v.into()))
+                        .collect(),
+                ),
+            );
+        }
 
         for visitor in &mut self.settings.visitors {
-            visitor.visit_root_schema(&mut root)
+            visitor.visit_schema(&mut schema);
         }
 
-        root
+        schema
     }
 
     /// Generates a root JSON Schema for the given example value.
@@ -349,29 +366,39 @@ impl SchemaGenerator {
     pub fn root_schema_for_value<T: ?Sized + Serialize>(
         &mut self,
         value: &T,
-    ) -> Result<RootSchema, serde_json::Error> {
-        let mut schema = value
-            .serialize(crate::ser::Serializer {
-                gen: self,
-                include_title: true,
-            })?
-            .into_object();
+    ) -> Result<Schema, serde_json::Error> {
+        let mut schema = value.serialize(crate::ser::Serializer {
+            gen: self,
+            include_title: true,
+        })?;
+
+        let object = schema.ensure_object();
 
         if let Ok(example) = serde_json::to_value(value) {
-            schema.metadata().examples.push(example);
+            object.insert("examples".into(), vec![example].into());
         }
 
-        let mut root = RootSchema {
-            meta_schema: self.settings.meta_schema.clone(),
-            definitions: self.definitions.clone(),
-            schema,
-        };
+        if let Some(meta_schema) = self.settings.meta_schema.as_deref() {
+            object.insert("$schema".into(), meta_schema.into());
+        }
+
+        if !self.definitions.is_empty() {
+            object.insert(
+                "definitions".into(),
+                serde_json::Value::Object(
+                    self.definitions
+                        .iter()
+                        .map(|(k, v)| (k.clone(), v.clone().into()))
+                        .collect(),
+                ),
+            );
+        }
 
         for visitor in &mut self.settings.visitors {
-            visitor.visit_root_schema(&mut root)
+            visitor.visit_schema(&mut schema);
         }
 
-        Ok(root)
+        Ok(schema)
     }
 
     /// Consumes `self` and generates a root JSON Schema for the given example value.
@@ -381,72 +408,39 @@ impl SchemaGenerator {
     pub fn into_root_schema_for_value<T: ?Sized + Serialize>(
         mut self,
         value: &T,
-    ) -> Result<RootSchema, serde_json::Error> {
-        let mut schema = value
-            .serialize(crate::ser::Serializer {
-                gen: &mut self,
-                include_title: true,
-            })?
-            .into_object();
+    ) -> Result<Schema, serde_json::Error> {
+        let mut schema = value.serialize(crate::ser::Serializer {
+            gen: &mut self,
+            include_title: true,
+        })?;
+
+        let object = schema.ensure_object();
 
         if let Ok(example) = serde_json::to_value(value) {
-            schema.metadata().examples.push(example);
+            object.insert("examples".into(), vec![example].into());
         }
 
-        let mut root = RootSchema {
-            meta_schema: self.settings.meta_schema,
-            definitions: self.definitions,
-            schema,
-        };
-
-        for visitor in &mut self.settings.visitors {
-            visitor.visit_root_schema(&mut root)
+        if let Some(meta_schema) = self.settings.meta_schema {
+            object.insert("$schema".into(), meta_schema.into());
         }
 
-        Ok(root)
-    }
+        if !self.definitions.is_empty() {
+            object.insert(
+                "definitions".into(),
+                serde_json::Value::Object(
+                    self.definitions
+                        .into_iter()
+                        .map(|(k, v)| (k, v.into()))
+                        .collect(),
+                ),
+            );
+        }
 
-    /// Attemps to find the schema that the given `schema` is referencing.
-    ///
-    /// If the given `schema` has a [`$ref`](../schema/struct.SchemaObject.html#structfield.reference) property which refers
-    /// to another schema in `self`'s schema definitions, the referenced schema will be returned. Otherwise, returns `None`.
-    ///
-    /// # Example
-    /// ```
-    /// use schemars::{JsonSchema, gen::SchemaGenerator};
-    ///
-    /// #[derive(JsonSchema)]
-    /// struct MyStruct {
-    ///     foo: i32,
-    /// }
-    ///
-    /// let mut gen = SchemaGenerator::default();
-    /// let ref_schema = gen.subschema_for::<MyStruct>();
-    ///
-    /// assert!(ref_schema.is_ref());
-    ///
-    /// let dereferenced = gen.dereference(&ref_schema);
-    ///
-    /// assert!(dereferenced.is_some());
-    /// assert!(!dereferenced.unwrap().is_ref());
-    /// assert_eq!(dereferenced, gen.definitions().get("MyStruct"));
-    /// ```
-    pub fn dereference<'a>(&'a self, schema: &Schema) -> Option<&'a Schema> {
-        match schema {
-            Schema::Object(SchemaObject {
-                reference: Some(ref schema_ref),
-                ..
-            }) => {
-                let definitions_path = &self.settings().definitions_path;
-                if schema_ref.starts_with(definitions_path) {
-                    let name = &schema_ref[definitions_path.len()..];
-                    self.definitions.get(name)
-                } else {
-                    None
-                }
-            }
-            _ => None,
+        for visitor in &mut self.settings.visitors {
+            visitor.visit_schema(&mut schema);
         }
+
+        Ok(schema)
     }
 
     fn json_schema_internal<T: ?Sized + JsonSchema>(&mut self, id: Cow<'static, str>) -> Schema {
diff --git a/schemars/src/json_schema_impls/array.rs b/schemars/src/json_schema_impls/array.rs
index 036d042..bacc079 100644
--- a/schemars/src/json_schema_impls/array.rs
+++ b/schemars/src/json_schema_impls/array.rs
@@ -1,6 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use std::borrow::Cow;
 
 // Does not require T: JsonSchema.
@@ -16,15 +15,10 @@ impl<T> JsonSchema for [T; 0] {
     }
 
     fn json_schema(_: &mut SchemaGenerator) -> Schema {
-        SchemaObject {
-            instance_type: Some(InstanceType::Array.into()),
-            array: Some(Box::new(ArrayValidation {
-                max_items: Some(0),
-                ..Default::default()
-            })),
-            ..Default::default()
-        }
-        .into()
+        json_schema!({
+            "type": "array",
+            "maxItems": 0,
+        })
     }
 }
 
@@ -44,17 +38,12 @@ macro_rules! array_impls {
                 }
 
                 fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-                    SchemaObject {
-                        instance_type: Some(InstanceType::Array.into()),
-                        array: Some(Box::new(ArrayValidation {
-                            items: Some(gen.subschema_for::<T>().into()),
-                            max_items: Some($len),
-                            min_items: Some($len),
-                            ..Default::default()
-                        })),
-                        ..Default::default()
-                    }
-                    .into()
+                    json_schema!({
+                        "type": "array",
+                        "items": serde_json::Value::from(gen.subschema_for::<T>()),
+                        "minItems": $len,
+                        "maxItems": $len,
+                    })
                 }
             }
         )+
@@ -67,40 +56,3 @@ array_impls! {
     21 22 23 24 25 26 27 28 29 30
     31 32
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::tests::{schema_for, schema_object_for};
-    use pretty_assertions::assert_eq;
-
-    #[test]
-    fn schema_for_array() {
-        let schema = schema_object_for::<[i32; 8]>();
-        assert_eq!(
-            schema.instance_type,
-            Some(SingleOrVec::from(InstanceType::Array))
-        );
-        let array_validation = schema.array.unwrap();
-        assert_eq!(
-            array_validation.items,
-            Some(SingleOrVec::from(schema_for::<i32>()))
-        );
-        assert_eq!(array_validation.max_items, Some(8));
-        assert_eq!(array_validation.min_items, Some(8));
-    }
-
-    // SomeStruct does not implement JsonSchema
-    struct SomeStruct;
-
-    #[test]
-    fn schema_for_empty_array() {
-        let schema = schema_object_for::<[SomeStruct; 0]>();
-        assert_eq!(
-            schema.instance_type,
-            Some(SingleOrVec::from(InstanceType::Array))
-        );
-        let array_validation = schema.array.unwrap();
-        assert_eq!(array_validation.max_items, Some(0));
-    }
-}
diff --git a/schemars/src/json_schema_impls/arrayvec05.rs b/schemars/src/json_schema_impls/arrayvec05.rs
deleted file mode 100644
index 281bde7..0000000
--- a/schemars/src/json_schema_impls/arrayvec05.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use arrayvec05::{Array, ArrayString, ArrayVec};
-use std::convert::TryInto;
-
-// Do not set maxLength on the schema as that describes length in characters, but we only
-// know max length in bytes.
-forward_impl!((<A> JsonSchema for ArrayString<A> where A: Array<Item = u8> + Copy) => String);
-
-impl<A: Array> JsonSchema for ArrayVec<A>
-where
-    A::Item: JsonSchema,
-{
-    no_ref_schema!();
-
-    fn schema_name() -> String {
-        format!(
-            "Array_up_to_size_{}_of_{}",
-            A::CAPACITY,
-            A::Item::schema_name()
-        )
-    }
-
-    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-        SchemaObject {
-            instance_type: Some(InstanceType::Array.into()),
-            array: Some(Box::new(ArrayValidation {
-                items: Some(gen.subschema_for::<A::Item>().into()),
-                max_items: A::CAPACITY.try_into().ok(),
-                ..Default::default()
-            })),
-            ..Default::default()
-        }
-        .into()
-    }
-}
diff --git a/schemars/src/json_schema_impls/arrayvec07.rs b/schemars/src/json_schema_impls/arrayvec07.rs
index e2d92c5..3d4d050 100644
--- a/schemars/src/json_schema_impls/arrayvec07.rs
+++ b/schemars/src/json_schema_impls/arrayvec07.rs
@@ -1,8 +1,6 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use arrayvec07::{ArrayString, ArrayVec};
-use std::convert::TryInto;
 
 // Do not set maxLength on the schema as that describes length in characters, but we only
 // know max length in bytes.
@@ -19,15 +17,10 @@ where
     }
 
     fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-        SchemaObject {
-            instance_type: Some(InstanceType::Array.into()),
-            array: Some(Box::new(ArrayValidation {
-                items: Some(gen.subschema_for::<T>().into()),
-                max_items: CAP.try_into().ok(),
-                ..Default::default()
-            })),
-            ..Default::default()
-        }
-        .into()
+        json_schema!({
+            "type": "array",
+            "items": gen.subschema_for::<T>(),
+            "maxItems": CAP
+        })
     }
 }
diff --git a/schemars/src/json_schema_impls/atomic.rs b/schemars/src/json_schema_impls/atomic.rs
index 5d8f6d9..53d55c6 100644
--- a/schemars/src/json_schema_impls/atomic.rs
+++ b/schemars/src/json_schema_impls/atomic.rs
@@ -1,6 +1,3 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
 use std::sync::atomic::*;
 
 forward_impl!(AtomicBool => bool);
@@ -22,12 +19,12 @@ forward_impl!(AtomicUsize => usize);
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::tests::schema_object_for;
+    use crate::schema_for;
     use pretty_assertions::assert_eq;
 
     #[test]
     fn schema_for_atomics() {
-        let atomic_schema = schema_object_for::<(
+        let atomic_schema = schema_for!((
             AtomicBool,
             AtomicI8,
             AtomicI16,
@@ -39,9 +36,8 @@ mod tests {
             AtomicU32,
             AtomicU64,
             AtomicUsize,
-        )>();
-        let basic_schema =
-            schema_object_for::<(bool, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize)>();
+        ));
+        let basic_schema = schema_for!((bool, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize));
         assert_eq!(atomic_schema, basic_schema);
     }
 }
diff --git a/schemars/src/json_schema_impls/bytes.rs b/schemars/src/json_schema_impls/bytes.rs
deleted file mode 100644
index f1a0f29..0000000
--- a/schemars/src/json_schema_impls/bytes.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use bytes::{Bytes, BytesMut};
-
-forward_impl!((JsonSchema for Bytes) => Vec<u8>);
-forward_impl!((JsonSchema for BytesMut) => Vec<u8>);
diff --git a/schemars/src/json_schema_impls/chrono.rs b/schemars/src/json_schema_impls/chrono04.rs
similarity index 61%
rename from schemars/src/json_schema_impls/chrono.rs
rename to schemars/src/json_schema_impls/chrono04.rs
index 3a1731b..5fd23e8 100644
--- a/schemars/src/json_schema_impls/chrono.rs
+++ b/schemars/src/json_schema_impls/chrono04.rs
@@ -1,8 +1,6 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use chrono::prelude::*;
-use serde_json::json;
+use crate::{json_schema, JsonSchema, Schema};
+use chrono04::prelude::*;
 use std::borrow::Cow;
 
 impl JsonSchema for Weekday {
@@ -17,20 +15,18 @@ impl JsonSchema for Weekday {
     }
 
     fn json_schema(_: &mut SchemaGenerator) -> Schema {
-        SchemaObject {
-            instance_type: Some(InstanceType::String.into()),
-            enum_values: Some(vec![
-                json!("Mon"),
-                json!("Tue"),
-                json!("Wed"),
-                json!("Thu"),
-                json!("Fri"),
-                json!("Sat"),
-                json!("Sun"),
-            ]),
-            ..Default::default()
-        }
-        .into()
+        json_schema!({
+            "type": "string",
+            "enum": [
+                "Mon",
+                "Tue",
+                "Wed",
+                "Thu",
+                "Fri",
+                "Sat",
+                "Sun",
+            ]
+        })
     }
 }
 
@@ -51,12 +47,10 @@ macro_rules! formatted_string_impl {
             }
 
             fn json_schema(_: &mut SchemaGenerator) -> Schema {
-                SchemaObject {
-                    instance_type: Some(InstanceType::String.into()),
-                    format: Some($format.to_owned()),
-                    ..Default::default()
-                }
-                .into()
+                json_schema!({
+                    "type": "string",
+                    "format": $format
+                })
             }
         }
     };
diff --git a/schemars/src/json_schema_impls/core.rs b/schemars/src/json_schema_impls/core.rs
index 955ead6..16104f8 100644
--- a/schemars/src/json_schema_impls/core.rs
+++ b/schemars/src/json_schema_impls/core.rs
@@ -1,7 +1,6 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use serde_json::json;
+use crate::{json_schema, JsonSchema, Schema};
+use serde_json::Value;
 use std::borrow::Cow;
 use std::ops::{Bound, Range, RangeInclusive};
 
@@ -18,35 +17,45 @@ impl<T: JsonSchema> JsonSchema for Option<T> {
 
     fn json_schema(gen: &mut SchemaGenerator) -> Schema {
         let mut schema = gen.subschema_for::<T>();
+
         if gen.settings().option_add_null_type {
-            schema = match schema {
-                Schema::Bool(true) => Schema::Bool(true),
-                Schema::Bool(false) => <()>::json_schema(gen),
-                Schema::Object(SchemaObject {
-                    instance_type: Some(ref mut instance_type),
-                    ..
-                }) => {
-                    add_null_type(instance_type);
-                    schema
-                }
-                schema => SchemaObject {
-                    // TODO technically the schema already accepts null, so this may be unnecessary
-                    subschemas: Some(Box::new(SubschemaValidation {
-                        any_of: Some(vec![schema, <()>::json_schema(gen)]),
-                        ..Default::default()
-                    })),
-                    ..Default::default()
+            schema = match schema.try_to_object() {
+                Ok(mut obj) => {
+                    let instance_type = obj.get_mut("type");
+                    match instance_type {
+                        Some(Value::Array(array)) => {
+                            let null = Value::from("null");
+                            if !array.contains(&null) {
+                                array.push(null);
+                            }
+                            obj.into()
+                        }
+                        Some(Value::String(string)) => {
+                            if string != "null" {
+                                *instance_type.unwrap() =
+                                    Value::Array(vec![std::mem::take(string).into(), "null".into()])
+                            }
+                            obj.into()
+                        }
+                        _ => json_schema!({
+                            "anyOf": [
+                                obj,
+                                <()>::json_schema(gen)
+                            ]
+                        }),
+                    }
                 }
-                .into(),
+                Err(true) => true.into(),
+                Err(false) => <()>::json_schema(gen),
             }
         }
+
         if gen.settings().option_nullable {
-            let mut schema_obj = schema.into_object();
-            schema_obj
-                .extensions
-                .insert("nullable".to_owned(), json!(true));
-            schema = Schema::Object(schema_obj);
+            schema
+                .ensure_object()
+                .insert("nullable".into(), true.into());
         };
+
         schema
     }
 
@@ -59,16 +68,6 @@ impl<T: JsonSchema> JsonSchema for Option<T> {
     }
 }
 
-fn add_null_type(instance_type: &mut SingleOrVec<InstanceType>) {
-    match instance_type {
-        SingleOrVec::Single(ty) if **ty != InstanceType::Null => {
-            *instance_type = vec![**ty, InstanceType::Null].into()
-        }
-        SingleOrVec::Vec(ty) if !ty.contains(&InstanceType::Null) => ty.push(InstanceType::Null),
-        _ => {}
-    };
-}
-
 impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
     fn schema_name() -> String {
         format!("Result_of_{}_or_{}", T::schema_name(), E::schema_name())
@@ -79,27 +78,24 @@ impl<T: JsonSchema, E: JsonSchema> JsonSchema for Result<T, E> {
     }
 
     fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-        let mut ok_schema = SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            ..Default::default()
-        };
-        let obj = ok_schema.object();
-        obj.required.insert("Ok".to_owned());
-        obj.properties
-            .insert("Ok".to_owned(), gen.subschema_for::<T>());
-
-        let mut err_schema = SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            ..Default::default()
-        };
-        let obj = err_schema.object();
-        obj.required.insert("Err".to_owned());
-        obj.properties
-            .insert("Err".to_owned(), gen.subschema_for::<E>());
-
-        let mut schema = SchemaObject::default();
-        schema.subschemas().one_of = Some(vec![ok_schema.into(), err_schema.into()]);
-        schema.into()
+        json_schema!({
+            "oneOf": [
+                {
+                    "type": "object",
+                    "properties": {
+                        "Ok": gen.subschema_for::<T>()
+                    },
+                    "required": ["Ok"]
+                },
+                {
+                    "type": "object",
+                    "properties": {
+                        "Err": gen.subschema_for::<E>()
+                    },
+                    "required": ["Err"]
+                }
+            ]
+        })
     }
 }
 
@@ -113,37 +109,28 @@ impl<T: JsonSchema> JsonSchema for Bound<T> {
     }
 
     fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-        let mut included_schema = SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            ..Default::default()
-        };
-        let obj = included_schema.object();
-        obj.required.insert("Included".to_owned());
-        obj.properties
-            .insert("Included".to_owned(), gen.subschema_for::<T>());
-
-        let mut excluded_schema = SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            ..Default::default()
-        };
-        let obj = excluded_schema.object();
-        obj.required.insert("Excluded".to_owned());
-        obj.properties
-            .insert("Excluded".to_owned(), gen.subschema_for::<T>());
-
-        let unbounded_schema = SchemaObject {
-            instance_type: Some(InstanceType::String.into()),
-            const_value: Some(json!("Unbounded")),
-            ..Default::default()
-        };
-
-        let mut schema = SchemaObject::default();
-        schema.subschemas().one_of = Some(vec![
-            included_schema.into(),
-            excluded_schema.into(),
-            unbounded_schema.into(),
-        ]);
-        schema.into()
+        json_schema!({
+            "oneOf": [
+                {
+                    "type": "object",
+                    "properties": {
+                        "Included": gen.subschema_for::<T>()
+                    },
+                    "required": ["Included"]
+                },
+                {
+                    "type": "object",
+                    "properties": {
+                        "Excluded": gen.subschema_for::<T>()
+                    },
+                    "required": ["Excluded"]
+                },
+                {
+                    "type": "string",
+                    "const": "Unbounded"
+                }
+            ]
+        })
     }
 }
 
@@ -157,18 +144,15 @@ impl<T: JsonSchema> JsonSchema for Range<T> {
     }
 
     fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-        let mut schema = SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            ..Default::default()
-        };
-        let obj = schema.object();
-        obj.required.insert("start".to_owned());
-        obj.required.insert("end".to_owned());
-        obj.properties
-            .insert("start".to_owned(), gen.subschema_for::<T>());
-        obj.properties
-            .insert("end".to_owned(), gen.subschema_for::<T>());
-        schema.into()
+        let subschema = gen.subschema_for::<T>();
+        json_schema!({
+            "type": "object",
+            "properties": {
+                "start": subschema,
+                "end": subschema
+            },
+            "required": ["start", "end"]
+        })
     }
 }
 
@@ -177,54 +161,3 @@ forward_impl!((<T: JsonSchema> JsonSchema for RangeInclusive<T>) => Range<T>);
 forward_impl!((<T: ?Sized> JsonSchema for std::marker::PhantomData<T>) => ());
 
 forward_impl!((<'a> JsonSchema for std::fmt::Arguments<'a>) => String);
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::tests::{schema_for, schema_object_for};
-    use pretty_assertions::assert_eq;
-
-    #[test]
-    fn schema_for_option() {
-        let schema = schema_object_for::<Option<i32>>();
-        assert_eq!(
-            schema.instance_type,
-            Some(vec![InstanceType::Integer, InstanceType::Null].into())
-        );
-        assert_eq!(schema.extensions.get("nullable"), None);
-        assert_eq!(schema.subschemas.is_none(), true);
-    }
-
-    #[test]
-    fn schema_for_option_with_ref() {
-        use crate as schemars;
-        #[derive(JsonSchema)]
-        struct Foo;
-
-        let schema = schema_object_for::<Option<Foo>>();
-        assert_eq!(schema.instance_type, None);
-        assert_eq!(schema.extensions.get("nullable"), None);
-        assert_eq!(schema.subschemas.is_some(), true);
-        let any_of = schema.subschemas.unwrap().any_of.unwrap();
-        assert_eq!(any_of.len(), 2);
-        assert_eq!(any_of[0], Schema::new_ref("#/definitions/Foo".to_string()));
-        assert_eq!(any_of[1], schema_for::<()>());
-    }
-
-    #[test]
-    fn schema_for_result() {
-        let schema = schema_object_for::<Result<bool, String>>();
-        let one_of = schema.subschemas.unwrap().one_of.unwrap();
-        assert_eq!(one_of.len(), 2);
-
-        let ok_schema: SchemaObject = one_of[0].clone().into();
-        let obj = ok_schema.object.unwrap();
-        assert!(obj.required.contains("Ok"));
-        assert_eq!(obj.properties["Ok"], schema_for::<bool>());
-
-        let err_schema: SchemaObject = one_of[1].clone().into();
-        let obj = err_schema.object.unwrap();
-        assert!(obj.required.contains("Err"));
-        assert_eq!(obj.properties["Err"], schema_for::<String>());
-    }
-}
diff --git a/schemars/src/json_schema_impls/decimal.rs b/schemars/src/json_schema_impls/decimal.rs
index 643ca44..462e3d5 100644
--- a/schemars/src/json_schema_impls/decimal.rs
+++ b/schemars/src/json_schema_impls/decimal.rs
@@ -1,6 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use std::borrow::Cow;
 
 macro_rules! decimal_impl {
@@ -17,23 +16,16 @@ macro_rules! decimal_impl {
             }
 
             fn json_schema(_: &mut SchemaGenerator) -> Schema {
-                SchemaObject {
-                    instance_type: Some(InstanceType::String.into()),
-                    string: Some(Box::new(StringValidation {
-                        pattern: Some(r"^-?[0-9]+(\.[0-9]+)?$".to_owned()),
-                        ..Default::default()
-                    })),
-                    ..Default::default()
-                }
-                .into()
+                json_schema!({
+                    "type": "string",
+                    "pattern": r"^-?[0-9]+(\.[0-9]+)?$",
+                })
             }
         }
     };
 }
 
-#[cfg(feature = "rust_decimal")]
-decimal_impl!(rust_decimal::Decimal);
-#[cfg(feature = "bigdecimal03")]
-decimal_impl!(bigdecimal03::BigDecimal);
+#[cfg(feature = "rust_decimal1")]
+decimal_impl!(rust_decimal1::Decimal);
 #[cfg(feature = "bigdecimal04")]
 decimal_impl!(bigdecimal04::BigDecimal);
diff --git a/schemars/src/json_schema_impls/either.rs b/schemars/src/json_schema_impls/either1.rs
similarity index 67%
rename from schemars/src/json_schema_impls/either.rs
rename to schemars/src/json_schema_impls/either1.rs
index 957fdd1..96ed8a8 100644
--- a/schemars/src/json_schema_impls/either.rs
+++ b/schemars/src/json_schema_impls/either1.rs
@@ -1,7 +1,6 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use either::Either;
+use crate::{json_schema, JsonSchema, Schema};
+use either1::Either;
 use std::borrow::Cow;
 
 impl<L: JsonSchema, R: JsonSchema> JsonSchema for Either<L, R> {
@@ -20,8 +19,8 @@ impl<L: JsonSchema, R: JsonSchema> JsonSchema for Either<L, R> {
     }
 
     fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-        let mut schema = SchemaObject::default();
-        schema.subschemas().any_of = Some(vec![gen.subschema_for::<L>(), gen.subschema_for::<R>()]);
-        schema.into()
+        json_schema!({
+            "anyOf": [gen.subschema_for::<L>(), gen.subschema_for::<R>()],
+        })
     }
 }
diff --git a/schemars/src/json_schema_impls/enumset.rs b/schemars/src/json_schema_impls/enumset.rs
deleted file mode 100644
index 22a3ebc..0000000
--- a/schemars/src/json_schema_impls/enumset.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use enumset::{EnumSet, EnumSetType};
-
-forward_impl!((<T> JsonSchema for EnumSet<T> where T: EnumSetType + JsonSchema) => std::collections::BTreeSet<T>);
diff --git a/schemars/src/json_schema_impls/ffi.rs b/schemars/src/json_schema_impls/ffi.rs
index 55b5012..4ad85c2 100644
--- a/schemars/src/json_schema_impls/ffi.rs
+++ b/schemars/src/json_schema_impls/ffi.rs
@@ -1,6 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use std::borrow::Cow;
 use std::ffi::{CStr, CString, OsStr, OsString};
 
@@ -14,27 +13,24 @@ impl JsonSchema for OsString {
     }
 
     fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-        let mut unix_schema = SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            ..Default::default()
-        };
-        let obj = unix_schema.object();
-        obj.required.insert("Unix".to_owned());
-        obj.properties
-            .insert("Unix".to_owned(), <Vec<u8>>::json_schema(gen));
-
-        let mut win_schema = SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            ..Default::default()
-        };
-        let obj = win_schema.object();
-        obj.required.insert("Windows".to_owned());
-        obj.properties
-            .insert("Windows".to_owned(), <Vec<u16>>::json_schema(gen));
-
-        let mut schema = SchemaObject::default();
-        schema.subschemas().one_of = Some(vec![unix_schema.into(), win_schema.into()]);
-        schema.into()
+        json_schema!({
+            "oneOf": [
+                {
+                    "type": "object",
+                    "properties": {
+                        "Unix": <Vec<u8>>::json_schema(gen)
+                    },
+                    "required": ["Unix"]
+                },
+                {
+                    "type": "object",
+                    "properties": {
+                        "Windows": <Vec<u16>>::json_schema(gen)
+                    },
+                    "required": ["Windows"]
+                },
+            ]
+        })
     }
 }
 
diff --git a/schemars/src/json_schema_impls/indexmap.rs b/schemars/src/json_schema_impls/indexmap.rs
deleted file mode 100644
index 3bd3e54..0000000
--- a/schemars/src/json_schema_impls/indexmap.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use indexmap::{IndexMap, IndexSet};
-use std::collections::{HashMap, HashSet};
-
-forward_impl!((<K, V: JsonSchema, H> JsonSchema for IndexMap<K, V, H>) => HashMap<K, V, H>);
-forward_impl!((<T: JsonSchema, H> JsonSchema for IndexSet<T, H>) => HashSet<T, H>);
diff --git a/schemars/src/json_schema_impls/indexmap2.rs b/schemars/src/json_schema_impls/indexmap2.rs
index 44eeedb..9473dea 100644
--- a/schemars/src/json_schema_impls/indexmap2.rs
+++ b/schemars/src/json_schema_impls/indexmap2.rs
@@ -1,5 +1,3 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::*;
 use crate::JsonSchema;
 use indexmap2::{IndexMap, IndexSet};
 use std::collections::{HashMap, HashSet};
diff --git a/schemars/src/json_schema_impls/maps.rs b/schemars/src/json_schema_impls/maps.rs
index 7c27808..b934f8b 100644
--- a/schemars/src/json_schema_impls/maps.rs
+++ b/schemars/src/json_schema_impls/maps.rs
@@ -1,6 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use std::borrow::Cow;
 
 macro_rules! map_impl {
@@ -20,16 +19,10 @@ macro_rules! map_impl {
             }
 
             fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-                let subschema = gen.subschema_for::<V>();
-                SchemaObject {
-                    instance_type: Some(InstanceType::Object.into()),
-                    object: Some(Box::new(ObjectValidation {
-                        additional_properties: Some(Box::new(subschema)),
-                        ..Default::default()
-                    })),
-                    ..Default::default()
-                }
-                .into()
+                json_schema!({
+                    "type": "object",
+                    "additionalProperties": gen.subschema_for::<V>(),
+                })
             }
         }
     };
diff --git a/schemars/src/json_schema_impls/mod.rs b/schemars/src/json_schema_impls/mod.rs
index 0c548eb..ebcb61b 100644
--- a/schemars/src/json_schema_impls/mod.rs
+++ b/schemars/src/json_schema_impls/mod.rs
@@ -21,11 +21,11 @@ macro_rules! forward_impl {
                 <$target>::schema_id()
             }
 
-            fn json_schema(gen: &mut SchemaGenerator) -> Schema {
+            fn json_schema(gen: &mut $crate::gen::SchemaGenerator) -> $crate::Schema {
                 <$target>::json_schema(gen)
             }
 
-            fn _schemars_private_non_optional_json_schema(gen: &mut SchemaGenerator) -> Schema {
+            fn _schemars_private_non_optional_json_schema(gen: &mut $crate::gen::SchemaGenerator) -> $crate::Schema {
                 <$target>::_schemars_private_non_optional_json_schema(gen)
             }
 
@@ -35,55 +35,61 @@ macro_rules! forward_impl {
         }
     };
     ($ty:ty => $target:ty) => {
-        forward_impl!((JsonSchema for $ty) => $target);
+        forward_impl!(($crate::JsonSchema for $ty) => $target);
     };
 }
 
 mod array;
-#[cfg(feature = "arrayvec05")]
-mod arrayvec05;
-#[cfg(feature = "arrayvec07")]
-mod arrayvec07;
-#[cfg(std_atomic)]
-mod atomic;
-#[cfg(feature = "bytes")]
-mod bytes;
-#[cfg(feature = "chrono")]
-mod chrono;
 mod core;
-#[cfg(any(
-    feature = "rust_decimal",
-    feature = "bigdecimal03",
-    feature = "bigdecimal04"
-))]
-mod decimal;
-#[cfg(feature = "either")]
-mod either;
-#[cfg(feature = "enumset")]
-mod enumset;
 mod ffi;
-#[cfg(feature = "indexmap")]
-mod indexmap;
-#[cfg(feature = "indexmap2")]
-mod indexmap2;
 mod maps;
 mod nonzero_signed;
 mod nonzero_unsigned;
 mod primitives;
-#[cfg(feature = "semver")]
-mod semver;
 mod sequences;
 mod serdejson;
-#[cfg(feature = "smallvec")]
-mod smallvec;
-#[cfg(feature = "smol_str")]
-mod smol_str;
 mod time;
 mod tuple;
-#[cfg(feature = "url")]
-mod url;
-#[cfg(feature = "uuid08")]
-mod uuid08;
+mod wrapper;
+
+#[cfg(std_atomic)]
+mod atomic;
+
+#[cfg(feature = "arrayvec07")]
+mod arrayvec07;
+
+#[cfg(feature = "bytes1")]
+mod bytes1 {
+    forward_impl!(bytes1::Bytes => Vec<u8>);
+    forward_impl!(bytes1::BytesMut => Vec<u8>);
+}
+
+#[cfg(feature = "chrono04")]
+mod chrono04;
+
+#[cfg(any(feature = "rust_decimal1", feature = "bigdecimal04"))]
+mod decimal;
+
+#[cfg(feature = "either1")]
+mod either1;
+
+#[cfg(feature = "enumset1")]
+forward_impl!((<T: enumset1::EnumSetType + crate::JsonSchema> crate::JsonSchema for enumset1::EnumSet<T>) => std::collections::BTreeSet<T>);
+
+#[cfg(feature = "indexmap2")]
+mod indexmap2;
+
+#[cfg(feature = "semver1")]
+mod semver1;
+
+#[cfg(feature = "smallvec1")]
+forward_impl!((<A: smallvec1::Array> crate::JsonSchema for smallvec1::SmallVec<A> where A::Item: crate::JsonSchema) => Vec<A::Item>);
+
+#[cfg(feature = "smol_str02")]
+forward_impl!(smol_str02::SmolStr => String);
+
+#[cfg(feature = "url2")]
+mod url2;
+
 #[cfg(feature = "uuid1")]
 mod uuid1;
-mod wrapper;
diff --git a/schemars/src/json_schema_impls/nonzero_signed.rs b/schemars/src/json_schema_impls/nonzero_signed.rs
index c2fba2b..d1b5142 100644
--- a/schemars/src/json_schema_impls/nonzero_signed.rs
+++ b/schemars/src/json_schema_impls/nonzero_signed.rs
@@ -1,6 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{JsonSchema, Schema};
 use std::borrow::Cow;
 use std::num::*;
 
@@ -18,14 +17,12 @@ macro_rules! nonzero_unsigned_impl {
             }
 
             fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-                let zero_schema: Schema = SchemaObject {
-                    const_value: Some(0.into()),
-                    ..Default::default()
-                }
-                .into();
-                let mut schema: SchemaObject = <$primitive>::json_schema(gen).into();
-                schema.subschemas().not = Some(Box::from(zero_schema));
-                schema.into()
+                let mut schema = <$primitive>::json_schema(gen);
+                let object = schema.ensure_object();
+                object.insert("not".to_owned(), serde_json::json!({
+                    "const": 0
+                }));
+                schema
             }
         }
     };
diff --git a/schemars/src/json_schema_impls/nonzero_unsigned.rs b/schemars/src/json_schema_impls/nonzero_unsigned.rs
index 1963d56..e3c6d1d 100644
--- a/schemars/src/json_schema_impls/nonzero_unsigned.rs
+++ b/schemars/src/json_schema_impls/nonzero_unsigned.rs
@@ -1,5 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
+use crate::Schema;
 use crate::JsonSchema;
 use std::borrow::Cow;
 use std::num::*;
@@ -18,9 +18,10 @@ macro_rules! nonzero_unsigned_impl {
             }
 
             fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-                let mut schema: SchemaObject = <$primitive>::json_schema(gen).into();
-                schema.number().minimum = Some(1.0);
-                schema.into()
+                let mut schema = <$primitive>::json_schema(gen);
+                let object = schema.ensure_object();
+                object.insert("minimum".to_owned(), 1.into());
+                schema
             }
         }
     };
@@ -32,18 +33,3 @@ nonzero_unsigned_impl!(NonZeroU32 => u32);
 nonzero_unsigned_impl!(NonZeroU64 => u64);
 nonzero_unsigned_impl!(NonZeroU128 => u128);
 nonzero_unsigned_impl!(NonZeroUsize => usize);
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::tests::schema_object_for;
-    use pretty_assertions::assert_eq;
-
-    #[test]
-    fn schema_for_nonzero_u32() {
-        let schema = schema_object_for::<NonZeroU32>();
-        assert_eq!(schema.number.unwrap().minimum, Some(1.0));
-        assert_eq!(schema.instance_type, Some(InstanceType::Integer.into()));
-        assert_eq!(schema.format, Some("uint32".to_owned()));
-    }
-}
diff --git a/schemars/src/json_schema_impls/primitives.rs b/schemars/src/json_schema_impls/primitives.rs
index d182984..63121fe 100644
--- a/schemars/src/json_schema_impls/primitives.rs
+++ b/schemars/src/json_schema_impls/primitives.rs
@@ -1,67 +1,77 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use std::borrow::Cow;
 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
 use std::path::{Path, PathBuf};
 
 macro_rules! simple_impl {
-    ($type:ty => $instance_type:ident) => {
-        simple_impl!($type => $instance_type, stringify!($instance_type), None);
-    };
-    ($type:ty => $instance_type:ident, $format:literal) => {
-        simple_impl!($type => $instance_type, $format, Some($format.to_owned()));
+    ($type:ty => $instance_type:literal) => {
+        impl JsonSchema for $type {
+            no_ref_schema!();
+
+            fn schema_name() -> String {
+                $instance_type.to_owned()
+            }
+
+            fn schema_id() -> Cow<'static, str> {
+                Cow::Borrowed($instance_type)
+            }
+
+            fn json_schema(_: &mut SchemaGenerator) -> Schema {
+                json_schema!({
+                    "type": $instance_type
+                })
+            }
+        }
     };
-    ($type:ty => $instance_type:ident, $name:expr, $format:expr) => {
+    ($type:ty => $instance_type:literal, $format:literal) => {
         impl JsonSchema for $type {
             no_ref_schema!();
 
             fn schema_name() -> String {
-                $name.to_owned()
+                $format.to_owned()
             }
 
             fn schema_id() -> Cow<'static, str> {
-                Cow::Borrowed($name)
+                Cow::Borrowed($format)
             }
 
             fn json_schema(_: &mut SchemaGenerator) -> Schema {
-                SchemaObject {
-                    instance_type: Some(InstanceType::$instance_type.into()),
-                    format: $format,
-                    ..Default::default()
-                }
-                .into()
+                json_schema!({
+                    "type": $instance_type,
+                    "format": $format
+                })
             }
         }
     };
 }
 
-simple_impl!(str => String);
-simple_impl!(String => String);
-simple_impl!(bool => Boolean);
-simple_impl!(f32 => Number, "float");
-simple_impl!(f64 => Number, "double");
-simple_impl!(i8 => Integer, "int8");
-simple_impl!(i16 => Integer, "int16");
-simple_impl!(i32 => Integer, "int32");
-simple_impl!(i64 => Integer, "int64");
-simple_impl!(i128 => Integer, "int128");
-simple_impl!(isize => Integer, "int");
-simple_impl!(() => Null);
-
-simple_impl!(Path => String);
-simple_impl!(PathBuf => String);
-
-simple_impl!(Ipv4Addr => String, "ipv4");
-simple_impl!(Ipv6Addr => String, "ipv6");
-simple_impl!(IpAddr => String, "ip");
-
-simple_impl!(SocketAddr => String);
-simple_impl!(SocketAddrV4 => String);
-simple_impl!(SocketAddrV6 => String);
+simple_impl!(str => "string");
+simple_impl!(String => "string");
+simple_impl!(bool => "boolean");
+simple_impl!(f32 => "number", "float");
+simple_impl!(f64 => "number", "double");
+simple_impl!(i8 => "integer", "int8");
+simple_impl!(i16 => "integer", "int16");
+simple_impl!(i32 => "integer", "int32");
+simple_impl!(i64 => "integer", "int64");
+simple_impl!(i128 => "integer", "int128");
+simple_impl!(isize => "integer", "int");
+simple_impl!(() => "null");
+
+simple_impl!(Path => "string");
+simple_impl!(PathBuf => "string");
+
+simple_impl!(Ipv4Addr => "string", "ipv4");
+simple_impl!(Ipv6Addr => "string", "ipv6");
+simple_impl!(IpAddr => "string", "ip");
+
+simple_impl!(SocketAddr => "string");
+simple_impl!(SocketAddrV4 => "string");
+simple_impl!(SocketAddrV6 => "string");
 
 macro_rules! unsigned_impl {
-    ($type:ty => $instance_type:ident, $format:expr) => {
+    ($type:ty => $instance_type:literal, $format:literal) => {
         impl JsonSchema for $type {
             no_ref_schema!();
 
@@ -74,24 +84,22 @@ macro_rules! unsigned_impl {
             }
 
             fn json_schema(_: &mut SchemaGenerator) -> Schema {
-                let mut schema = SchemaObject {
-                    instance_type: Some(InstanceType::$instance_type.into()),
-                    format: Some($format.to_owned()),
-                    ..Default::default()
-                };
-                schema.number().minimum = Some(0.0);
-                schema.into()
+                json_schema!({
+                    "type": $instance_type,
+                    "format": $format,
+                    "minimum": 0
+                })
             }
         }
     };
 }
 
-unsigned_impl!(u8 => Integer, "uint8");
-unsigned_impl!(u16 => Integer, "uint16");
-unsigned_impl!(u32 => Integer, "uint32");
-unsigned_impl!(u64 => Integer, "uint64");
-unsigned_impl!(u128 => Integer, "uint128");
-unsigned_impl!(usize => Integer, "uint");
+unsigned_impl!(u8 => "integer", "uint8");
+unsigned_impl!(u16 => "integer", "uint16");
+unsigned_impl!(u32 => "integer", "uint32");
+unsigned_impl!(u64 => "integer", "uint64");
+unsigned_impl!(u128 => "integer", "uint128");
+unsigned_impl!(usize => "integer", "uint");
 
 impl JsonSchema for char {
     no_ref_schema!();
@@ -105,15 +113,10 @@ impl JsonSchema for char {
     }
 
     fn json_schema(_: &mut SchemaGenerator) -> Schema {
-        SchemaObject {
-            instance_type: Some(InstanceType::String.into()),
-            string: Some(Box::new(StringValidation {
-                min_length: Some(1),
-                max_length: Some(1),
-                ..Default::default()
-            })),
-            ..Default::default()
-        }
-        .into()
+        json_schema!({
+            "type": "string",
+            "minLength": 1,
+            "maxLength": 1,
+        })
     }
 }
diff --git a/schemars/src/json_schema_impls/semver.rs b/schemars/src/json_schema_impls/semver.rs
deleted file mode 100644
index dd8ed8e..0000000
--- a/schemars/src/json_schema_impls/semver.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use semver::Version;
-use std::borrow::Cow;
-
-impl JsonSchema for Version {
-    no_ref_schema!();
-
-    fn schema_name() -> String {
-        "Version".to_owned()
-    }
-
-    fn schema_id() -> Cow<'static, str> {
-        Cow::Borrowed("semver::Version")
-    }
-
-    fn json_schema(_: &mut SchemaGenerator) -> Schema {
-        SchemaObject {
-            instance_type: Some(InstanceType::String.into()),
-            string: Some(Box::new(StringValidation {
-                // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
-                pattern: Some(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$".to_owned()),
-                ..Default::default()
-            })),
-            ..Default::default()
-        }
-        .into()
-    }
-}
diff --git a/schemars/src/json_schema_impls/semver1.rs b/schemars/src/json_schema_impls/semver1.rs
new file mode 100644
index 0000000..fb47f78
--- /dev/null
+++ b/schemars/src/json_schema_impls/semver1.rs
@@ -0,0 +1,24 @@
+use crate::gen::SchemaGenerator;
+use crate::{json_schema, JsonSchema, Schema};
+use semver1::Version;
+use std::borrow::Cow;
+
+impl JsonSchema for Version {
+    no_ref_schema!();
+
+    fn schema_name() -> String {
+        "Version".to_owned()
+    }
+
+    fn schema_id() -> Cow<'static, str> {
+        Cow::Borrowed("semver::Version")
+    }
+
+    fn json_schema(_: &mut SchemaGenerator) -> Schema {
+        json_schema!({
+            "type": "string",
+            // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
+            "pattern": r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
+        })
+    }
+}
diff --git a/schemars/src/json_schema_impls/sequences.rs b/schemars/src/json_schema_impls/sequences.rs
index 780307f..d14d4d3 100644
--- a/schemars/src/json_schema_impls/sequences.rs
+++ b/schemars/src/json_schema_impls/sequences.rs
@@ -1,6 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use std::borrow::Cow;
 
 macro_rules! seq_impl {
@@ -21,15 +20,10 @@ macro_rules! seq_impl {
             }
 
             fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-                SchemaObject {
-                    instance_type: Some(InstanceType::Array.into()),
-                    array: Some(Box::new(ArrayValidation {
-                        items: Some(gen.subschema_for::<T>().into()),
-                        ..Default::default()
-                    })),
-                    ..Default::default()
-                }
-                .into()
+                json_schema!({
+                    "type": "array",
+                    "items": gen.subschema_for::<T>(),
+                })
             }
         }
     };
@@ -53,16 +47,11 @@ macro_rules! set_impl {
             }
 
             fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-                SchemaObject {
-                    instance_type: Some(InstanceType::Array.into()),
-                    array: Some(Box::new(ArrayValidation {
-                        unique_items: Some(true),
-                        items: Some(gen.subschema_for::<T>().into()),
-                        ..Default::default()
-                    })),
-                    ..Default::default()
-                }
-                .into()
+                json_schema!({
+                    "type": "array",
+                    "uniqueItems": true,
+                    "items": gen.subschema_for::<T>(),
+                })
             }
         }
     };
diff --git a/schemars/src/json_schema_impls/serdejson.rs b/schemars/src/json_schema_impls/serdejson.rs
index 41eafd5..5c6b58d 100644
--- a/schemars/src/json_schema_impls/serdejson.rs
+++ b/schemars/src/json_schema_impls/serdejson.rs
@@ -1,6 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use serde_json::{Map, Number, Value};
 use std::borrow::Cow;
 use std::collections::BTreeMap;
@@ -17,7 +16,7 @@ impl JsonSchema for Value {
     }
 
     fn json_schema(_: &mut SchemaGenerator) -> Schema {
-        Schema::Bool(true)
+        true.into()
     }
 }
 
@@ -35,11 +34,9 @@ impl JsonSchema for Number {
     }
 
     fn json_schema(_: &mut SchemaGenerator) -> Schema {
-        SchemaObject {
-            instance_type: Some(InstanceType::Number.into()),
-            ..Default::default()
-        }
-        .into()
+        json_schema!({
+            "type": "number"
+        })
     }
 }
 
diff --git a/schemars/src/json_schema_impls/smallvec.rs b/schemars/src/json_schema_impls/smallvec.rs
deleted file mode 100644
index f7a75e3..0000000
--- a/schemars/src/json_schema_impls/smallvec.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use smallvec::{Array, SmallVec};
-
-forward_impl!((<A: Array> JsonSchema for SmallVec<A> where A::Item: JsonSchema) => Vec<A::Item>);
diff --git a/schemars/src/json_schema_impls/smol_str.rs b/schemars/src/json_schema_impls/smol_str.rs
deleted file mode 100644
index cbca4a1..0000000
--- a/schemars/src/json_schema_impls/smol_str.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use smol_str::SmolStr;
-
-forward_impl!(SmolStr => String);
diff --git a/schemars/src/json_schema_impls/time.rs b/schemars/src/json_schema_impls/time.rs
index 767a1d2..7317cfd 100644
--- a/schemars/src/json_schema_impls/time.rs
+++ b/schemars/src/json_schema_impls/time.rs
@@ -1,6 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use std::borrow::Cow;
 use std::time::{Duration, SystemTime};
 
@@ -14,18 +13,14 @@ impl JsonSchema for Duration {
     }
 
     fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-        let mut schema = SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            ..Default::default()
-        };
-        let obj = schema.object();
-        obj.required.insert("secs".to_owned());
-        obj.required.insert("nanos".to_owned());
-        obj.properties
-            .insert("secs".to_owned(), <u64>::json_schema(gen));
-        obj.properties
-            .insert("nanos".to_owned(), <u32>::json_schema(gen));
-        schema.into()
+        json_schema!({
+            "type": "object",
+            "required": ["secs", "nanos"],
+            "properties": {
+                "secs": u64::json_schema(gen),
+                "nanos": u32::json_schema(gen),
+            }
+        })
     }
 }
 
@@ -39,17 +34,13 @@ impl JsonSchema for SystemTime {
     }
 
     fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-        let mut schema = SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            ..Default::default()
-        };
-        let obj = schema.object();
-        obj.required.insert("secs_since_epoch".to_owned());
-        obj.required.insert("nanos_since_epoch".to_owned());
-        obj.properties
-            .insert("secs_since_epoch".to_owned(), <u64>::json_schema(gen));
-        obj.properties
-            .insert("nanos_since_epoch".to_owned(), <u32>::json_schema(gen));
-        schema.into()
+        json_schema!({
+            "type": "object",
+            "required": ["secs_since_epoch", "nanos_since_epoch"],
+            "properties": {
+                "secs_since_epoch": u64::json_schema(gen),
+                "nanos_since_epoch": u32::json_schema(gen),
+            }
+        })
     }
 }
diff --git a/schemars/src/json_schema_impls/tuple.rs b/schemars/src/json_schema_impls/tuple.rs
index f67f06a..ab9a36d 100644
--- a/schemars/src/json_schema_impls/tuple.rs
+++ b/schemars/src/json_schema_impls/tuple.rs
@@ -1,6 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use std::borrow::Cow;
 
 macro_rules! tuple_impls {
@@ -24,20 +23,14 @@ macro_rules! tuple_impls {
                 }
 
                 fn json_schema(gen: &mut SchemaGenerator) -> Schema {
-                    let items = vec![
-                        $(gen.subschema_for::<$name>()),+
-                    ];
-                    SchemaObject {
-                        instance_type: Some(InstanceType::Array.into()),
-                        array: Some(Box::new(ArrayValidation {
-                            items: Some(items.into()),
-                            max_items: Some($len),
-                            min_items: Some($len),
-                            ..Default::default()
-                        })),
-                        ..Default::default()
-                    }
-                    .into()
+                    json_schema!({
+                        "type": "array",
+                        "items": [
+                            $(gen.subschema_for::<$name>()),+
+                        ],
+                        "minItems": $len,
+                        "maxItems": $len,
+                    })
                 }
             }
         )+
@@ -62,29 +55,3 @@ tuple_impls! {
     15 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14)
     16 => (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15)
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::tests::{schema_for, schema_object_for};
-    use pretty_assertions::assert_eq;
-
-    #[test]
-    fn schema_for_map_any_value() {
-        let schema = schema_object_for::<(i32, bool)>();
-        assert_eq!(
-            schema.instance_type,
-            Some(SingleOrVec::from(InstanceType::Array))
-        );
-        let array_validation = schema.array.unwrap();
-        assert_eq!(
-            array_validation.items,
-            Some(SingleOrVec::Vec(vec![
-                schema_for::<i32>(),
-                schema_for::<bool>()
-            ]))
-        );
-        assert_eq!(array_validation.max_items, Some(2));
-        assert_eq!(array_validation.min_items, Some(2));
-    }
-}
diff --git a/schemars/src/json_schema_impls/url.rs b/schemars/src/json_schema_impls/url2.rs
similarity index 56%
rename from schemars/src/json_schema_impls/url.rs
rename to schemars/src/json_schema_impls/url2.rs
index be18612..2fcfd04 100644
--- a/schemars/src/json_schema_impls/url.rs
+++ b/schemars/src/json_schema_impls/url2.rs
@@ -1,8 +1,7 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use std::borrow::Cow;
-use url::Url;
+use url2::Url;
 
 impl JsonSchema for Url {
     no_ref_schema!();
@@ -16,11 +15,9 @@ impl JsonSchema for Url {
     }
 
     fn json_schema(_: &mut SchemaGenerator) -> Schema {
-        SchemaObject {
-            instance_type: Some(InstanceType::String.into()),
-            format: Some("uri".to_owned()),
-            ..Default::default()
-        }
-        .into()
+        json_schema!({
+            "type": "string",
+            "format": "uri",
+        })
     }
 }
diff --git a/schemars/src/json_schema_impls/uuid08.rs b/schemars/src/json_schema_impls/uuid08.rs
deleted file mode 100644
index b3b18f8..0000000
--- a/schemars/src/json_schema_impls/uuid08.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
-use std::borrow::Cow;
-use uuid08::Uuid;
-
-impl JsonSchema for Uuid {
-    no_ref_schema!();
-
-    fn schema_name() -> String {
-        "Uuid".to_string()
-    }
-
-    fn schema_id() -> Cow<'static, str> {
-        Cow::Borrowed("uuid::Uuid")
-    }
-
-    fn json_schema(_: &mut SchemaGenerator) -> Schema {
-        SchemaObject {
-            instance_type: Some(InstanceType::String.into()),
-            format: Some("uuid".to_string()),
-            ..Default::default()
-        }
-        .into()
-    }
-}
diff --git a/schemars/src/json_schema_impls/uuid1.rs b/schemars/src/json_schema_impls/uuid1.rs
index 2e0c6e9..825f7a2 100644
--- a/schemars/src/json_schema_impls/uuid1.rs
+++ b/schemars/src/json_schema_impls/uuid1.rs
@@ -1,6 +1,5 @@
 use crate::gen::SchemaGenerator;
-use crate::schema::*;
-use crate::JsonSchema;
+use crate::{json_schema, JsonSchema, Schema};
 use std::borrow::Cow;
 use uuid1::Uuid;
 
@@ -16,11 +15,9 @@ impl JsonSchema for Uuid {
     }
 
     fn json_schema(_: &mut SchemaGenerator) -> Schema {
-        SchemaObject {
-            instance_type: Some(InstanceType::String.into()),
-            format: Some("uuid".to_string()),
-            ..Default::default()
-        }
-        .into()
+        json_schema!({
+            "type": "string",
+            "format": "uuid",
+        })
     }
 }
diff --git a/schemars/src/json_schema_impls/wrapper.rs b/schemars/src/json_schema_impls/wrapper.rs
index 243a06b..e7e233c 100644
--- a/schemars/src/json_schema_impls/wrapper.rs
+++ b/schemars/src/json_schema_impls/wrapper.rs
@@ -1,5 +1,3 @@
-use crate::gen::SchemaGenerator;
-use crate::schema::Schema;
 use crate::JsonSchema;
 
 macro_rules! wrapper_impl {
diff --git a/schemars/src/lib.rs b/schemars/src/lib.rs
index f2332e5..fbe5f1d 100644
--- a/schemars/src/lib.rs
+++ b/schemars/src/lib.rs
@@ -1,32 +1,9 @@
-#![forbid(unsafe_code)]
+#![deny(unsafe_code)]
 #![doc = include_str!("../README.md")]
 
-/// The map type used by schemars types.
-///
-/// Currently a `BTreeMap` or `IndexMap` can be used, but this may change to a different implementation
-/// with a similar interface in a future version of schemars.
-/// The `IndexMap` will be used when the `preserve_order` feature flag is set.
-#[cfg(not(feature = "preserve_order"))]
-pub type Map<K, V> = std::collections::BTreeMap<K, V>;
-#[cfg(feature = "preserve_order")]
-pub type Map<K, V> = indexmap::IndexMap<K, V>;
-/// The set type used by schemars types.
-///
-/// Currently a `BTreeSet`, but this may change to a different implementation
-/// with a similar interface in a future version of schemars.
-pub type Set<T> = std::collections::BTreeSet<T>;
-
-/// A view into a single entry in a map, which may either be vacant or occupied.
-//
-/// This is constructed from the `entry` method on `BTreeMap` or `IndexMap`,
-/// depending on whether the `preserve_order` feature flag is set.
-#[cfg(not(feature = "preserve_order"))]
-pub type MapEntry<'a, K, V> = std::collections::btree_map::Entry<'a, K, V>;
-#[cfg(feature = "preserve_order")]
-pub type MapEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>;
-
 mod flatten;
 mod json_schema_impls;
+mod schema;
 mod ser;
 #[macro_use]
 mod macros;
@@ -36,7 +13,6 @@ mod macros;
 #[doc(hidden)]
 pub mod _private;
 pub mod gen;
-pub mod schema;
 pub mod visit;
 
 #[cfg(feature = "schemars_derive")]
@@ -50,7 +26,7 @@ pub use schemars_derive::*;
 #[doc(hidden)]
 pub use serde_json as _serde_json;
 
-use schema::Schema;
+pub use schema::Schema;
 
 /// A type which can be described as a JSON Schema document.
 ///
@@ -75,7 +51,7 @@ use schema::Schema;
 /// you will need to determine an appropriate name and ID for the type.
 /// For non-generic types, the type name/path are suitable for this:
 /// ```
-/// use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
+/// use schemars::{gen::SchemaGenerator, Schema, JsonSchema};
 /// use std::borrow::Cow;
 ///
 /// struct NonGenericType;
@@ -101,7 +77,7 @@ use schema::Schema;
 ///
 /// But generic type parameters which may affect the generated schema should typically be included in the name/ID:
 /// ```
-/// use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
+/// use schemars::{gen::SchemaGenerator, Schema, JsonSchema};
 /// use std::{borrow::Cow, marker::PhantomData};
 ///
 /// struct GenericType<T>(PhantomData<T>);
@@ -175,24 +151,3 @@ pub trait JsonSchema {
         false
     }
 }
-
-#[cfg(test)]
-pub mod tests {
-    use super::*;
-
-    pub fn schema_object_for<T: JsonSchema>() -> schema::SchemaObject {
-        schema_object(schema_for::<T>())
-    }
-
-    pub fn schema_for<T: JsonSchema>() -> schema::Schema {
-        let mut gen = gen::SchemaGenerator::default();
-        T::json_schema(&mut gen)
-    }
-
-    pub fn schema_object(schema: schema::Schema) -> schema::SchemaObject {
-        match schema {
-            schema::Schema::Object(o) => o,
-            s => panic!("Schema was not an object: {:?}", s),
-        }
-    }
-}
diff --git a/schemars/src/macros.rs b/schemars/src/macros.rs
index 18a6810..baa144d 100644
--- a/schemars/src/macros.rs
+++ b/schemars/src/macros.rs
@@ -76,3 +76,19 @@ macro_rules! schema_for_value {
             .unwrap()
     };
 }
+
+// TODO doc
+#[macro_export]
+macro_rules! json_schema {
+    (
+        {$($json_object:tt)*}
+    ) => {
+        $crate::Schema::try_from($crate::_serde_json::json!({$($json_object)*})).unwrap()
+    };
+    (true) => {
+        $crate::Schema::from(true)
+    };
+    (false) => {
+        $crate::Schema::from(false)
+    };
+}
diff --git a/schemars/src/schema.rs b/schemars/src/schema.rs
index 4bef66e..6a88158 100644
--- a/schemars/src/schema.rs
+++ b/schemars/src/schema.rs
@@ -2,550 +2,229 @@
 JSON Schema types.
 */
 
-#[cfg(feature = "impl_json_schema")]
-use crate as schemars;
-#[cfg(feature = "impl_json_schema")]
-use crate::JsonSchema;
-use crate::{Map, Set};
+use ref_cast::ref_cast_custom;
+use ref_cast::RefCastCustom;
 use serde::{Deserialize, Serialize};
-use serde_json::Value;
-use std::ops::Deref;
+use serde_json::{Map, Value};
 
 /// A JSON Schema.
-#[allow(clippy::large_enum_variant)]
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(untagged)]
-pub enum Schema {
-    /// A trivial boolean JSON Schema.
-    ///
-    /// The schema `true` matches everything (always passes validation), whereas the schema `false`
-    /// matches nothing (always fails validation).
-    Bool(bool),
-    /// A JSON Schema object.
-    Object(SchemaObject),
+#[derive(Debug, Clone, PartialEq, RefCastCustom)]
+#[repr(transparent)]
+pub struct Schema(Value);
+
+impl<'de> Deserialize<'de> for Schema {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let value = Value::deserialize(deserializer)?;
+        Schema::validate(&value)?;
+        Ok(Schema(value))
+    }
 }
 
-impl Schema {
-    /// Creates a new `$ref` schema.
-    ///
-    /// The given reference string should be a URI reference. This will usually be a JSON Pointer
-    /// in [URI Fragment representation](https://tools.ietf.org/html/rfc6901#section-6).
-    pub fn new_ref(reference: String) -> Self {
-        SchemaObject::new_ref(reference).into()
+impl Serialize for Schema {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        ser::OrderedKeywordWrapper(&self.0).serialize(serializer)
     }
+}
 
-    /// Returns `true` if `self` is a `$ref` schema.
-    ///
-    /// If `self` is a [`SchemaObject`] with `Some` [`reference`](struct.SchemaObject.html#structfield.reference) set, this returns `true`.
-    /// Otherwise, returns `false`.
-    pub fn is_ref(&self) -> bool {
-        match self {
-            Schema::Object(o) => o.is_ref(),
-            _ => false,
-        }
+impl Schema {
+    pub fn new() -> Self {
+        Self(Value::Object(Map::new()))
     }
 
-    /// Converts the given schema (if it is a boolean schema) into an equivalent schema object.
-    ///
-    /// If the given schema is already a schema object, this has no effect.
-    ///
-    /// # Example
-    /// ```
-    /// use schemars::schema::{Schema, SchemaObject};
-    ///
-    /// let bool_schema = Schema::Bool(true);
-    ///
-    /// assert_eq!(bool_schema.into_object(), SchemaObject::default());
-    /// ```
-    pub fn into_object(self) -> SchemaObject {
-        match self {
-            Schema::Object(o) => o,
-            Schema::Bool(true) => SchemaObject::default(),
-            Schema::Bool(false) => SchemaObject {
-                subschemas: Some(Box::new(SubschemaValidation {
-                    not: Some(Schema::Object(Default::default()).into()),
-                    ..Default::default()
-                })),
-                ..Default::default()
-            },
-        }
+    pub fn new_ref(reference: String) -> Self {
+        let mut map = Map::new();
+        map.insert("$ref".to_owned(), Value::String(reference));
+        Self(Value::Object(map))
     }
-}
 
-impl From<SchemaObject> for Schema {
-    fn from(o: SchemaObject) -> Self {
-        Schema::Object(o)
+    pub fn as_value(&self) -> &Value {
+        &self.0
     }
-}
 
-impl From<bool> for Schema {
-    fn from(b: bool) -> Self {
-        Schema::Bool(b)
+    pub fn as_bool(&self) -> Option<bool> {
+        self.0.as_bool()
     }
-}
 
-/// The root object of a JSON Schema document.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(rename_all = "camelCase", default)]
-pub struct RootSchema {
-    /// The `$schema` keyword.
-    ///
-    /// See [JSON Schema 8.1.1. The "$schema" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.1.1).
-    #[serde(rename = "$schema", skip_serializing_if = "Option::is_none")]
-    pub meta_schema: Option<String>,
-    /// The root schema itself.
-    #[serde(flatten)]
-    pub schema: SchemaObject,
-    /// The `definitions` keyword.
-    ///
-    /// In JSON Schema draft 2019-09 this was replaced by $defs, but in Schemars this is still
-    /// serialized as `definitions` for backward-compatibility.
-    ///
-    /// See [JSON Schema 8.2.5. Schema Re-Use With "$defs"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.5),
-    /// and [JSON Schema (draft 07) 9. Schema Re-Use With "definitions"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-9).
-    #[serde(alias = "$defs", skip_serializing_if = "Map::is_empty")]
-    pub definitions: Map<String, Schema>,
-}
+    pub fn as_object(&self) -> Option<&Map<String, Value>> {
+        self.0.as_object()
+    }
 
-/// A JSON Schema object.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(rename_all = "camelCase", default)]
-pub struct SchemaObject {
-    /// Properties which annotate the [`SchemaObject`] which typically have no effect when an object is being validated against the schema.
-    #[serde(flatten, deserialize_with = "skip_if_default")]
-    pub metadata: Option<Box<Metadata>>,
-    /// The `type` keyword.
-    ///
-    /// See [JSON Schema Validation 6.1.1. "type"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.1)
-    /// and [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).
-    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
-    pub instance_type: Option<SingleOrVec<InstanceType>>,
-    /// The `format` keyword.
-    ///
-    /// See [JSON Schema Validation 7. A Vocabulary for Semantic Content With "format"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-7).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub format: Option<String>,
-    /// The `enum` keyword.
-    ///
-    /// See [JSON Schema Validation 6.1.2. "enum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.2)
-    #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
-    pub enum_values: Option<Vec<Value>>,
-    /// The `const` keyword.
-    ///
-    /// See [JSON Schema Validation 6.1.3. "const"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.1.3)
-    #[serde(
-        rename = "const",
-        skip_serializing_if = "Option::is_none",
-        deserialize_with = "allow_null"
-    )]
-    pub const_value: Option<Value>,
-    /// Properties of the [`SchemaObject`] which define validation assertions in terms of other schemas.
-    #[serde(flatten, deserialize_with = "skip_if_default")]
-    pub subschemas: Option<Box<SubschemaValidation>>,
-    /// Properties of the [`SchemaObject`] which define validation assertions for numbers.
-    #[serde(flatten, deserialize_with = "skip_if_default")]
-    pub number: Option<Box<NumberValidation>>,
-    /// Properties of the [`SchemaObject`] which define validation assertions for strings.
-    #[serde(flatten, deserialize_with = "skip_if_default")]
-    pub string: Option<Box<StringValidation>>,
-    /// Properties of the [`SchemaObject`] which define validation assertions for arrays.
-    #[serde(flatten, deserialize_with = "skip_if_default")]
-    pub array: Option<Box<ArrayValidation>>,
-    /// Properties of the [`SchemaObject`] which define validation assertions for objects.
-    #[serde(flatten, deserialize_with = "skip_if_default")]
-    pub object: Option<Box<ObjectValidation>>,
-    /// The `$ref` keyword.
-    ///
-    /// See [JSON Schema 8.2.4.1. Direct References with "$ref"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.4.1).
-    #[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
-    pub reference: Option<String>,
-    /// Arbitrary extra properties which are not part of the JSON Schema specification, or which `schemars` does not support.
-    #[serde(flatten)]
-    pub extensions: Map<String, Value>,
-}
+    pub fn as_object_mut(&mut self) -> Option<&mut Map<String, Value>> {
+        self.0.as_object_mut()
+    }
 
-// Deserializing "null" to `Option<Value>` directly results in `None`,
-// this function instead makes it deserialize to `Some(Value::Null)`.
-fn allow_null<'de, D>(de: D) -> Result<Option<Value>, D::Error>
-where
-    D: serde::Deserializer<'de>,
-{
-    Value::deserialize(de).map(Option::Some)
-}
+    pub(crate) fn try_to_object(self) -> Result<Map<String, Value>, bool> {
+        match self.0 {
+            Value::Object(m) => Ok(m),
+            Value::Bool(b) => Err(b),
+            _ => unreachable!(),
+        }
+    }
 
-fn skip_if_default<'de, D, T>(deserializer: D) -> Result<Option<Box<T>>, D::Error>
-where
-    D: serde::Deserializer<'de>,
-    T: Deserialize<'de> + Default + PartialEq,
-{
-    let value = T::deserialize(deserializer)?;
-    if value == T::default() {
-        Ok(None)
-    } else {
-        Ok(Some(Box::new(value)))
+    pub fn to_value(self) -> Value {
+        self.0
     }
-}
 
-macro_rules! get_or_insert_default_fn {
-    ($name:ident, $ret:ty) => {
-        get_or_insert_default_fn!(
-            concat!(
-                "Returns a mutable reference to this schema's [`",
-                stringify!($ret),
-                "`](#structfield.",
-                stringify!($name),
-                "), creating it if it was `None`."
-            ),
-            $name,
-            $ret
-        );
-    };
-    ($doc:expr, $name:ident, $ret:ty) => {
-        #[doc = $doc]
-        pub fn $name(&mut self) -> &mut $ret {
-            self.$name.get_or_insert_with(Default::default)
+    pub fn ensure_object(&mut self) -> &mut Map<String, Value> {
+        if let Some(b) = self.as_bool() {
+            let mut map = Map::new();
+            if !b {
+                map.insert("not".into(), Value::Object(Map::new()));
+            }
+            self.0 = Value::Object(map);
         }
-    };
-}
 
-impl SchemaObject {
-    /// Creates a new `$ref` schema.
-    ///
-    /// The given reference string should be a URI reference. This will usually be a JSON Pointer
-    /// in [URI Fragment representation](https://tools.ietf.org/html/rfc6901#section-6).
-    pub fn new_ref(reference: String) -> Self {
-        SchemaObject {
-            reference: Some(reference),
-            ..Default::default()
-        }
+        self.as_object_mut()
+            .expect("Schema value should be of type Object.")
     }
 
-    /// Returns `true` if `self` is a `$ref` schema.
-    ///
-    /// If `self` has `Some` [`reference`](struct.SchemaObject.html#structfield.reference) set, this returns `true`.
-    /// Otherwise, returns `false`.
-    pub fn is_ref(&self) -> bool {
-        self.reference.is_some()
+    pub(crate) fn has_type(&self, ty: &str) -> bool {
+        match self.0.get("type") {
+            Some(Value::Array(values)) => values.iter().any(|v| v.as_str() == Some(ty)),
+            Some(Value::String(s)) => s == ty,
+            _ => false,
+        }
     }
 
-    /// Returns `true` if `self` accepts values of the given type, according to the [`instance_type`](struct.SchemaObject.html#structfield.instance_type) field.
-    ///
-    /// This is a basic check that always returns `true` if no `instance_type` is specified on the schema,
-    /// and does not check any subschemas. Because of this, both `{}` and  `{"not": {}}` accept any type according
-    /// to this method.
-    pub fn has_type(&self, ty: InstanceType) -> bool {
-        self.instance_type
-            .as_ref()
-            .map_or(true, |x| x.contains(&ty))
+    fn validate<E: serde::de::Error>(value: &Value) -> Result<(), E> {
+        use serde::de::Unexpected;
+        let unexpected = match value {
+            Value::Bool(_) | Value::Object(_) => return Ok(()),
+            Value::Null => Unexpected::Unit,
+            Value::Number(n) => {
+                if let Some(u) = n.as_u64() {
+                    Unexpected::Unsigned(u)
+                } else if let Some(i) = n.as_i64() {
+                    Unexpected::Signed(i)
+                } else if let Some(f) = n.as_f64() {
+                    Unexpected::Float(f)
+                } else {
+                    unreachable!()
+                }
+            }
+            Value::String(s) => Unexpected::Str(s),
+            Value::Array(_) => Unexpected::Seq,
+        };
+
+        Err(E::invalid_type(unexpected, &"object or boolean"))
     }
 
-    get_or_insert_default_fn!(metadata, Metadata);
-    get_or_insert_default_fn!(subschemas, SubschemaValidation);
-    get_or_insert_default_fn!(number, NumberValidation);
-    get_or_insert_default_fn!(string, StringValidation);
-    get_or_insert_default_fn!(array, ArrayValidation);
-    get_or_insert_default_fn!(object, ObjectValidation);
-}
-
-impl From<Schema> for SchemaObject {
-    fn from(schema: Schema) -> Self {
-        schema.into_object()
-    }
-}
+    #[allow(unsafe_code)]
+    #[ref_cast_custom]
+    fn ref_cast(value: &Value) -> &Self;
 
-/// Properties which annotate a [`SchemaObject`] which typically have no effect when an object is being validated against the schema.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(rename_all = "camelCase", default)]
-pub struct Metadata {
-    /// The `$id` keyword.
-    ///
-    /// See [JSON Schema 8.2.2. The "$id" Keyword](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-8.2.2).
-    #[serde(rename = "$id", skip_serializing_if = "Option::is_none")]
-    pub id: Option<String>,
-    /// The `title` keyword.
-    ///
-    /// See [JSON Schema Validation 9.1. "title" and "description"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub title: Option<String>,
-    /// The `description` keyword.
-    ///
-    /// See [JSON Schema Validation 9.1. "title" and "description"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.1).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub description: Option<String>,
-    /// The `default` keyword.
-    ///
-    /// See [JSON Schema Validation 9.2. "default"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.2).
-    #[serde(
-        skip_serializing_if = "Option::is_none",
-        deserialize_with = "allow_null"
-    )]
-    pub default: Option<Value>,
-    /// The `deprecated` keyword.
-    ///
-    /// See [JSON Schema Validation 9.3. "deprecated"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.3).
-    #[serde(skip_serializing_if = "is_false")]
-    pub deprecated: bool,
-    /// The `readOnly` keyword.
-    ///
-    /// See [JSON Schema Validation 9.4. "readOnly" and "writeOnly"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).
-    #[serde(skip_serializing_if = "is_false")]
-    pub read_only: bool,
-    /// The `writeOnly` keyword.
-    ///
-    /// See [JSON Schema Validation 9.4. "readOnly" and "writeOnly"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.4).
-    #[serde(skip_serializing_if = "is_false")]
-    pub write_only: bool,
-    /// The `examples` keyword.
-    ///
-    /// See [JSON Schema Validation 9.5. "examples"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-9.5).
-    #[serde(skip_serializing_if = "Vec::is_empty")]
-    pub examples: Vec<Value>,
+    #[allow(unsafe_code)]
+    #[ref_cast_custom]
+    fn ref_cast_mut(value: &mut Value) -> &mut Self;
 }
 
-#[allow(clippy::trivially_copy_pass_by_ref)]
-fn is_false(b: &bool) -> bool {
-    !b
+impl From<Schema> for Value {
+    fn from(v: Schema) -> Value {
+        v.0
+    }
 }
 
-/// Properties of a [`SchemaObject`] which define validation assertions in terms of other schemas.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(rename_all = "camelCase", default)]
-pub struct SubschemaValidation {
-    /// The `allOf` keyword.
-    ///
-    /// See [JSON Schema 9.2.1.1. "allOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.1).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub all_of: Option<Vec<Schema>>,
-    /// The `anyOf` keyword.
-    ///
-    /// See [JSON Schema 9.2.1.2. "anyOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.2).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub any_of: Option<Vec<Schema>>,
-    /// The `oneOf` keyword.
-    ///
-    /// See [JSON Schema 9.2.1.3. "oneOf"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.3).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub one_of: Option<Vec<Schema>>,
-    /// The `not` keyword.
-    ///
-    /// See [JSON Schema 9.2.1.4. "not"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.1.4).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub not: Option<Box<Schema>>,
-    /// The `if` keyword.
-    ///
-    /// See [JSON Schema 9.2.2.1. "if"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.1).
-    #[serde(rename = "if", skip_serializing_if = "Option::is_none")]
-    pub if_schema: Option<Box<Schema>>,
-    /// The `then` keyword.
-    ///
-    /// See [JSON Schema 9.2.2.2. "then"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.2).
-    #[serde(rename = "then", skip_serializing_if = "Option::is_none")]
-    pub then_schema: Option<Box<Schema>>,
-    /// The `else` keyword.
-    ///
-    /// See [JSON Schema 9.2.2.3. "else"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.2.2.3).
-    #[serde(rename = "else", skip_serializing_if = "Option::is_none")]
-    pub else_schema: Option<Box<Schema>>,
-}
+impl std::convert::TryFrom<Value> for Schema {
+    type Error = serde_json::Error;
 
-/// Properties of a [`SchemaObject`] which define validation assertions for numbers.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(rename_all = "camelCase", default)]
-pub struct NumberValidation {
-    /// The `multipleOf` keyword.
-    ///
-    /// See [JSON Schema Validation 6.2.1. "multipleOf"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.1).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub multiple_of: Option<f64>,
-    /// The `maximum` keyword.
-    ///
-    /// See [JSON Schema Validation 6.2.2. "maximum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.2).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub maximum: Option<f64>,
-    /// The `exclusiveMaximum` keyword.
-    ///
-    /// See [JSON Schema Validation 6.2.3. "exclusiveMaximum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.3).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub exclusive_maximum: Option<f64>,
-    /// The `minimum` keyword.
-    ///
-    /// See [JSON Schema Validation 6.2.4. "minimum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.4).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub minimum: Option<f64>,
-    /// The `exclusiveMinimum` keyword.
-    ///
-    /// See [JSON Schema Validation 6.2.5. "exclusiveMinimum"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.2.5).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub exclusive_minimum: Option<f64>,
+    fn try_from(value: Value) -> serde_json::Result<Schema> {
+        Schema::validate(&value)?;
+        Ok(Schema(value))
+    }
 }
 
-/// Properties of a [`SchemaObject`] which define validation assertions for strings.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(rename_all = "camelCase", default)]
-pub struct StringValidation {
-    /// The `maxLength` keyword.
-    ///
-    /// See [JSON Schema Validation 6.3.1. "maxLength"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.1).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub max_length: Option<u32>,
-    /// The `minLength` keyword.
-    ///
-    /// See [JSON Schema Validation 6.3.2. "minLength"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.2).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub min_length: Option<u32>,
-    /// The `pattern` keyword.
-    ///
-    /// See [JSON Schema Validation 6.3.3. "pattern"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.3.3).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub pattern: Option<String>,
-}
+impl<'a> std::convert::TryFrom<&'a Value> for &'a Schema {
+    type Error = serde_json::Error;
 
-/// Properties of a [`SchemaObject`] which define validation assertions for arrays.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(rename_all = "camelCase", default)]
-pub struct ArrayValidation {
-    /// The `items` keyword.
-    ///
-    /// See [JSON Schema 9.3.1.1. "items"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.1).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub items: Option<SingleOrVec<Schema>>,
-    /// The `additionalItems` keyword.
-    ///
-    /// See [JSON Schema 9.3.1.2. "additionalItems"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.2).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub additional_items: Option<Box<Schema>>,
-    /// The `maxItems` keyword.
-    ///
-    /// See [JSON Schema Validation 6.4.1. "maxItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.1).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub max_items: Option<u32>,
-    /// The `minItems` keyword.
-    ///
-    /// See [JSON Schema Validation 6.4.2. "minItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.2).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub min_items: Option<u32>,
-    /// The `uniqueItems` keyword.
-    ///
-    /// See [JSON Schema Validation 6.4.3. "uniqueItems"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.4.3).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub unique_items: Option<bool>,
-    /// The `contains` keyword.
-    ///
-    /// See [JSON Schema 9.3.1.4. "contains"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.1.4).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub contains: Option<Box<Schema>>,
+    fn try_from(value: &Value) -> serde_json::Result<&Schema> {
+        Schema::validate(value)?;
+        Ok(Schema::ref_cast(value))
+    }
 }
 
-/// Properties of a [`SchemaObject`] which define validation assertions for objects.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(rename_all = "camelCase", default)]
-pub struct ObjectValidation {
-    /// The `maxProperties` keyword.
-    ///
-    /// See [JSON Schema Validation 6.5.1. "maxProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.1).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub max_properties: Option<u32>,
-    /// The `minProperties` keyword.
-    ///
-    /// See [JSON Schema Validation 6.5.2. "minProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.2).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub min_properties: Option<u32>,
-    /// The `required` keyword.
-    ///
-    /// See [JSON Schema Validation 6.5.3. "required"](https://tools.ietf.org/html/draft-handrews-json-schema-validation-02#section-6.5.3).
-    #[serde(skip_serializing_if = "Set::is_empty")]
-    pub required: Set<String>,
-    /// The `properties` keyword.
-    ///
-    /// See [JSON Schema 9.3.2.1. "properties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.1).
-    #[serde(skip_serializing_if = "Map::is_empty")]
-    pub properties: Map<String, Schema>,
-    /// The `patternProperties` keyword.
-    ///
-    /// See [JSON Schema 9.3.2.2. "patternProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.2).
-    #[serde(skip_serializing_if = "Map::is_empty")]
-    pub pattern_properties: Map<String, Schema>,
-    /// The `additionalProperties` keyword.
-    ///
-    /// See [JSON Schema 9.3.2.3. "additionalProperties"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.3).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub additional_properties: Option<Box<Schema>>,
-    /// The `propertyNames` keyword.
-    ///
-    /// See [JSON Schema 9.3.2.5. "propertyNames"](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-9.3.2.5).
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub property_names: Option<Box<Schema>>,
-}
+impl<'a> std::convert::TryFrom<&'a mut Value> for &'a mut Schema {
+    type Error = serde_json::Error;
 
-/// The possible types of values in JSON Schema documents.
-///
-/// See [JSON Schema 4.2.1. Instance Data Model](https://tools.ietf.org/html/draft-handrews-json-schema-02#section-4.2.1).
-#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(rename_all = "camelCase")]
-pub enum InstanceType {
-    Null,
-    Boolean,
-    Object,
-    Array,
-    Number,
-    String,
-    Integer,
+    fn try_from(value: &mut Value) -> serde_json::Result<&mut Schema> {
+        Schema::validate(value)?;
+        Ok(Schema::ref_cast_mut(value))
+    }
 }
 
-/// A type which can be serialized as a single item, or multiple items.
-///
-/// In some contexts, a `Single` may be semantically distinct from a `Vec` containing only item.
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
-#[cfg_attr(feature = "impl_json_schema", derive(JsonSchema))]
-#[serde(untagged)]
-pub enum SingleOrVec<T> {
-    Single(Box<T>),
-    Vec(Vec<T>),
+impl Default for Schema {
+    fn default() -> Self {
+        Self(Value::Object(Map::new()))
+    }
 }
 
-impl<T> From<T> for SingleOrVec<T> {
-    fn from(single: T) -> Self {
-        SingleOrVec::Single(Box::new(single))
+impl From<Map<String, Value>> for Schema {
+    fn from(o: Map<String, Value>) -> Self {
+        Schema(Value::Object(o))
     }
 }
 
-impl<T> From<Vec<T>> for SingleOrVec<T> {
-    fn from(vec: Vec<T>) -> Self {
-        SingleOrVec::Vec(vec)
+impl From<bool> for Schema {
+    fn from(b: bool) -> Self {
+        Schema(Value::Bool(b))
     }
 }
 
-impl<T: PartialEq> SingleOrVec<T> {
-    /// Returns `true` if `self` is either a `Single` equal to `x`, or a `Vec` containing `x`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use schemars::schema::SingleOrVec;
-    ///
-    /// let s = SingleOrVec::from(10);
-    /// assert!(s.contains(&10));
-    /// assert!(!s.contains(&20));
-    ///
-    /// let v = SingleOrVec::from(vec![10, 20]);
-    /// assert!(v.contains(&10));
-    /// assert!(v.contains(&20));
-    /// assert!(!v.contains(&30));
-    /// ```
-    pub fn contains(&self, x: &T) -> bool {
-        match self {
-            SingleOrVec::Single(s) => s.deref() == x,
-            SingleOrVec::Vec(v) => v.contains(x),
+mod ser {
+    use serde::ser::{Serialize, SerializeMap, SerializeSeq};
+    use serde_json::Value;
+
+    const ORDERED_KEYWORDS_START: [&str; 6] =
+        ["$id", "$schema", "title", "description", "type", "format"];
+    const ORDERED_KEYWORDS_END: [&str; 2] = ["$defs", "definitions"];
+
+    pub(super) struct OrderedKeywordWrapper<'a>(pub &'a Value);
+
+    impl Serialize for OrderedKeywordWrapper<'_> {
+        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: serde::Serializer,
+        {
+            match self.0 {
+                Value::Array(array) => {
+                    let mut seq = serializer.serialize_seq(Some(array.len()))?;
+                    for value in array {
+                        seq.serialize_element(&OrderedKeywordWrapper(value))?;
+                    }
+                    seq.end()
+                }
+                Value::Object(object) => {
+                    let mut map = serializer.serialize_map(Some(object.len()))?;
+
+                    for key in ORDERED_KEYWORDS_START {
+                        if let Some(value) = object.get(key) {
+                            map.serialize_entry(key, &OrderedKeywordWrapper(value))?;
+                        }
+                    }
+
+                    for (key, value) in object {
+                        if !ORDERED_KEYWORDS_START.contains(&key.as_str())
+                            && !ORDERED_KEYWORDS_END.contains(&key.as_str())
+                        {
+                            map.serialize_entry(key, &OrderedKeywordWrapper(value))?;
+                        }
+                    }
+
+                    for key in ORDERED_KEYWORDS_END {
+                        if let Some(value) = object.get(key) {
+                            map.serialize_entry(key, &OrderedKeywordWrapper(value))?;
+                        }
+                    }
+
+                    map.end()
+                }
+                _ => self.0.serialize(serializer),
+            }
         }
     }
 }
diff --git a/schemars/src/ser.rs b/schemars/src/ser.rs
index 3f69bef..8be7b34 100644
--- a/schemars/src/ser.rs
+++ b/schemars/src/ser.rs
@@ -1,8 +1,7 @@
-use crate::schema::*;
-use crate::JsonSchema;
-use crate::{gen::SchemaGenerator, Map};
-use serde_json::{Error, Value};
-use std::{convert::TryInto, fmt::Display};
+use crate::gen::SchemaGenerator;
+use crate::{json_schema, JsonSchema, Schema};
+use serde_json::{Error, Map, Value};
+use std::fmt::Display;
 
 pub(crate) struct Serializer<'a> {
     pub(crate) gen: &'a mut SchemaGenerator,
@@ -22,7 +21,7 @@ pub(crate) struct SerializeTuple<'a> {
 
 pub(crate) struct SerializeMap<'a> {
     gen: &'a mut SchemaGenerator,
-    properties: Map<String, Schema>,
+    properties: Map<String, Value>,
     current_key: Option<String>,
     title: &'static str,
 }
@@ -36,13 +35,11 @@ macro_rules! forward_to_subschema_for {
 }
 
 macro_rules! return_instance_type {
-    ($fn:ident, $ty:ty, $instance_type:ident) => {
+    ($fn:ident, $ty:ty, $instance_type:expr) => {
         fn $fn(self, _value: $ty) -> Result<Self::Ok, Self::Error> {
-            Ok(SchemaObject {
-                instance_type: Some(InstanceType::$instance_type.into()),
-                ..Default::default()
-            }
-            .into())
+            Ok(json_schema!({
+                "type": $instance_type
+            }))
         }
     };
 }
@@ -59,18 +56,18 @@ impl<'a> serde::Serializer for Serializer<'a> {
     type SerializeStruct = SerializeMap<'a>;
     type SerializeStructVariant = Self;
 
-    return_instance_type!(serialize_i8, i8, Integer);
-    return_instance_type!(serialize_i16, i16, Integer);
-    return_instance_type!(serialize_i32, i32, Integer);
-    return_instance_type!(serialize_i64, i64, Integer);
-    return_instance_type!(serialize_i128, i128, Integer);
-    return_instance_type!(serialize_u8, u8, Integer);
-    return_instance_type!(serialize_u16, u16, Integer);
-    return_instance_type!(serialize_u32, u32, Integer);
-    return_instance_type!(serialize_u64, u64, Integer);
-    return_instance_type!(serialize_u128, u128, Integer);
-    return_instance_type!(serialize_f32, f32, Number);
-    return_instance_type!(serialize_f64, f64, Number);
+    return_instance_type!(serialize_i8, i8, "integer");
+    return_instance_type!(serialize_i16, i16, "integer");
+    return_instance_type!(serialize_i32, i32, "integer");
+    return_instance_type!(serialize_i64, i64, "integer");
+    return_instance_type!(serialize_i128, i128, "integer");
+    return_instance_type!(serialize_u8, u8, "integer");
+    return_instance_type!(serialize_u16, u16, "integer");
+    return_instance_type!(serialize_u32, u32, "integer");
+    return_instance_type!(serialize_u64, u64, "integer");
+    return_instance_type!(serialize_u128, u128, "integer");
+    return_instance_type!(serialize_f32, f32, "number");
+    return_instance_type!(serialize_f64, f64, "number");
 
     forward_to_subschema_for!(serialize_bool, bool);
     forward_to_subschema_for!(serialize_char, char);
@@ -93,7 +90,7 @@ impl<'a> serde::Serializer for Serializer<'a> {
         let value_schema = iter
             .into_iter()
             .try_fold(None, |acc, (_, v)| {
-                if acc == Some(Schema::Bool(true)) {
+                if acc == Some(true.into()) {
                     return Ok(acc);
                 }
 
@@ -103,21 +100,16 @@ impl<'a> serde::Serializer for Serializer<'a> {
                 })?;
                 Ok(match &acc {
                     None => Some(schema),
-                    Some(items) if items != &schema => Some(Schema::Bool(true)),
+                    Some(items) if items != &schema => Some(true.into()),
                     _ => acc,
                 })
             })?
-            .unwrap_or(Schema::Bool(true));
-
-        Ok(SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            object: Some(Box::new(ObjectValidation {
-                additional_properties: Some(Box::new(value_schema)),
-                ..ObjectValidation::default()
-            })),
-            ..SchemaObject::default()
-        }
-        .into())
+            .unwrap_or(true.into());
+
+        Ok(json_schema!({
+            "type": "object",
+            "additionalProperties": value_schema,
+        }))
     }
 
     fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
@@ -132,52 +124,47 @@ impl<'a> serde::Serializer for Serializer<'a> {
     where
         T: serde::Serialize,
     {
-        // FIXME nasty duplication of `impl JsonSchema for Option<T>`
-        fn add_null_type(instance_type: &mut SingleOrVec<InstanceType>) {
-            match instance_type {
-                SingleOrVec::Single(ty) if **ty != InstanceType::Null => {
-                    *instance_type = vec![**ty, InstanceType::Null].into()
-                }
-                SingleOrVec::Vec(ty) if !ty.contains(&InstanceType::Null) => {
-                    ty.push(InstanceType::Null)
-                }
-                _ => {}
-            };
-        }
-
         let mut schema = value.serialize(Serializer {
             gen: self.gen,
             include_title: false,
         })?;
 
         if self.gen.settings().option_add_null_type {
-            schema = match schema {
-                Schema::Bool(true) => Schema::Bool(true),
-                Schema::Bool(false) => <()>::json_schema(self.gen),
-                Schema::Object(SchemaObject {
-                    instance_type: Some(ref mut instance_type),
-                    ..
-                }) => {
-                    add_null_type(instance_type);
-                    schema
-                }
-                schema => SchemaObject {
-                    subschemas: Some(Box::new(SubschemaValidation {
-                        any_of: Some(vec![schema, <()>::json_schema(self.gen)]),
-                        ..Default::default()
-                    })),
-                    ..Default::default()
+            schema = match schema.try_to_object() {
+                Ok(mut obj) => {
+                    let value = obj.get_mut("type");
+                    match value {
+                        Some(Value::Array(array)) => {
+                            let null = Value::from("null");
+                            if !array.contains(&null) {
+                                array.push(null);
+                            }
+                            obj.into()
+                        }
+                        Some(Value::String(string)) => {
+                            if string != "null" {
+                                *value.unwrap() =
+                                    Value::Array(vec![std::mem::take(string).into(), "null".into()])
+                            }
+                            obj.into()
+                        }
+                        _ => json_schema!({
+                            "anyOf": [
+                                obj,
+                                <()>::json_schema(self.gen)
+                            ]
+                        }),
+                    }
                 }
-                .into(),
+                Err(true) => true.into(),
+                Err(false) => <()>::json_schema(self.gen),
             }
         }
 
         if self.gen.settings().option_nullable {
-            let mut schema_obj = schema.into_object();
-            schema_obj
-                .extensions
-                .insert("nullable".to_owned(), serde_json::json!(true));
-            schema = Schema::Object(schema_obj);
+            schema
+                .ensure_object()
+                .insert("nullable".into(), true.into());
         };
 
         Ok(schema)
@@ -193,7 +180,7 @@ impl<'a> serde::Serializer for Serializer<'a> {
         _variant_index: u32,
         _variant: &'static str,
     ) -> Result<Self::Ok, Self::Error> {
-        Ok(Schema::Bool(true))
+        Ok(true.into())
     }
 
     fn serialize_newtype_struct<T: ?Sized>(
@@ -205,15 +192,13 @@ impl<'a> serde::Serializer for Serializer<'a> {
         T: serde::Serialize,
     {
         let include_title = self.include_title;
-        let mut result = value.serialize(self);
+        let mut schema = value.serialize(self)?;
 
-        if include_title {
-            if let Ok(Schema::Object(ref mut object)) = result {
-                object.metadata().title = Some(name.to_string());
-            }
+        if include_title && !name.is_empty() {
+            schema.ensure_object().insert("title".into(), name.into());
         }
 
-        result
+        Ok(schema)
     }
 
     fn serialize_newtype_variant<T: ?Sized>(
@@ -226,7 +211,7 @@ impl<'a> serde::Serializer for Serializer<'a> {
     where
         T: serde::Serialize,
     {
-        Ok(Schema::Bool(true))
+        Ok(true.into())
     }
 
     fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
@@ -313,7 +298,7 @@ impl serde::ser::SerializeTupleVariant for Serializer<'_> {
     }
 
     fn end(self) -> Result<Self::Ok, Self::Error> {
-        Ok(Schema::Bool(true))
+        Ok(true.into())
     }
 }
 
@@ -333,7 +318,7 @@ impl serde::ser::SerializeStructVariant for Serializer<'_> {
     }
 
     fn end(self) -> Result<Self::Ok, Self::Error> {
-        Ok(Schema::Bool(true))
+        Ok(true.into())
     }
 }
 
@@ -345,7 +330,7 @@ impl serde::ser::SerializeSeq for SerializeSeq<'_> {
     where
         T: serde::Serialize,
     {
-        if self.items != Some(Schema::Bool(true)) {
+        if self.items != Some(true.into()) {
             let schema = value.serialize(Serializer {
                 gen: self.gen,
                 include_title: false,
@@ -354,7 +339,7 @@ impl serde::ser::SerializeSeq for SerializeSeq<'_> {
                 None => self.items = Some(schema),
                 Some(items) => {
                     if items != &schema {
-                        self.items = Some(Schema::Bool(true))
+                        self.items = Some(true.into())
                     }
                 }
             }
@@ -364,16 +349,12 @@ impl serde::ser::SerializeSeq for SerializeSeq<'_> {
     }
 
     fn end(self) -> Result<Self::Ok, Self::Error> {
-        let items = self.items.unwrap_or(Schema::Bool(true));
-        Ok(SchemaObject {
-            instance_type: Some(InstanceType::Array.into()),
-            array: Some(Box::new(ArrayValidation {
-                items: Some(items.into()),
-                ..ArrayValidation::default()
-            })),
-            ..SchemaObject::default()
-        }
-        .into())
+        let items = self.items.unwrap_or(true.into());
+
+        Ok(json_schema!({
+            "type": "array",
+            "items": items
+        }))
     }
 }
 
@@ -394,23 +375,21 @@ impl serde::ser::SerializeTuple for SerializeTuple<'_> {
     }
 
     fn end(self) -> Result<Self::Ok, Self::Error> {
-        let len = self.items.len().try_into().ok();
-        let mut schema = SchemaObject {
-            instance_type: Some(InstanceType::Array.into()),
-            array: Some(Box::new(ArrayValidation {
-                items: Some(SingleOrVec::Vec(self.items)),
-                max_items: len,
-                min_items: len,
-                ..ArrayValidation::default()
-            })),
-            ..SchemaObject::default()
-        };
+        let len = self.items.len();
+        let mut schema = json_schema!({
+            "type": "array",
+            "items": self.items,
+            "maxItems": len,
+            "minItems": len,
+        });
 
         if !self.title.is_empty() {
-            schema.metadata().title = Some(self.title.to_owned());
+            schema
+                .ensure_object()
+                .insert("title".into(), self.title.into());
         }
 
-        Ok(schema.into())
+        Ok(schema)
     }
 }
 
@@ -459,26 +438,24 @@ impl serde::ser::SerializeMap for SerializeMap<'_> {
             gen: self.gen,
             include_title: false,
         })?;
-        self.properties.insert(key, schema);
+        self.properties.insert(key, schema.into());
 
         Ok(())
     }
 
     fn end(self) -> Result<Self::Ok, Self::Error> {
-        let mut schema = SchemaObject {
-            instance_type: Some(InstanceType::Object.into()),
-            object: Some(Box::new(ObjectValidation {
-                properties: self.properties,
-                ..ObjectValidation::default()
-            })),
-            ..SchemaObject::default()
-        };
+        let mut schema = json_schema!({
+            "type": "object",
+            "properties": self.properties,
+        });
 
         if !self.title.is_empty() {
-            schema.metadata().title = Some(self.title.to_owned());
+            schema
+                .ensure_object()
+                .insert("title".into(), self.title.into());
         }
 
-        Ok(schema.into())
+        Ok(schema)
     }
 }
 
@@ -498,7 +475,7 @@ impl serde::ser::SerializeStruct for SerializeMap<'_> {
             gen: self.gen,
             include_title: false,
         })?;
-        self.properties.insert(key.to_string(), prop_schema);
+        self.properties.insert(key.to_string(), prop_schema.into());
 
         Ok(())
     }
diff --git a/schemars/src/visit.rs b/schemars/src/visit.rs
index 182b572..cfe11df 100644
--- a/schemars/src/visit.rs
+++ b/schemars/src/visit.rs
@@ -7,116 +7,87 @@ All methods of `Visitor` have a default implementation that makes no change but
 When overriding one of these methods, you will *usually* want to still call this default implementation.
 
 # Example
-To add a custom property to all schemas:
+To add a custom property to all object schemas:
 ```
-use schemars::schema::SchemaObject;
-use schemars::visit::{Visitor, visit_schema_object};
+use schemars::Schema;
+use schemars::visit::{Visitor, visit_schema};
 
 pub struct MyVisitor;
 
 impl Visitor for MyVisitor {
-    fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
+    fn visit_schema(&mut self, schema: &mut Schema) {
         // First, make our change to this schema
-        schema.extensions.insert("my_property".to_string(), serde_json::json!("hello world"));
+        if let Some(obj) = schema.as_object_mut() {
+            obj.insert("my_property".to_string(), serde_json::json!("hello world"));
+        }
 
         // Then delegate to default implementation to visit any subschemas
-        visit_schema_object(self, schema);
+        visit_schema(self, schema);
     }
 }
 ```
 */
-use crate::schema::{RootSchema, Schema, SchemaObject, SingleOrVec};
+use serde_json::{json, Value};
+
+use crate::Schema;
 
 /// Trait used to recursively modify a constructed schema and its subschemas.
 pub trait Visitor {
-    /// Override this method to modify a [`RootSchema`] and (optionally) its subschemas.
-    ///
-    /// When overriding this method, you will usually want to call the [`visit_root_schema`] function to visit subschemas.
-    fn visit_root_schema(&mut self, root: &mut RootSchema) {
-        visit_root_schema(self, root)
-    }
-
     /// Override this method to modify a [`Schema`] and (optionally) its subschemas.
     ///
     /// When overriding this method, you will usually want to call the [`visit_schema`] function to visit subschemas.
     fn visit_schema(&mut self, schema: &mut Schema) {
         visit_schema(self, schema)
     }
-
-    /// Override this method to modify a [`SchemaObject`] and (optionally) its subschemas.
-    ///
-    /// When overriding this method, you will usually want to call the [`visit_schema_object`] function to visit subschemas.
-    fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
-        visit_schema_object(self, schema)
-    }
-}
-
-/// Visits all subschemas of the [`RootSchema`].
-pub fn visit_root_schema<V: Visitor + ?Sized>(v: &mut V, root: &mut RootSchema) {
-    v.visit_schema_object(&mut root.schema);
-    visit_map_values(v, &mut root.definitions);
 }
 
 /// Visits all subschemas of the [`Schema`].
 pub fn visit_schema<V: Visitor + ?Sized>(v: &mut V, schema: &mut Schema) {
-    if let Schema::Object(schema) = schema {
-        v.visit_schema_object(schema)
-    }
-}
-
-/// Visits all subschemas of the [`SchemaObject`].
-pub fn visit_schema_object<V: Visitor + ?Sized>(v: &mut V, schema: &mut SchemaObject) {
-    if let Some(sub) = &mut schema.subschemas {
-        visit_vec(v, &mut sub.all_of);
-        visit_vec(v, &mut sub.any_of);
-        visit_vec(v, &mut sub.one_of);
-        visit_box(v, &mut sub.not);
-        visit_box(v, &mut sub.if_schema);
-        visit_box(v, &mut sub.then_schema);
-        visit_box(v, &mut sub.else_schema);
-    }
-
-    if let Some(arr) = &mut schema.array {
-        visit_single_or_vec(v, &mut arr.items);
-        visit_box(v, &mut arr.additional_items);
-        visit_box(v, &mut arr.contains);
-    }
-
-    if let Some(obj) = &mut schema.object {
-        visit_map_values(v, &mut obj.properties);
-        visit_map_values(v, &mut obj.pattern_properties);
-        visit_box(v, &mut obj.additional_properties);
-        visit_box(v, &mut obj.property_names);
-    }
-}
-
-fn visit_box<V: Visitor + ?Sized>(v: &mut V, target: &mut Option<Box<Schema>>) {
-    if let Some(s) = target {
-        v.visit_schema(s)
-    }
-}
-
-fn visit_vec<V: Visitor + ?Sized>(v: &mut V, target: &mut Option<Vec<Schema>>) {
-    if let Some(vec) = target {
-        for s in vec {
-            v.visit_schema(s)
-        }
-    }
-}
-
-fn visit_map_values<V: Visitor + ?Sized>(v: &mut V, target: &mut crate::Map<String, Schema>) {
-    for s in target.values_mut() {
-        v.visit_schema(s)
-    }
-}
-
-fn visit_single_or_vec<V: Visitor + ?Sized>(v: &mut V, target: &mut Option<SingleOrVec<Schema>>) {
-    match target {
-        None => {}
-        Some(SingleOrVec::Single(s)) => v.visit_schema(s),
-        Some(SingleOrVec::Vec(vec)) => {
-            for s in vec {
-                v.visit_schema(s)
+    if let Some(obj) = schema.as_object_mut() {
+        for (key, value) in obj {
+            match key.as_str() {
+                "not"
+                | "if"
+                | "then"
+                | "else"
+                | "additionalItems"
+                | "contains"
+                | "additionalProperties"
+                | "propertyNames" => {
+                    if let Ok(subschema) = value.try_into() {
+                        v.visit_schema(subschema)
+                    }
+                }
+                "allOf" | "anyOf" | "oneOf" => {
+                    if let Some(array) = value.as_array_mut() {
+                        for value in array {
+                            if let Ok(subschema) = value.try_into() {
+                                v.visit_schema(subschema)
+                            }
+                        }
+                    }
+                }
+                "items" => {
+                    if let Some(array) = value.as_array_mut() {
+                        for value in array {
+                            if let Ok(subschema) = value.try_into() {
+                                v.visit_schema(subschema)
+                            }
+                        }
+                    } else if let Ok(subschema) = value.try_into() {
+                        v.visit_schema(subschema)
+                    }
+                }
+                "properties" | "patternProperties" | "definitions" | "$defs" => {
+                    if let Some(obj) = value.as_object_mut() {
+                        for value in obj.values_mut() {
+                            if let Ok(subschema) = value.try_into() {
+                                v.visit_schema(subschema)
+                            }
+                        }
+                    }
+                }
+                _ => {}
             }
         }
     }
@@ -133,29 +104,23 @@ pub struct ReplaceBoolSchemas {
 
 impl Visitor for ReplaceBoolSchemas {
     fn visit_schema(&mut self, schema: &mut Schema) {
-        visit_schema(self, schema);
-
-        if let Schema::Bool(b) = *schema {
-            *schema = Schema::Bool(b).into_object().into()
-        }
-    }
+        if let Some(obj) = schema.as_object_mut() {
+            if self.skip_additional_properties {
+                if let Some((ap_key, ap_value)) = obj.remove_entry("additionalProperties") {
+                    visit_schema(self, schema);
 
-    fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
-        if self.skip_additional_properties {
-            if let Some(obj) = &mut schema.object {
-                if let Some(ap) = &obj.additional_properties {
-                    if let Schema::Bool(_) = ap.as_ref() {
-                        let additional_properties = obj.additional_properties.take();
-                        visit_schema_object(self, schema);
-                        schema.object().additional_properties = additional_properties;
-
-                        return;
+                    if let Some(obj) = schema.as_object_mut() {
+                        obj.insert(ap_key, ap_value);
                     }
+
+                    return;
                 }
             }
-        }
 
-        visit_schema_object(self, schema);
+            visit_schema(self, schema);
+        } else {
+            schema.ensure_object();
+        }
     }
 }
 
@@ -166,18 +131,19 @@ impl Visitor for ReplaceBoolSchemas {
 pub struct RemoveRefSiblings;
 
 impl Visitor for RemoveRefSiblings {
-    fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
-        visit_schema_object(self, schema);
-
-        if let Some(reference) = schema.reference.take() {
-            if schema == &SchemaObject::default() {
-                schema.reference = Some(reference);
-            } else {
-                let ref_schema = Schema::new_ref(reference);
-                let all_of = &mut schema.subschemas().all_of;
-                match all_of {
-                    Some(vec) => vec.push(ref_schema),
-                    None => *all_of = Some(vec![ref_schema]),
+    fn visit_schema(&mut self, schema: &mut Schema) {
+        visit_schema(self, schema);
+
+        if let Some(obj) = schema.as_object_mut() {
+            if obj.len() > 1 {
+                if let Some(ref_value) = obj.remove("$ref") {
+                    if let Value::Array(all_of) =
+                        obj.entry("allOf").or_insert(Value::Array(Vec::new()))
+                    {
+                        all_of.push(json!({
+                            "$ref": ref_value
+                        }));
+                    }
                 }
             }
         }
@@ -188,25 +154,18 @@ impl Visitor for RemoveRefSiblings {
 ///
 /// This is useful for dialects of JSON Schema (e.g. OpenAPI 3.0) that do not support the `examples` property.
 #[derive(Debug, Clone)]
-pub struct SetSingleExample {
-    /// When set to `true`, the `examples` property will not be removed, but its first value will still be copied to `example`.
-    pub retain_examples: bool,
-}
+pub struct SetSingleExample;
 
 impl Visitor for SetSingleExample {
-    fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
-        visit_schema_object(self, schema);
-
-        let first_example = schema.metadata.as_mut().and_then(|m| {
-            if self.retain_examples {
-                m.examples.first().cloned()
-            } else {
-                m.examples.drain(..).next()
-            }
-        });
+    fn visit_schema(&mut self, schema: &mut Schema) {
+        visit_schema(self, schema);
 
-        if let Some(example) = first_example {
-            schema.extensions.insert("example".to_owned(), example);
+        if let Some(obj) = schema.as_object_mut() {
+            if let Some(Value::Array(examples)) = obj.remove("examples") {
+                if let Some(first_example) = examples.into_iter().next() {
+                    obj.insert("example".into(), first_example);
+                }
+            }
         }
     }
 }
diff --git a/schemars_derive/src/attr/validation.rs b/schemars_derive/src/attr/validation.rs
index a511ab4..8d90302 100644
--- a/schemars_derive/src/attr/validation.rs
+++ b/schemars_derive/src/attr/validation.rs
@@ -325,119 +325,85 @@ impl ValidationAttrs {
     }
 
     pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) {
-        if let Some(apply_expr) = self.apply_to_schema_expr() {
-            *schema_expr = quote! {
-                {
-                    let mut schema = #schema_expr;
-                    #apply_expr
-                    schema
-                }
-            }
+        let setters = self.make_setters(quote!(&mut schema));
+        if !setters.is_empty() {
+            *schema_expr = quote!({
+                let mut schema = #schema_expr;
+                #(#setters)*
+                schema
+            });
         }
     }
 
-    fn apply_to_schema_expr(&self) -> Option<TokenStream> {
-        let mut array_validation = Vec::new();
-        let mut number_validation = Vec::new();
-        let mut object_validation = Vec::new();
-        let mut string_validation = Vec::new();
+    fn make_setters(&self, mut_schema: impl ToTokens) -> Vec<TokenStream> {
+        let mut result = Vec::new();
 
         if let Some(length_min) = self.length_min.as_ref().or(self.length_equal.as_ref()) {
-            string_validation.push(quote! {
-                validation.min_length = Some(#length_min as u32);
+            result.push(quote! {
+                schemars::_private::insert_validation_property(#mut_schema, "string", "minLength", #length_min);
             });
-            array_validation.push(quote! {
-                validation.min_items = Some(#length_min as u32);
+            result.push(quote! {
+                schemars::_private::insert_validation_property(#mut_schema, "array", "minItems", #length_min);
             });
         }
 
         if let Some(length_max) = self.length_max.as_ref().or(self.length_equal.as_ref()) {
-            string_validation.push(quote! {
-                validation.max_length = Some(#length_max as u32);
+            result.push(quote! {
+                schemars::_private::insert_validation_property(#mut_schema, "string", "maxLength", #length_max);
             });
-            array_validation.push(quote! {
-                validation.max_items = Some(#length_max as u32);
+            result.push(quote! {
+                schemars::_private::insert_validation_property(#mut_schema, "array", "maxItems", #length_max);
             });
         }
 
         if let Some(range_min) = &self.range_min {
-            number_validation.push(quote! {
-                validation.minimum = Some(#range_min as f64);
+            result.push(quote! {
+                schemars::_private::insert_validation_property(#mut_schema, "number", "minimum", #range_min);
             });
         }
 
         if let Some(range_max) = &self.range_max {
-            number_validation.push(quote! {
-                validation.maximum = Some(#range_max as f64);
+            result.push(quote! {
+                schemars::_private::insert_validation_property(#mut_schema, "number", "maximum", #range_max);
             });
         }
 
         if let Some(regex) = &self.regex {
-            string_validation.push(quote! {
-                validation.pattern = Some(#regex.to_string());
+            result.push(quote! {
+                schemars::_private::insert_validation_property(#mut_schema, "string", "pattern", #regex);
             });
         }
 
         if let Some(contains) = &self.contains {
-            object_validation.push(quote! {
-                validation.required.insert(#contains.to_string());
+            result.push(quote! {
+                schemars::_private::append_required(#mut_schema, #contains);
             });
 
             if self.regex.is_none() {
                 let pattern = crate::regex_syntax::escape(contains);
-                string_validation.push(quote! {
-                    validation.pattern = Some(#pattern.to_string());
+                result.push(quote! {
+                    schemars::_private::insert_validation_property(#mut_schema, "string", "pattern", #pattern);
                 });
             }
         }
 
-        let format = self.format.as_ref().map(|f| {
-            let f = f.schema_str();
-            quote! {
-                schema_object.format = Some(#f.to_string());
-            }
-        });
-
-        let inner_validation = self
-            .inner
-            .as_deref()
-            .and_then(|inner| inner.apply_to_schema_expr())
-            .map(|apply_expr| {
-                quote! {
-                    if schema_object.has_type(schemars::schema::InstanceType::Array) {
-                        if let Some(schemars::schema::SingleOrVec::Single(inner_schema)) = &mut schema_object.array().items {
-                            let mut schema = &mut **inner_schema;
-                            #apply_expr
-                        }
-                    }
-                }
-            });
-
-        let array_validation = wrap_array_validation(array_validation);
-        let number_validation = wrap_number_validation(number_validation);
-        let object_validation = wrap_object_validation(object_validation);
-        let string_validation = wrap_string_validation(string_validation);
-
-        if array_validation.is_some()
-            || number_validation.is_some()
-            || object_validation.is_some()
-            || string_validation.is_some()
-            || format.is_some()
-            || inner_validation.is_some()
-        {
-            Some(quote! {
-                if let schemars::schema::Schema::Object(schema_object) = &mut schema {
-                    #array_validation
-                    #number_validation
-                    #object_validation
-                    #string_validation
-                    #format
-                    #inner_validation
-                }
+        if let Some(format) = &self.format {
+            let f = format.schema_str();
+            result.push(quote! {
+                schema.ensure_object().insert("format".to_owned(), #f.into());
             })
-        } else {
-            None
+        };
+
+        if let Some(inner) = &self.inner {
+            let inner_setters = inner.make_setters(quote!(schema));
+            if !inner_setters.is_empty() {
+                result.push(quote! {
+                    schemars::_private::apply_inner_validation(#mut_schema, |schema| { #(#inner_setters)* });
+                })
+            }
         }
+
+        result
     }
 }
 
@@ -456,59 +422,6 @@ fn parse_lit_into_expr_path(
     })
 }
 
-fn wrap_array_validation(v: Vec<TokenStream>) -> Option<TokenStream> {
-    if v.is_empty() {
-        None
-    } else {
-        Some(quote! {
-            if schema_object.has_type(schemars::schema::InstanceType::Array) {
-                let validation = schema_object.array();
-                #(#v)*
-            }
-        })
-    }
-}
-
-fn wrap_number_validation(v: Vec<TokenStream>) -> Option<TokenStream> {
-    if v.is_empty() {
-        None
-    } else {
-        Some(quote! {
-            if schema_object.has_type(schemars::schema::InstanceType::Integer)
-                || schema_object.has_type(schemars::schema::InstanceType::Number) {
-                let validation = schema_object.number();
-                #(#v)*
-            }
-        })
-    }
-}
-
-fn wrap_object_validation(v: Vec<TokenStream>) -> Option<TokenStream> {
-    if v.is_empty() {
-        None
-    } else {
-        Some(quote! {
-            if schema_object.has_type(schemars::schema::InstanceType::Object) {
-                let validation = schema_object.object();
-                #(#v)*
-            }
-        })
-    }
-}
-
-fn wrap_string_validation(v: Vec<TokenStream>) -> Option<TokenStream> {
-    if v.is_empty() {
-        None
-    } else {
-        Some(quote! {
-            if schema_object.has_type(schemars::schema::InstanceType::String) {
-                let validation = schema_object.string();
-                #(#v)*
-            }
-        })
-    }
-}
-
 fn str_or_num_to_expr(cx: &Ctxt, meta_item_name: &str, expr: Expr) -> Option<Expr> {
     // this odd double-parsing is to make `-10` parsed as an Lit instead of an Expr::Unary
     let lit: Lit = match syn::parse2(expr.to_token_stream()) {
diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs
index ea6c0a6..c890f75 100644
--- a/schemars_derive/src/lib.rs
+++ b/schemars_derive/src/lib.rs
@@ -68,11 +68,11 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result<To
                         <#ty as schemars::JsonSchema>::schema_id()
                     }
 
-                    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+                    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema {
                         <#ty as schemars::JsonSchema>::json_schema(gen)
                     }
 
-                    fn _schemars_private_non_optional_json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+                    fn _schemars_private_non_optional_json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema {
                         <#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(gen)
                     }
 
@@ -182,7 +182,7 @@ fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result<To
                     #schema_id
                 }
 
-                fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+                fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema {
                     #schema_expr
                 }
             };
diff --git a/schemars_derive/src/metadata.rs b/schemars_derive/src/metadata.rs
index 3dd77a0..6de6834 100644
--- a/schemars_derive/src/metadata.rs
+++ b/schemars_derive/src/metadata.rs
@@ -13,32 +13,46 @@ pub struct SchemaMetadata<'a> {
 
 impl<'a> SchemaMetadata<'a> {
     pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) {
+        let setters = self.make_setters();
+        if !setters.is_empty() {
+            *schema_expr = quote! {{
+                let mut schema = #schema_expr;
+                let obj = schema.ensure_object();
+                #(#setters)*
+                schema
+            }}
+        }
+    }
+
+    fn make_setters(&self) -> Vec<TokenStream> {
+        let mut setters = Vec::<TokenStream>::new();
+
         if let Some(title) = &self.title {
-            *schema_expr = quote! {
-                schemars::_private::metadata::add_title(#schema_expr, #title)
-            };
+            setters.push(quote! {
+                obj.insert("title".to_owned(), #title.into());
+            });
         }
         if let Some(description) = &self.description {
-            *schema_expr = quote! {
-                schemars::_private::metadata::add_description(#schema_expr, #description)
-            };
+            setters.push(quote! {
+                obj.insert("description".to_owned(), #description.into());
+            });
         }
 
         if self.deprecated {
-            *schema_expr = quote! {
-                schemars::_private::metadata::add_deprecated(#schema_expr, true)
-            };
+            setters.push(quote! {
+                obj.insert("deprecated".to_owned(), true.into());
+            });
         }
 
         if self.read_only {
-            *schema_expr = quote! {
-                schemars::_private::metadata::add_read_only(#schema_expr, true)
-            };
+            setters.push(quote! {
+                obj.insert("readOnly".to_owned(), true.into());
+            });
         }
         if self.write_only {
-            *schema_expr = quote! {
-                schemars::_private::metadata::add_write_only(#schema_expr, true)
-            };
+            setters.push(quote! {
+                obj.insert("writeOnly".to_owned(), true.into());
+            });
         }
 
         if !self.examples.is_empty() {
@@ -47,16 +61,19 @@ impl<'a> SchemaMetadata<'a> {
                     schemars::_serde_json::value::to_value(#eg())
                 }
             });
-
-            *schema_expr = quote! {
-                schemars::_private::metadata::add_examples(#schema_expr,  [#(#examples),*].into_iter().flatten())
-            };
+            setters.push(quote! {
+                obj.insert("examples".to_owned(), schemars::_serde_json::Value::Array([#(#examples),*].into_iter().flatten().collect()));
+            });
         }
 
         if let Some(default) = &self.default {
-            *schema_expr = quote! {
-                schemars::_private::metadata::add_default(#schema_expr, #default.and_then(|d| schemars::_schemars_maybe_to_value!(d)))
-            };
+            setters.push(quote! {
+                if let Some(default) = #default.and_then(|d| schemars::_schemars_maybe_to_value!(d)) {
+                    obj.insert("default".to_owned(), default);
+                }
+            });
         }
+
+        setters
     }
 }
diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs
index b625114..585184c 100644
--- a/schemars_derive/src/schema_exprs.rs
+++ b/schemars_derive/src/schema_exprs.rs
@@ -49,9 +49,18 @@ pub fn expr_for_repr(cont: &Container) -> Result<TokenStream, syn::Error> {
     let enum_ident = &cont.ident;
     let variant_idents = variants.iter().map(|v| &v.ident);
 
-    let mut schema_expr = schema_object(quote! {
-        instance_type: Some(schemars::schema::InstanceType::Integer.into()),
-        enum_values: Some(vec![#((#enum_ident::#variant_idents as #repr_type).into()),*]),
+    let mut schema_expr = quote!({
+        let mut map = schemars::_serde_json::Map::new();
+        map.insert("type".to_owned(), "integer".into());
+        map.insert(
+            "enum".to_owned(),
+            schemars::_serde_json::Value::Array({
+                let mut enum_values = Vec::new();
+                #(enum_values.push((#enum_ident::#variant_idents as #repr_type).into());)*
+                enum_values
+            }),
+        );
+        schemars::Schema::from(map)
     });
 
     cont.attrs.as_metadata().apply_to_schema(&mut schema_expr);
@@ -118,7 +127,7 @@ fn type_for_schema(with_attr: &WithAttr) -> (syn::Type, Option<TokenStream>) {
                         ))
                     }
 
-                    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+                    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema {
                         #fun(gen)
                     }
                 }
@@ -160,9 +169,18 @@ fn expr_for_external_tagged_enum<'a>(
         })
         .partition(|v| v.is_unit() && v.attrs.is_default());
     let unit_names = unit_variants.iter().map(|v| v.name());
-    let unit_schema = schema_object(quote! {
-        instance_type: Some(schemars::schema::InstanceType::String.into()),
-        enum_values: Some(vec![#(#unit_names.into()),*]),
+    let unit_schema = quote!({
+        let mut map = schemars::_serde_json::Map::new();
+        map.insert("type".to_owned(), "string".into());
+        map.insert(
+            "enum".to_owned(),
+            schemars::_serde_json::Value::Array({
+                let mut enum_values = Vec::new();
+                #(enum_values.push((#unit_names).into());)*
+                enum_values
+            }),
+        );
+        schemars::Schema::from(map)
     });
 
     if complex_variants.is_empty() {
@@ -276,47 +294,44 @@ fn expr_for_adjacent_tagged_enum<'a>(
             let (add_content_to_props, add_content_to_required) = content_schema
                 .map(|content_schema| {
                     (
-                        quote!(props.insert(#content_name.to_owned(), #content_schema);),
-                        quote!(required.insert(#content_name.to_owned());),
+                        quote!(#content_name: (#content_schema),),
+                        quote!(#content_name,),
                     )
                 })
                 .unwrap_or_default();
 
             let name = variant.name();
-            let tag_schema = schema_object(quote! {
-                instance_type: Some(schemars::schema::InstanceType::String.into()),
-                enum_values: Some(vec![#name.into()]),
-            });
+            let tag_schema = quote! {
+                schemars::json_schema!({
+                    "type": "string",
+                    "enum": [#name],
+                })
+            };
 
             let set_additional_properties = if deny_unknown_fields {
                 quote! {
-                    additional_properties: Some(Box::new(false.into())),
+                    "additionalProperties": false,
                 }
             } else {
                 TokenStream::new()
             };
 
-            let mut outer_schema = schema_object(quote! {
-                instance_type: Some(schemars::schema::InstanceType::Object.into()),
-                object: Some(Box::new(schemars::schema::ObjectValidation {
-                    properties: {
-                        let mut props = schemars::Map::new();
-                        props.insert(#tag_name.to_owned(), #tag_schema);
+            let mut outer_schema = quote! {
+                schemars::json_schema!({
+                    "type": "object",
+                    "properties": {
+                        #tag_name: (#tag_schema),
                         #add_content_to_props
-                        props
                     },
-                    required: {
-                        let mut required = schemars::Set::new();
-                        required.insert(#tag_name.to_owned());
+                    "required": [
+                        #tag_name,
                         #add_content_to_required
-                        required
-                    },
+                    ],
                     // As we're creating a "wrapper" object, we can honor the
                     // disposition of deny_unknown_fields.
                     #set_additional_properties
-                    ..Default::default()
-                })),
-            });
+                })
+            };
 
             variant
                 .attrs
@@ -333,21 +348,19 @@ fn expr_for_adjacent_tagged_enum<'a>(
 /// Callers must determine if all subschemas are mutually exclusive. This can
 /// be done for most tagging regimes by checking that all tag names are unique.
 fn variant_subschemas(unique: bool, schemas: Vec<TokenStream>) -> TokenStream {
-    if unique {
-        schema_object(quote! {
-            subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
-                one_of: Some(vec![#(#schemas),*]),
-                ..Default::default()
-            })),
-        })
-    } else {
-        schema_object(quote! {
-            subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
-                any_of: Some(vec![#(#schemas),*]),
-                ..Default::default()
-            })),
-        })
-    }
+    let keyword = if unique { "oneOf" } else { "anyOf" };
+    quote!({
+        let mut map = schemars::_serde_json::Map::new();
+        map.insert(
+            #keyword.to_owned(),
+            schemars::_serde_json::Value::Array({
+                let mut enum_values = Vec::new();
+                #(enum_values.push(#schemas.to_value());)*
+                enum_values
+            }),
+        );
+        schemars::Schema::from(map)
+    })
 }
 
 fn expr_for_untagged_enum_variant(variant: &Variant, deny_unknown_fields: bool) -> TokenStream {
@@ -412,16 +425,11 @@ fn expr_for_tuple_struct(fields: &[Field]) -> TokenStream {
     let len = fields.len() as u32;
 
     quote! {
-        schemars::schema::Schema::Object(
-            schemars::schema::SchemaObject {
-            instance_type: Some(schemars::schema::InstanceType::Array.into()),
-            array: Some(Box::new(schemars::schema::ArrayValidation {
-                items: Some(vec![#(#fields),*].into()),
-                max_items: Some(#len),
-                min_items: Some(#len),
-                ..Default::default()
-            })),
-            ..Default::default()
+        schemars::json_schema!({
+            "type": "array",
+            "items": [#((#fields)),*],
+            "minItems": #len,
+            "maxItems": #len,
         })
     }
 }
@@ -477,7 +485,7 @@ fn expr_for_struct(
             quote! {
                 {
                     #type_def
-                    schemars::_private::insert_object_property::<#ty>(object_validation, #name, #has_default, #required, #schema_expr);
+                    schemars::_private::insert_object_property::<#ty>(&mut schema, #name, #has_default, #required, #schema_expr);
                 }
             }
         })
@@ -502,7 +510,7 @@ fn expr_for_struct(
 
     let set_additional_properties = if deny_unknown_fields {
         quote! {
-            object_validation.additional_properties = Some(Box::new(false.into()));
+            "additionalProperties": false,
         }
     } else {
         TokenStream::new()
@@ -510,15 +518,12 @@ fn expr_for_struct(
     quote! {
         {
             #set_container_default
-            let mut schema_object = schemars::schema::SchemaObject {
-                instance_type: Some(schemars::schema::InstanceType::Object.into()),
-                ..Default::default()
-            };
-            let object_validation = schema_object.object();
-            #set_additional_properties
+            let mut schema = schemars::json_schema!({
+                "type": "object",
+                #set_additional_properties
+            });
             #(#properties)*
-            schemars::schema::Schema::Object(schema_object)
-            #(.flatten(#flattens))*
+            schema #(.flatten(#flattens))*
         }
     }
 }
@@ -578,16 +583,6 @@ fn field_default_expr(field: &Field, container_has_default: bool) -> Option<Toke
     })
 }
 
-fn schema_object(properties: TokenStream) -> TokenStream {
-    quote! {
-        schemars::schema::Schema::Object(
-            schemars::schema::SchemaObject {
-            #properties
-            ..Default::default()
-        })
-    }
-}
-
 fn prepend_type_def(type_def: Option<TokenStream>, schema_expr: &mut TokenStream) {
     if let Some(type_def) = type_def {
         *schema_expr = quote! {


Test patch:
diff --git a/schemars/tests/arrayvec.rs b/schemars/tests/arrayvec.rs
index b00b395..e32d1a7 100644
--- a/schemars/tests/arrayvec.rs
+++ b/schemars/tests/arrayvec.rs
@@ -1,16 +1,6 @@
 mod util;
 use util::*;
 
-#[test]
-fn arrayvec05() -> TestResult {
-    test_default_generated_schema::<arrayvec05::ArrayVec<[i32; 16]>>("arrayvec")
-}
-
-#[test]
-fn arrayvec05_string() -> TestResult {
-    test_default_generated_schema::<arrayvec05::ArrayString<[u8; 16]>>("arrayvec_string")
-}
-
 #[test]
 fn arrayvec07() -> TestResult {
     test_default_generated_schema::<arrayvec07::ArrayVec<i32, 16>>("arrayvec")
diff --git a/schemars/tests/bytes.rs b/schemars/tests/bytes.rs
index 688ab21..5797411 100644
--- a/schemars/tests/bytes.rs
+++ b/schemars/tests/bytes.rs
@@ -1,5 +1,5 @@
 mod util;
-use bytes::{Bytes, BytesMut};
+use bytes1::{Bytes, BytesMut};
 use util::*;
 
 #[test]
diff --git a/schemars/tests/chrono.rs b/schemars/tests/chrono.rs
index 7f518d7..da6f8d4 100644
--- a/schemars/tests/chrono.rs
+++ b/schemars/tests/chrono.rs
@@ -1,5 +1,5 @@
 mod util;
-use chrono::prelude::*;
+use chrono04::prelude::*;
 use schemars::JsonSchema;
 use util::*;
 
diff --git a/schemars/tests/decimal.rs b/schemars/tests/decimal.rs
index d245583..246e813 100644
--- a/schemars/tests/decimal.rs
+++ b/schemars/tests/decimal.rs
@@ -3,12 +3,7 @@ use util::*;
 
 #[test]
 fn rust_decimal() -> TestResult {
-    test_default_generated_schema::<rust_decimal::Decimal>("rust_decimal")
-}
-
-#[test]
-fn bigdecimal03() -> TestResult {
-    test_default_generated_schema::<bigdecimal03::BigDecimal>("bigdecimal03")
+    test_default_generated_schema::<rust_decimal1::Decimal>("rust_decimal")
 }
 
 #[test]
diff --git a/schemars/tests/dereference.rs b/schemars/tests/dereference.rs
deleted file mode 100644
index b1abab0..0000000
--- a/schemars/tests/dereference.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use schemars::{gen::SchemaGenerator, JsonSchema};
-use std::ptr;
-
-#[allow(dead_code)]
-#[derive(JsonSchema)]
-struct Struct {
-    foo: i32,
-    bar: bool,
-}
-
-#[test]
-fn dereference_struct() {
-    let mut gen = SchemaGenerator::default();
-    let struct_ref_schema = gen.subschema_for::<Struct>();
-    let struct_schema = gen.definitions().get(&<Struct>::schema_name()).unwrap();
-
-    assert!(struct_ref_schema.is_ref());
-    assert!(!struct_schema.is_ref());
-
-    let dereferenced = gen.dereference(&struct_ref_schema);
-    assert!(dereferenced.is_some());
-    assert!(ptr::eq(dereferenced.unwrap(), struct_schema));
-}
diff --git a/schemars/tests/either.rs b/schemars/tests/either.rs
index 16a2e59..5dcd079 100644
--- a/schemars/tests/either.rs
+++ b/schemars/tests/either.rs
@@ -1,5 +1,5 @@
 mod util;
-use either::Either;
+use either1::Either;
 use util::*;
 
 #[test]
diff --git a/schemars/tests/enum.rs b/schemars/tests/enum.rs
index 1ed6046..70bd9d2 100644
--- a/schemars/tests/enum.rs
+++ b/schemars/tests/enum.rs
@@ -1,5 +1,7 @@
 mod util;
-use schemars::{JsonSchema, Map};
+use std::collections::BTreeMap;
+
+use schemars::JsonSchema;
 use util::*;
 
 // Ensure that schemars_derive uses the full path to std::string::String
@@ -20,7 +22,7 @@ struct Struct {
 #[schemars(rename_all = "camelCase")]
 enum External {
     UnitOne,
-    StringMap(Map<&'static str, &'static str>),
+    StringMap(BTreeMap<&'static str, &'static str>),
     UnitStructNewType(UnitStruct),
     StructNewType(Struct),
     Struct {
@@ -29,6 +31,7 @@ enum External {
     },
     UnitTwo,
     Tuple(i32, bool),
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(with = "i32")]
     WithInt,
 }
@@ -43,7 +46,7 @@ fn enum_external_tag() -> TestResult {
 #[schemars(tag = "typeProperty")]
 enum Internal {
     UnitOne,
-    StringMap(Map<&'static str, &'static str>),
+    StringMap(BTreeMap<&'static str, &'static str>),
     UnitStructNewType(UnitStruct),
     StructNewType(Struct),
     Struct {
@@ -51,6 +54,7 @@ enum Internal {
         bar: bool,
     },
     UnitTwo,
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(with = "i32")]
     WithInt,
 }
@@ -65,7 +69,7 @@ fn enum_internal_tag() -> TestResult {
 #[schemars(untagged)]
 enum Untagged {
     UnitOne,
-    StringMap(Map<&'static str, &'static str>),
+    StringMap(BTreeMap<&'static str, &'static str>),
     UnitStructNewType(UnitStruct),
     StructNewType(Struct),
     Struct {
@@ -73,6 +77,7 @@ enum Untagged {
         bar: bool,
     },
     Tuple(i32, bool),
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(with = "i32")]
     WithInt,
 }
@@ -87,7 +92,7 @@ fn enum_untagged() -> TestResult {
 #[schemars(tag = "t", content = "c")]
 enum Adjacent {
     UnitOne,
-    StringMap(Map<&'static str, &'static str>),
+    StringMap(BTreeMap<&'static str, &'static str>),
     UnitStructNewType(UnitStruct),
     StructNewType(Struct),
     Struct {
@@ -96,6 +101,7 @@ enum Adjacent {
     },
     Tuple(i32, bool),
     UnitTwo,
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(with = "i32")]
     WithInt,
 }
diff --git a/schemars/tests/enum_deny_unknown_fields.rs b/schemars/tests/enum_deny_unknown_fields.rs
index 6201784..62c1a45 100644
--- a/schemars/tests/enum_deny_unknown_fields.rs
+++ b/schemars/tests/enum_deny_unknown_fields.rs
@@ -1,5 +1,7 @@
 mod util;
-use schemars::{JsonSchema, Map};
+use std::collections::BTreeMap;
+
+use schemars::JsonSchema;
 use util::*;
 
 // Ensure that schemars_derive uses the full path to std::string::String
@@ -22,7 +24,7 @@ struct Struct {
 #[schemars(rename_all = "camelCase", deny_unknown_fields)]
 enum External {
     UnitOne,
-    StringMap(Map<&'static str, &'static str>),
+    StringMap(BTreeMap<&'static str, &'static str>),
     UnitStructNewType(UnitStruct),
     StructNewType(Struct),
     Struct {
@@ -31,6 +33,7 @@ enum External {
     },
     UnitTwo,
     Tuple(i32, bool),
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(with = "i32")]
     WithInt,
 }
@@ -46,7 +49,7 @@ fn enum_external_tag() -> TestResult {
 #[schemars(tag = "typeProperty", deny_unknown_fields)]
 enum Internal {
     UnitOne,
-    StringMap(Map<&'static str, &'static str>),
+    StringMap(BTreeMap<&'static str, &'static str>),
     UnitStructNewType(UnitStruct),
     StructNewType(Struct),
     Struct {
@@ -54,6 +57,7 @@ enum Internal {
         bar: bool,
     },
     UnitTwo,
+    // FIXME this should only replace the "payload" of the enum (which doesn't even make sense for unit enums!)
     #[schemars(with = "i32")]
     WithInt,
 }
@@ -69,7 +73,7 @@ fn enum_internal_tag() -> TestResult {
 #[schemars(untagged, deny_unknown_fields)]
 enum Untagged {
     UnitOne,
-    StringMap(Map<&'static str, &'static str>),
+    StringMap(BTreeMap<&'static str, &'static str>),
     UnitStructNewType(UnitStruct),
     StructNewType(Struct),
     Struct {
@@ -77,6 +81,7 @@ enum Untagged {
         bar: bool,
     },
     Tuple(i32, bool),
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(with = "i32")]
     WithInt,
 }
@@ -92,7 +97,7 @@ fn enum_untagged() -> TestResult {
 #[schemars(tag = "t", content = "c", deny_unknown_fields)]
 enum Adjacent {
     UnitOne,
-    StringMap(Map<&'static str, &'static str>),
+    StringMap(BTreeMap<&'static str, &'static str>),
     UnitStructNewType(UnitStruct),
     StructNewType(Struct),
     Struct {
@@ -101,6 +106,7 @@ enum Adjacent {
     },
     Tuple(i32, bool),
     UnitTwo,
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(with = "i32")]
     WithInt,
 }
diff --git a/schemars/tests/enumset.rs b/schemars/tests/enumset.rs
index 27f2030..c04ba0e 100644
--- a/schemars/tests/enumset.rs
+++ b/schemars/tests/enumset.rs
@@ -1,8 +1,11 @@
 mod util;
-use enumset::{EnumSet, EnumSetType};
+use enumset1::{EnumSet, EnumSetType};
 use schemars::JsonSchema;
 use util::*;
 
+// needed to derive EnumSetType when using a crate alias
+extern crate enumset1 as enumset;
+
 #[derive(EnumSetType, JsonSchema)]
 enum Foo {
     Bar,
diff --git a/schemars/tests/expected/arrayvec_string.json b/schemars/tests/expected/arrayvec_string.json
index 42f099a..ad174d8 100644
--- a/schemars/tests/expected/arrayvec_string.json
+++ b/schemars/tests/expected/arrayvec_string.json
@@ -1,5 +1,5 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "String",
+  "title": "string",
   "type": "string"
 }
\ No newline at end of file
diff --git a/schemars/tests/expected/bigdecimal03.json b/schemars/tests/expected/bigdecimal03.json
deleted file mode 100644
index 855db6f..0000000
--- a/schemars/tests/expected/bigdecimal03.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "Decimal",
-  "type": "string",
-  "pattern": "^-?[0-9]+(\\.[0-9]+)?$"
-}
\ No newline at end of file
diff --git a/schemars/tests/expected/either.json b/schemars/tests/expected/either.json
index b028057..807c9c9 100644
--- a/schemars/tests/expected/either.json
+++ b/schemars/tests/expected/either.json
@@ -1,6 +1,6 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "Either_int32_or_Either_Boolean_or_Null",
+  "title": "Either_int32_or_Either_boolean_or_null",
   "anyOf": [
     {
       "type": "integer",
diff --git a/schemars/tests/expected/enum-internal-duf.json b/schemars/tests/expected/enum-internal-duf.json
index fc36644..501f13b 100644
--- a/schemars/tests/expected/enum-internal-duf.json
+++ b/schemars/tests/expected/enum-internal-duf.json
@@ -127,8 +127,7 @@
             "WithInt"
           ]
         }
-      },
-      "additionalProperties": false
+      }
     }
   ]
 }
\ No newline at end of file
diff --git a/schemars/tests/expected/range.json b/schemars/tests/expected/range.json
index a3b14af..0bb3aeb 100644
--- a/schemars/tests/expected/range.json
+++ b/schemars/tests/expected/range.json
@@ -15,7 +15,7 @@
       "$ref": "#/definitions/Range_of_double"
     },
     "bound": {
-      "$ref": "#/definitions/Bound_of_String"
+      "$ref": "#/definitions/Bound_of_string"
     }
   },
   "definitions": {
@@ -55,7 +55,7 @@
         }
       }
     },
-    "Bound_of_String": {
+    "Bound_of_string": {
       "oneOf": [
         {
           "type": "object",
diff --git a/schemars/tests/expected/remote_derive_generic.json b/schemars/tests/expected/remote_derive_generic.json
index 2fea80a..5a65f3b 100644
--- a/schemars/tests/expected/remote_derive_generic.json
+++ b/schemars/tests/expected/remote_derive_generic.json
@@ -10,10 +10,10 @@
   ],
   "properties": {
     "byte_or_bool2": {
-      "$ref": "#/definitions/Or_for_uint8_and_Boolean"
+      "$ref": "#/definitions/Or_for_uint8_and_boolean"
     },
     "unit_or_t2": {
-      "$ref": "#/definitions/Or_for_Null_and_int32"
+      "$ref": "#/definitions/Or_for_null_and_int32"
     },
     "s": {
       "$ref": "#/definitions/Str"
@@ -30,7 +30,7 @@
     }
   },
   "definitions": {
-    "Or_for_uint8_and_Boolean": {
+    "Or_for_uint8_and_boolean": {
       "anyOf": [
         {
           "type": "integer",
@@ -42,7 +42,7 @@
         }
       ]
     },
-    "Or_for_Null_and_int32": {
+    "Or_for_null_and_int32": {
       "anyOf": [
         {
           "type": "null"
diff --git a/schemars/tests/expected/result.json b/schemars/tests/expected/result.json
index d8d6ec1..2a01573 100644
--- a/schemars/tests/expected/result.json
+++ b/schemars/tests/expected/result.json
@@ -8,14 +8,14 @@
   ],
   "properties": {
     "result1": {
-      "$ref": "#/definitions/Result_of_MyStruct_or_Array_of_String"
+      "$ref": "#/definitions/Result_of_MyStruct_or_Array_of_string"
     },
     "result2": {
-      "$ref": "#/definitions/Result_of_Boolean_or_Null"
+      "$ref": "#/definitions/Result_of_boolean_or_null"
     }
   },
   "definitions": {
-    "Result_of_MyStruct_or_Array_of_String": {
+    "Result_of_MyStruct_or_Array_of_string": {
       "oneOf": [
         {
           "type": "object",
@@ -56,7 +56,7 @@
         }
       }
     },
-    "Result_of_Boolean_or_Null": {
+    "Result_of_boolean_or_null": {
       "oneOf": [
         {
           "type": "object",
diff --git a/schemars/tests/expected/schema-name-custom.json b/schemars/tests/expected/schema-name-custom.json
index 866fb9d..844699a 100644
--- a/schemars/tests/expected/schema-name-custom.json
+++ b/schemars/tests/expected/schema-name-custom.json
@@ -1,6 +1,6 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "a-new-name-Array_of_String-int32-int32",
+  "title": "a-new-name-Array_of_string-int32-int32",
   "type": "object",
   "required": [
     "inner",
diff --git a/schemars/tests/expected/schema-name-default.json b/schemars/tests/expected/schema-name-default.json
index 31f7f26..39c39a9 100644
--- a/schemars/tests/expected/schema-name-default.json
+++ b/schemars/tests/expected/schema-name-default.json
@@ -1,15 +1,11 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String",
+  "title": "MyStruct_for_int32_and_null_and_boolean_and_Array_of_string",
   "type": "object",
-  "required": [
-    "inner",
-    "t",
-    "u",
-    "v",
-    "w"
-  ],
   "properties": {
+    "inner": {
+      "$ref": "#/definitions/MySimpleStruct"
+    },
     "t": {
       "type": "integer",
       "format": "int32"
@@ -25,23 +21,27 @@
       "items": {
         "type": "string"
       }
-    },
-    "inner": {
-      "$ref": "#/definitions/MySimpleStruct"
     }
   },
+  "required": [
+    "inner",
+    "t",
+    "u",
+    "v",
+    "w"
+  ],
   "definitions": {
     "MySimpleStruct": {
       "type": "object",
-      "required": [
-        "foo"
-      ],
       "properties": {
         "foo": {
           "type": "integer",
           "format": "int32"
         }
-      }
+      },
+      "required": [
+        "foo"
+      ]
     }
   }
 }
\ No newline at end of file
diff --git a/schemars/tests/expected/schema-name-mixed-generics.json b/schemars/tests/expected/schema-name-mixed-generics.json
index 32ac797..bddd083 100644
--- a/schemars/tests/expected/schema-name-mixed-generics.json
+++ b/schemars/tests/expected/schema-name-mixed-generics.json
@@ -1,6 +1,6 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "MixedGenericStruct_for_MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String_and_42_and_z",
+  "title": "MixedGenericStruct_for_MyStruct_for_int32_and_null_and_boolean_and_Array_of_string_and_42_and_z",
   "type": "object",
   "required": [
     "foo",
@@ -12,7 +12,7 @@
       "format": "int32"
     },
     "generic": {
-      "$ref": "#/definitions/MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String"
+      "$ref": "#/definitions/MyStruct_for_int32_and_null_and_boolean_and_Array_of_string"
     }
   },
   "definitions": {
@@ -28,7 +28,7 @@
         }
       }
     },
-    "MyStruct_for_int32_and_Null_and_Boolean_and_Array_of_String": {
+    "MyStruct_for_int32_and_null_and_boolean_and_Array_of_string": {
       "type": "object",
       "required": [
         "inner",
diff --git a/schemars/tests/expected/schema_with-enum-internal.json b/schemars/tests/expected/schema_with-enum-internal.json
index 7ede7e6..75b28dc 100644
--- a/schemars/tests/expected/schema_with-enum-internal.json
+++ b/schemars/tests/expected/schema_with-enum-internal.json
@@ -22,8 +22,8 @@
     },
     {
       "type": [
-        "boolean",
-        "object"
+        "object",
+        "boolean"
       ],
       "required": [
         "typeProperty"
@@ -39,8 +39,8 @@
     },
     {
       "type": [
-        "boolean",
-        "object"
+        "object",
+        "boolean"
       ],
       "required": [
         "typeProperty"
diff --git a/schemars/tests/expected/smallvec.json b/schemars/tests/expected/smallvec.json
index eab45c0..7ce011d 100644
--- a/schemars/tests/expected/smallvec.json
+++ b/schemars/tests/expected/smallvec.json
@@ -1,6 +1,6 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "Array_of_String",
+  "title": "Array_of_string",
   "type": "array",
   "items": {
     "type": "string"
diff --git a/schemars/tests/expected/smol_str.json b/schemars/tests/expected/smol_str.json
index 42f099a..ad174d8 100644
--- a/schemars/tests/expected/smol_str.json
+++ b/schemars/tests/expected/smol_str.json
@@ -1,5 +1,5 @@
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
-  "title": "String",
+  "title": "string",
   "type": "string"
 }
\ No newline at end of file
diff --git a/schemars/tests/indexmap.rs b/schemars/tests/indexmap.rs
index 9501b67..e243f35 100644
--- a/schemars/tests/indexmap.rs
+++ b/schemars/tests/indexmap.rs
@@ -1,13 +1,15 @@
 mod util;
-use indexmap::{IndexMap, IndexSet};
+use std::hash::RandomState;
+
+use indexmap2::{IndexMap, IndexSet};
 use schemars::JsonSchema;
 use util::*;
 
 #[allow(dead_code)]
 #[derive(JsonSchema)]
 struct IndexMapTypes {
-    map: IndexMap<i32, bool>,
-    set: IndexSet<isize>,
+    map: IndexMap<i32, bool, RandomState>,
+    set: IndexSet<isize, RandomState>,
 }
 
 #[test]
diff --git a/schemars/tests/indexmap2.rs b/schemars/tests/indexmap2.rs
deleted file mode 100644
index efc77dd..0000000
--- a/schemars/tests/indexmap2.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-mod util;
-use indexmap2::{IndexMap, IndexSet};
-use schemars::JsonSchema;
-use util::*;
-
-#[allow(dead_code)]
-#[derive(JsonSchema)]
-struct IndexMapTypes {
-    map: IndexMap<i32, bool>,
-    set: IndexSet<isize>,
-}
-
-#[test]
-fn indexmap_types() -> TestResult {
-    test_default_generated_schema::<IndexMapTypes>("indexmap")
-}
diff --git a/schemars/tests/schema_for_schema.rs b/schemars/tests/schema_for_schema.rs
deleted file mode 100644
index d175505..0000000
--- a/schemars/tests/schema_for_schema.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-mod util;
-use schemars::gen::SchemaSettings;
-use schemars::schema::RootSchema;
-use util::*;
-
-#[test]
-fn schema_matches_draft07() -> TestResult {
-    test_generated_schema::<RootSchema>("schema", SchemaSettings::draft07())
-}
-
-#[test]
-fn schema_matches_2019_09() -> TestResult {
-    test_generated_schema::<RootSchema>("schema-2019_09", SchemaSettings::draft2019_09())
-}
-
-#[test]
-fn schema_matches_openapi3() -> TestResult {
-    test_generated_schema::<RootSchema>("schema-openapi3", SchemaSettings::openapi3())
-}
diff --git a/schemars/tests/schema_with_enum.rs b/schemars/tests/schema_with_enum.rs
index a91aa4d..5a6c575 100644
--- a/schemars/tests/schema_with_enum.rs
+++ b/schemars/tests/schema_with_enum.rs
@@ -2,7 +2,7 @@ mod util;
 use schemars::JsonSchema;
 use util::*;
 
-fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema {
     <bool>::json_schema(gen)
 }
 
@@ -21,6 +21,7 @@ pub enum External {
         #[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
         i32,
     ),
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(schema_with = "schema_fn")]
     Unit,
 }
@@ -38,6 +39,7 @@ pub enum Internal {
         foo: DoesntImplementJsonSchema,
     },
     NewType(#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema),
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(schema_with = "schema_fn")]
     Unit,
 }
@@ -59,6 +61,7 @@ pub enum Untagged {
         #[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
         i32,
     ),
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(schema_with = "schema_fn")]
     Unit,
 }
@@ -80,6 +83,7 @@ pub enum Adjacent {
         #[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
         i32,
     ),
+    // FIXME this should probably only replace the "payload" of the enum
     #[schemars(schema_with = "schema_fn")]
     Unit,
 }
diff --git a/schemars/tests/schema_with_struct.rs b/schemars/tests/schema_with_struct.rs
index 9c87abe..70ba24d 100644
--- a/schemars/tests/schema_with_struct.rs
+++ b/schemars/tests/schema_with_struct.rs
@@ -2,7 +2,7 @@ mod util;
 use schemars::JsonSchema;
 use util::*;
 
-fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::Schema {
     <bool>::json_schema(gen)
 }
 
diff --git a/schemars/tests/semver.rs b/schemars/tests/semver.rs
index 617e508..3c351c6 100644
--- a/schemars/tests/semver.rs
+++ b/schemars/tests/semver.rs
@@ -1,6 +1,6 @@
 mod util;
 use schemars::JsonSchema;
-use semver::Version;
+use semver1::Version;
 use util::*;
 
 #[allow(dead_code)]
diff --git a/schemars/tests/smallvec.rs b/schemars/tests/smallvec.rs
index bc9c6ef..8412a6a 100644
--- a/schemars/tests/smallvec.rs
+++ b/schemars/tests/smallvec.rs
@@ -1,5 +1,5 @@
 mod util;
-use smallvec::SmallVec;
+use smallvec1::SmallVec;
 use util::*;
 
 #[test]
diff --git a/schemars/tests/smol_str.rs b/schemars/tests/smol_str.rs
index 1e48196..43fad30 100644
--- a/schemars/tests/smol_str.rs
+++ b/schemars/tests/smol_str.rs
@@ -1,5 +1,5 @@
 mod util;
-use smol_str::SmolStr;
+use smol_str02::SmolStr;
 use util::*;
 
 #[test]
diff --git a/schemars/tests/url.rs b/schemars/tests/url.rs
index 0e9499a..018a66b 100644
--- a/schemars/tests/url.rs
+++ b/schemars/tests/url.rs
@@ -1,6 +1,6 @@
 mod util;
 use schemars::JsonSchema;
-use url::Url;
+use url2::Url;
 use util::*;
 
 #[allow(dead_code)]
diff --git a/schemars/tests/util/mod.rs b/schemars/tests/util/mod.rs
index 595d68b..99cf677 100644
--- a/schemars/tests/util/mod.rs
+++ b/schemars/tests/util/mod.rs
@@ -1,5 +1,6 @@
 use pretty_assertions::assert_eq;
-use schemars::{gen::SchemaSettings, schema::RootSchema, schema_for, JsonSchema};
+use schemars::visit::Visitor;
+use schemars::{gen::SchemaSettings, schema_for, JsonSchema, Schema};
 use std::error::Error;
 use std::fs;
 
@@ -17,7 +18,16 @@ pub fn test_default_generated_schema<T: JsonSchema>(file: &str) -> TestResult {
     test_schema(&actual, file)
 }
 
-pub fn test_schema(actual: &RootSchema, file: &str) -> TestResult {
+pub fn test_schema(actual: &Schema, file: &str) -> TestResult {
+    // TEMP for easier comparison of schemas handling changes that don't actually affect a schema:
+    // - `required` ordering has changed
+    // - previously `f64` properties may now be integers
+    let actual = &{
+        let mut actual = actual.clone();
+        TempFixupForTests.visit_schema(&mut actual);
+        actual
+    };
+
     let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) {
         Ok(j) => j,
         Err(e) => {
@@ -35,8 +45,30 @@ pub fn test_schema(actual: &RootSchema, file: &str) -> TestResult {
     Ok(())
 }
 
-fn write_actual_to_file(schema: &RootSchema, file: &str) -> TestResult {
+fn write_actual_to_file(schema: &Schema, file: &str) -> TestResult {
     let actual_json = serde_json::to_string_pretty(&schema)?;
     fs::write(format!("tests/actual/{}.json", file), actual_json)?;
     Ok(())
 }
+
+struct TempFixupForTests;
+
+impl schemars::visit::Visitor for TempFixupForTests {
+    fn visit_schema(&mut self, schema: &mut Schema) {
+        schemars::visit::visit_schema(self, schema);
+
+        if let Some(object) = schema.as_object_mut() {
+            if let Some(serde_json::Value::Array(required)) = object.get_mut("required") {
+                required.sort_unstable_by(|a, b| a.as_str().cmp(&b.as_str()));
+            }
+
+            for (key, value) in object {
+                if key == "multipleOf" || key.ends_with("aximum") || key.ends_with("inimum") {
+                    if let Some(f) = value.as_f64() {
+                        *value = f.into();
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/schemars/tests/uuid.rs b/schemars/tests/uuid.rs
index bd673b5..77e92c2 100644
--- a/schemars/tests/uuid.rs
+++ b/schemars/tests/uuid.rs
@@ -1,11 +1,6 @@
 mod util;
 use util::*;
 
-#[test]
-fn uuid08() -> TestResult {
-    test_default_generated_schema::<uuid08::Uuid>("uuid")
-}
-
 #[test]
 fn uuid1() -> TestResult {
     test_default_generated_schema::<uuid1::Uuid>("uuid")