Skip to main content

api_bones/serde/
json_string.rs

1//! Serde module for fields that carry stringified JSON on the wire.
2//!
3//! Use `#[serde(with = "api_bones::serde::json_string")]` to transparently
4//! serialize any `T: Serialize` as a JSON string and deserialize a JSON
5//! string back into `T: DeserializeOwned`.
6//!
7//! ## Wire format
8//!
9//! ```json
10//! { "payload": "{\"key\":\"value\"}" }
11//! ```
12//!
13//! ## Examples
14//!
15//! ```rust
16//! use serde::{Deserialize, Serialize};
17//!
18//! #[derive(Debug, PartialEq, Serialize, Deserialize)]
19//! struct Inner {
20//!     key: String,
21//! }
22//!
23//! #[derive(Debug, PartialEq, Serialize, Deserialize)]
24//! struct Outer {
25//!     #[serde(with = "api_bones::serde::json_string")]
26//!     payload: Inner,
27//! }
28//!
29//! let outer = Outer { payload: Inner { key: "value".to_string() } };
30//! let json = serde_json::to_string(&outer).unwrap();
31//! assert_eq!(json, r#"{"payload":"{\"key\":\"value\"}"}"#);
32//!
33//! let back: Outer = serde_json::from_str(&json).unwrap();
34//! assert_eq!(back, outer);
35//! ```
36
37#[cfg(all(not(feature = "std"), feature = "alloc"))]
38use alloc::string::String;
39
40use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned};
41
42/// Serialize `value` as a JSON string.
43///
44/// # Errors
45///
46/// Returns a serialization error if `value` cannot be serialized to JSON or
47/// if the serializer rejects the resulting string.
48pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
49where
50    T: Serialize,
51    S: Serializer,
52{
53    let json = serde_json::to_string(value).map_err(serde::ser::Error::custom)?;
54    serializer.serialize_str(&json)
55}
56
57/// Deserialize a JSON string into `T`.
58///
59/// # Errors
60///
61/// Returns a deserialization error if the input is not a string or if the
62/// string is not valid JSON for `T`.
63pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
64where
65    T: DeserializeOwned,
66    D: Deserializer<'de>,
67{
68    let s = String::deserialize(deserializer)?;
69    serde_json::from_str(&s).map_err(serde::de::Error::custom)
70}
71
72#[cfg(test)]
73mod tests {
74    use serde::{Deserialize, Serialize};
75
76    #[derive(Debug, PartialEq, Serialize, Deserialize)]
77    struct Inner {
78        key: String,
79    }
80
81    #[derive(Debug, PartialEq, Serialize, Deserialize)]
82    struct Outer {
83        #[serde(with = "super")]
84        payload: Inner,
85    }
86
87    #[derive(Debug, PartialEq, Serialize, Deserialize)]
88    struct NumWrapper {
89        #[serde(with = "super")]
90        value: u32,
91    }
92
93    #[test]
94    fn serialize_struct() {
95        let outer = Outer {
96            payload: Inner {
97                key: "value".to_string(),
98            },
99        };
100        let json = serde_json::to_string(&outer).unwrap();
101        assert_eq!(json, r#"{"payload":"{\"key\":\"value\"}"}"#);
102    }
103
104    #[test]
105    fn deserialize_struct() {
106        let json = r#"{"payload":"{\"key\":\"value\"}"}"#;
107        let outer: Outer = serde_json::from_str(json).unwrap();
108        assert_eq!(
109            outer.payload,
110            Inner {
111                key: "value".to_string()
112            }
113        );
114    }
115
116    #[test]
117    fn roundtrip() {
118        let original = Outer {
119            payload: Inner {
120                key: "hello world".to_string(),
121            },
122        };
123        let json = serde_json::to_string(&original).unwrap();
124        let back: Outer = serde_json::from_str(&json).unwrap();
125        assert_eq!(back, original);
126    }
127
128    #[test]
129    fn serialize_number() {
130        let w = NumWrapper { value: 42 };
131        let json = serde_json::to_string(&w).unwrap();
132        assert_eq!(json, r#"{"value":"42"}"#);
133    }
134
135    #[test]
136    fn deserialize_number() {
137        let w: NumWrapper = serde_json::from_str(r#"{"value":"42"}"#).unwrap();
138        assert_eq!(w.value, 42);
139    }
140
141    #[test]
142    fn deserialize_invalid_json_string() {
143        // The outer string is valid, but its content is not valid JSON for Inner
144        let result: Result<Outer, _> = serde_json::from_str(r#"{"payload":"not json"}"#);
145        assert!(result.is_err());
146    }
147
148    #[test]
149    fn deserialize_non_string_field() {
150        // The field must be a JSON string, not a raw object
151        let result: Result<Outer, _> = serde_json::from_str(r#"{"payload":{"key":"value"}}"#);
152        assert!(result.is_err());
153    }
154}