diff --git a/CHANGELOG.md b/CHANGELOG.md
index 78ec122..a95ed2d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
## In-dev - version TBC
### Added:
- Setting `#[deprecated]` attribute will now cause generated schemas to have the `deprecated` property set to `true`
+- Respect #[serde(transparent)] attribute (https://github.com/GREsau/schemars/issues/17)
## [0.7.4] - 2020-05-16
### Added:
diff --git a/docs/1.1-attributes.md b/docs/1.1-attributes.md
index 47ace02..6c509d9 100644
--- a/docs/1.1-attributes.md
+++ b/docs/1.1-attributes.md
@@ -133,6 +133,15 @@ Setting this on a container will set the `additionalProperties` keyword on gener
Serde docs: [container](https://serde.rs/container-attrs.html#deny_unknown_fields)
+
+
+`#[serde(transparent)]` / `#[schemars(transparent)]`
+
+
+Set on a newtype struct or a braced struct with one field to make the struct's generated schema exactly the same as that of the single field's.
+
+Serde docs: [container](https://serde.rs/container-attrs.html#transparent)
+
## Other Attributes
diff --git a/schemars/tests/expected/transparent-struct.json b/schemars/tests/expected/transparent-struct.json
new file mode 100644
index 0000000..e83f905
--- /dev/null
+++ b/schemars/tests/expected/transparent-struct.json
@@ -0,0 +1,33 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "OuterStruct",
+ "type": "object",
+ "properties": {
+ "inner": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/InnerStruct"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "definitions": {
+ "InnerStruct": {
+ "type": "array",
+ "items": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "integer",
+ "format": "int32"
+ }
+ ],
+ "maxItems": 2,
+ "minItems": 2
+ }
+ }
+}
\ No newline at end of file
diff --git a/schemars/tests/transparent.rs b/schemars/tests/transparent.rs
new file mode 100644
index 0000000..7bbb24a
--- /dev/null
+++ b/schemars/tests/transparent.rs
@@ -0,0 +1,27 @@
+mod util;
+use schemars::JsonSchema;
+use util::*;
+
+#[derive(Debug, JsonSchema)]
+pub struct OuterStruct {
+ inner: TransparentStruct,
+}
+
+#[derive(Debug, JsonSchema)]
+#[serde(transparent)]
+pub struct TransparentStruct {
+ #[serde(with = "TransparentNewType")]
+ inner: (),
+}
+
+#[derive(Debug, JsonSchema)]
+#[schemars(transparent)]
+pub struct TransparentNewType(Option);
+
+#[derive(Debug, JsonSchema)]
+pub struct InnerStruct(String, i32);
+
+#[test]
+fn transparent_struct() -> TestResult {
+ test_default_generated_schema::("transparent-struct")
+}
diff --git a/schemars_derive/src/ast/mod.rs b/schemars_derive/src/ast/mod.rs
index 2f2cb98..a388ac8 100644
--- a/schemars_derive/src/ast/mod.rs
+++ b/schemars_derive/src/ast/mod.rs
@@ -49,6 +49,16 @@ impl<'a> Container<'a> {
pub fn name(&self) -> String {
self.serde_attrs.name().deserialize_name()
}
+
+ pub fn transparent_field(&'a self) -> Option<&'a Field> {
+ if self.serde_attrs.transparent() {
+ if let Data::Struct(_, fields) = &self.data {
+ return Some(&fields[0]);
+ }
+ }
+
+ None
+ }
}
impl<'a> Variant<'a> {
diff --git a/schemars_derive/src/attr/schemars_to_serde.rs b/schemars_derive/src/attr/schemars_to_serde.rs
index 20b7b10..380a276 100644
--- a/schemars_derive/src/attr/schemars_to_serde.rs
+++ b/schemars_derive/src/attr/schemars_to_serde.rs
@@ -19,6 +19,7 @@ static SERDE_KEYWORDS: &[&str] = &[
"skip_deserializing",
"flatten",
"remote",
+ "transparent",
// Special cases - `with`/`serialize_with` are passed to serde but not copied from schemars attrs to serde attrs.
// This is because we want to preserve any serde attribute's `serialize_with` value to determine whether the field's
// default value should be serialized. We also check the `with` value on schemars/serde attrs e.g. to support deriving
diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs
index 2e768d4..006d78a 100644
--- a/schemars_derive/src/lib.rs
+++ b/schemars_derive/src/lib.rs
@@ -30,10 +30,44 @@ fn derive_json_schema(mut input: syn::DeriveInput) -> TokenStream {
Err(e) => return compile_error(&e),
};
- let schema_expr = schema_exprs::expr_for_container(&cont);
-
let type_name = &cont.ident;
- let type_params: Vec<_> = cont.generics.type_params().map(|ty| &ty.ident).collect();
+ let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
+
+ if let Some(transparent_field) = cont.transparent_field() {
+ let (ty, type_def) = schema_exprs::type_for_schema(transparent_field, 0);
+ return quote! {
+ #[automatically_derived]
+ impl #impl_generics schemars::JsonSchema for #type_name #ty_generics #where_clause {
+ #type_def
+
+ fn is_referenceable() -> bool {
+ <#ty as schemars::JsonSchema>::is_referenceable()
+ }
+
+ fn schema_name() -> std::string::String {
+ <#ty as schemars::JsonSchema>::schema_name()
+ }
+
+ fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+ <#ty as schemars::JsonSchema>::json_schema(gen)
+ }
+
+ fn json_schema_for_flatten(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+ <#ty as schemars::JsonSchema>::json_schema_for_flatten(gen)
+ }
+
+ fn add_schema_as_property(
+ gen: &mut schemars::gen::SchemaGenerator,
+ parent: &mut schemars::schema::SchemaObject,
+ name: String,
+ metadata: Option,
+ required: bool,
+ ) {
+ <#ty as schemars::JsonSchema>::add_schema_as_property(gen, parent, name, metadata, required)
+ }
+ };
+ };
+ }
let mut schema_base_name = cont.name();
let schema_is_renamed = *type_name != schema_base_name;
@@ -46,6 +80,7 @@ fn derive_json_schema(mut input: syn::DeriveInput) -> TokenStream {
}
}
+ let type_params: Vec<_> = cont.generics.type_params().map(|ty| &ty.ident).collect();
let schema_name = if type_params.is_empty() {
quote! {
#schema_base_name.to_owned()
@@ -67,7 +102,7 @@ fn derive_json_schema(mut input: syn::DeriveInput) -> TokenStream {
}
};
- let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
+ let schema_expr = schema_exprs::expr_for_container(&cont);
quote! {
#[automatically_derived]
diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs
index 7129c02..6b312fe 100644
--- a/schemars_derive/src/schema_exprs.rs
+++ b/schemars_derive/src/schema_exprs.rs
@@ -38,7 +38,7 @@ fn expr_for_field(field: &Field, allow_ref: bool) -> TokenStream {
}
}
-fn type_for_schema(field: &Field, local_id: usize) -> (syn::Type, Option) {
+pub fn type_for_schema(field: &Field, local_id: usize) -> (syn::Type, Option) {
match &field.attrs.with {
None => (field.ty.to_owned(), None),
Some(WithAttr::Type(ty)) => (ty.to_owned(), None),