Repository: juhaku/utoipa (Rust)

Issue:
Only the first serde container attribute is used
This does not work as expected, only the first serde container attribute works, the rest are ignored:

```
#[derive(Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "type")]
pub enum EnumTest {
    StringType { field: String },
    IntegerValue { value: i64 },
}
```

Combining the two works as expected:

```
#[derive(Serialize, ToSchema)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum EnumTest {
    StringType { field: String },
    IntegerValue { value: i64 },
}
```


Discussion:
This is indeed a current limitaiton in utoipa library. Probably this could be improved in future releases to recognize multiple attributes. 



Patch:
diff --git a/utoipa-gen/src/component/serde.rs b/utoipa-gen/src/component/serde.rs
index d5842525..ab0af3dc 100644
--- a/utoipa-gen/src/component/serde.rs
+++ b/utoipa-gen/src/component/serde.rs
@@ -116,23 +116,56 @@ impl SerdeContainer {
 pub fn parse_value(attributes: &[Attribute]) -> Option<SerdeValue> {
     attributes
         .iter()
-        .find(|attribute| attribute.path.is_ident("serde"))
+        .filter(|attribute| attribute.path.is_ident("serde"))
         .map(|serde_attribute| {
             serde_attribute
                 .parse_args_with(SerdeValue::parse)
                 .unwrap_or_abort()
         })
+        .fold(Some(SerdeValue::default()), |acc, value| {
+            acc.map(|mut acc| {
+                if value.skip {
+                    acc.skip = value.skip;
+                }
+                if value.rename.is_some() {
+                    acc.rename = value.rename;
+                }
+                if value.flatten {
+                    acc.flatten = value.flatten;
+                }
+                if value.default {
+                    acc.default = value.default;
+                }
+
+                acc
+            })
+        })
 }
 
 pub fn parse_container(attributes: &[Attribute]) -> Option<SerdeContainer> {
     attributes
         .iter()
-        .find(|attribute| attribute.path.is_ident("serde"))
+        .filter(|attribute| attribute.path.is_ident("serde"))
         .map(|serde_attribute| {
             serde_attribute
                 .parse_args_with(SerdeContainer::parse)
                 .unwrap_or_abort()
         })
+        .fold(Some(SerdeContainer::default()), |acc, value| {
+            acc.map(|mut acc| {
+                if value.default {
+                    acc.default = value.default;
+                }
+                if !value.tag.is_empty() {
+                    acc.tag = value.tag;
+                }
+                if value.rename_all.is_some() {
+                    acc.rename_all = value.rename_all;
+                }
+
+                acc
+            })
+        })
 }
 
 #[derive(Clone)]


Test patch:
diff --git a/utoipa-gen/tests/schema_derive_test.rs b/utoipa-gen/tests/schema_derive_test.rs
index 46eea274..23a41c92 100644
--- a/utoipa-gen/tests/schema_derive_test.rs
+++ b/utoipa-gen/tests/schema_derive_test.rs
@@ -2914,3 +2914,27 @@ fn derive_schema_with_slice_and_array() {
         })
     )
 }
+
+#[test]
+fn derive_schema_multiple_serde_definitions() {
+    let value = api_doc! {
+        #[derive(serde::Deserialize)]
+        struct Value {
+            #[serde(default)]
+            #[serde(rename = "ID")]
+            id: String
+        }
+    };
+
+    assert_json_eq!(
+        value,
+        json!({
+            "properties": {
+                "ID": {
+                    "type": "string",
+                }
+            },
+            "type": "object",
+        })
+    );
+}