Skip to content

Commit 985cfa5

Browse files
committed
Fix serialization of SchemaObject with boolean json_schema
1 parent 20a660e commit 985cfa5

File tree

1 file changed

+95
-5
lines changed

1 file changed

+95
-5
lines changed

crates/aide/src/openapi/schema.rs

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
#![allow(clippy::large_enum_variant)]
22
use crate::openapi::*;
33
use schemars::Schema;
4-
use serde::{Deserialize, Serialize};
4+
use serde::{ser::SerializeMap, Deserialize, Serialize};
5+
use serde_json::json;
56

6-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, schemars::JsonSchema)]
7+
#[derive(Debug, Clone, Deserialize, PartialEq, schemars::JsonSchema)]
78
pub struct SchemaObject {
89
#[serde(flatten)]
910
pub json_schema: Schema,
1011
/// Additional external documentation for this schema.
1112
#[serde(rename = "externalDocs")]
12-
#[serde(skip_serializing_if = "Option::is_none")]
1313
pub external_docs: Option<ExternalDocumentation>,
1414
/// A free-form property to include an example of an instance for this
1515
/// schema. To represent examples that cannot be naturally represented in
@@ -18,16 +18,52 @@ pub struct SchemaObject {
1818
/// been deprecated in favor of the JSON Schema `examples` keyword. Use
1919
/// of `example` is discouraged, and later versions of this
2020
/// specification may remove it.
21-
#[serde(skip_serializing_if = "Option::is_none")]
2221
pub example: Option<serde_json::Value>,
2322
}
2423

24+
impl Serialize for SchemaObject {
25+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26+
where
27+
S: serde::Serializer,
28+
{
29+
if self.external_docs.is_none() && self.example.is_none() {
30+
return self.json_schema.serialize(serializer);
31+
}
32+
33+
let mut map = serializer.serialize_map(None)?;
34+
match self.json_schema.as_bool() {
35+
Some(true) => {}
36+
Some(false) => {
37+
map.serialize_entry("not", &json!({}))?;
38+
}
39+
_ => {
40+
let object = self
41+
.json_schema
42+
.as_object()
43+
.expect("JSON schema must be either a bool or an object");
44+
for (k, v) in object {
45+
map.serialize_entry(k, v)?;
46+
}
47+
}
48+
}
49+
50+
if let Some(value) = &self.external_docs {
51+
map.serialize_entry("externalDocs", value)?;
52+
}
53+
if let Some(value) = &self.example {
54+
map.serialize_entry("example", value)?;
55+
}
56+
57+
map.end()
58+
}
59+
}
60+
2561
#[cfg(test)]
2662
mod tests {
2763
use crate::openapi::ExternalDocumentation;
2864

2965
use super::SchemaObject;
30-
use schemars::Schema;
66+
use schemars::{json_schema, Schema};
3167
use serde_json::json;
3268

3369
#[test]
@@ -63,4 +99,58 @@ mod tests {
6399
})
64100
);
65101
}
102+
103+
#[test]
104+
fn test_serialize_true_schema() {
105+
let serialized = serde_json::to_value(SchemaObject {
106+
json_schema: json_schema!(true),
107+
external_docs: None,
108+
example: None,
109+
})
110+
.unwrap();
111+
112+
assert_eq!(serialized, json!(true));
113+
}
114+
115+
#[test]
116+
fn test_serialize_true_schema_with_external_docs() {
117+
let serialized = serde_json::to_value(SchemaObject {
118+
json_schema: json_schema!(true),
119+
external_docs: Some(ExternalDocumentation {
120+
description: None,
121+
url: "http://example.org".to_owned(),
122+
extensions: Default::default(),
123+
}),
124+
example: None,
125+
})
126+
.unwrap();
127+
128+
assert_eq!(
129+
serialized,
130+
json!({
131+
"externalDocs": { "url": "http://example.org" },
132+
})
133+
);
134+
}
135+
136+
#[test]
137+
fn test_serialize_false_schema_with_example() {
138+
let example = json!({
139+
"what a useless schema": "even this example is wrong",
140+
});
141+
let serialized = serde_json::to_value(SchemaObject {
142+
json_schema: json_schema!(false),
143+
external_docs: None,
144+
example: Some(example.clone()),
145+
})
146+
.unwrap();
147+
148+
assert_eq!(
149+
serialized,
150+
json!({
151+
"not": {},
152+
"example": example,
153+
})
154+
);
155+
}
66156
}

0 commit comments

Comments
 (0)