Skip to main content

jsonschema_value/
serde_json.rs

1//! `serde_json::Value` representation: borrow-only accessors that monomorphize to direct `&Value` code.
2
3use std::borrow::Cow;
4
5use serde_json::{Map, Value};
6
7use crate::{cmp, types::JsonType};
8
9use super::{Array, Json, JsonNumber, Node, NodeIdentity, Object};
10
11pub struct SerdeJson;
12
13impl Json for SerdeJson {
14    type Node<'a> = &'a Value;
15    type PreparedKey = String;
16    type StringBuffer = Value;
17
18    fn prepare_key(key: &str) -> String {
19        key.to_owned()
20    }
21
22    fn with_string_node<T>(buffer: &mut Value, string: &str, f: impl FnOnce(&Value) -> T) -> T {
23        // Reuses the buffer's allocation across calls instead of building a fresh `String` per name.
24        if let Value::String(existing) = buffer {
25            existing.clear();
26            existing.push_str(string);
27        } else {
28            *buffer = Value::String(string.to_owned());
29        }
30        f(buffer)
31    }
32}
33
34impl JsonNumber for serde_json::Number {
35    fn as_u64(&self) -> Option<u64> {
36        serde_json::Number::as_u64(self)
37    }
38    fn as_i64(&self) -> Option<i64> {
39        serde_json::Number::as_i64(self)
40    }
41    fn as_f64(&self) -> Option<f64> {
42        serde_json::Number::as_f64(self)
43    }
44    fn as_str(&self) -> Cow<'_, str> {
45        Cow::Owned(self.to_string())
46    }
47    fn to_number(&self) -> Cow<'_, serde_json::Number> {
48        Cow::Borrowed(self)
49    }
50}
51
52impl JsonNumber for &serde_json::Number {
53    fn as_u64(&self) -> Option<u64> {
54        serde_json::Number::as_u64(self)
55    }
56    fn as_i64(&self) -> Option<i64> {
57        serde_json::Number::as_i64(self)
58    }
59    fn as_f64(&self) -> Option<f64> {
60        serde_json::Number::as_f64(self)
61    }
62    fn as_str(&self) -> Cow<'_, str> {
63        Cow::Owned(self.to_string())
64    }
65    fn to_number(&self) -> Cow<'_, serde_json::Number> {
66        Cow::Borrowed(self)
67    }
68}
69
70impl<'a> Node<'a, SerdeJson> for &'a Value {
71    type Object = &'a Map<String, Value>;
72    type Array = &'a [Value];
73    type Number = &'a serde_json::Number;
74
75    fn as_object(&self) -> Option<&'a Map<String, Value>> {
76        match self {
77            Value::Object(members) => Some(members),
78            _ => None,
79        }
80    }
81
82    fn as_array(&self) -> Option<&'a [Value]> {
83        match self {
84            Value::Array(items) => Some(items),
85            _ => None,
86        }
87    }
88
89    fn as_string(&self) -> Option<Cow<'a, str>> {
90        match self {
91            Value::String(string) => Some(Cow::Borrowed(string)),
92            _ => None,
93        }
94    }
95
96    fn as_number(&self) -> Option<&'a serde_json::Number> {
97        match self {
98            Value::Number(number) => Some(number),
99            _ => None,
100        }
101    }
102
103    fn as_boolean(&self) -> Option<bool> {
104        match self {
105            Value::Bool(boolean) => Some(*boolean),
106            _ => None,
107        }
108    }
109
110    fn is_null(&self) -> bool {
111        matches!(self, Value::Null)
112    }
113
114    fn json_type(&self) -> JsonType {
115        match self {
116            Value::Null => JsonType::Null,
117            Value::Bool(_) => JsonType::Boolean,
118            Value::Number(_) => JsonType::Number,
119            Value::String(_) => JsonType::String,
120            Value::Array(_) => JsonType::Array,
121            Value::Object(_) => JsonType::Object,
122        }
123    }
124
125    fn string_length(&self) -> Option<u64> {
126        match self {
127            // SIMD-accelerated counting; the default `chars().count()` is measurably slower.
128            Value::String(string) => Some(bytecount::num_chars(string.as_bytes()) as u64),
129            _ => None,
130        }
131    }
132
133    fn equals_value(&self, expected: &Value) -> bool {
134        cmp::equal(self, expected)
135    }
136
137    fn to_value(&self) -> Cow<'a, Value> {
138        Cow::Borrowed(self)
139    }
140
141    fn identity(&self) -> Option<NodeIdentity> {
142        Some(NodeIdentity::new(std::ptr::from_ref::<Value>(self) as usize))
143    }
144}
145
146pub struct SerdeMembersIter<'a>(serde_json::map::Iter<'a>);
147
148impl<'a> Iterator for SerdeMembersIter<'a> {
149    type Item = (&'a str, &'a Value);
150
151    fn next(&mut self) -> Option<Self::Item> {
152        self.0.next().map(|(key, value)| (key.as_str(), value))
153    }
154}
155
156impl<'a> Object<'a, SerdeJson> for &'a Map<String, Value> {
157    type Node = &'a Value;
158    type MemberName = &'a str;
159    type MembersIter = SerdeMembersIter<'a>;
160
161    fn len(&self) -> usize {
162        Map::len(self)
163    }
164
165    fn get(&self, key: &String) -> Option<&'a Value> {
166        (*self).get(key.as_str())
167    }
168
169    fn members(&self) -> SerdeMembersIter<'a> {
170        SerdeMembersIter((*self).iter())
171    }
172}
173
174impl<'a> Array<'a, SerdeJson> for &'a [Value] {
175    type Node = &'a Value;
176    type ElementsIter = std::slice::Iter<'a, Value>;
177
178    fn len(&self) -> usize {
179        <[Value]>::len(self)
180    }
181
182    fn elements(&self) -> std::slice::Iter<'a, Value> {
183        (*self).iter()
184    }
185
186    fn is_unique(&self) -> bool {
187        crate::unique::is_unique(self)
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    use std::borrow::Cow;
194
195    use serde_json::{json, Value};
196    use test_case::test_case;
197
198    use super::{
199        super::{Array, Json, JsonNumber, Node, Object},
200        SerdeJson,
201    };
202    use crate::types::JsonType;
203
204    // Generic on purpose: inherent `Value` methods shadow the trait on concrete `&Value`, and keyword code
205    // only ever sees `F::Node<'_>`.
206    fn assert_document_accessors<F: Json>(node: &F::Node<'_>) {
207        let object = node.as_object().expect("object");
208
209        let member = |name: &str| object.get(&F::prepare_key(name)).expect("present");
210
211        assert!(member("object").as_object().is_some());
212        assert_eq!(member("array").as_array().expect("array").len(), 3);
213        assert_eq!(member("string").as_string().as_deref(), Some("héllo"));
214        assert_eq!(member("string").json_type(), JsonType::String);
215        assert_eq!(member("string").string_length(), Some(5));
216        assert_eq!(
217            member("integer").as_number().expect("number").as_u64(),
218            Some(42)
219        );
220        assert_eq!(
221            member("float").as_number().expect("number").as_f64(),
222            Some(1.5)
223        );
224        assert_eq!(member("boolean").as_boolean(), Some(true));
225        assert!(member("null").is_null());
226        assert_eq!(member("null").json_type(), JsonType::Null);
227        assert!(object.get(&F::prepare_key("missing")).is_none());
228    }
229
230    #[test]
231    fn accessors_match_value_kinds() {
232        let document = json!({
233            "object": {"a": 1},
234            "array": [1, 2, 3],
235            "string": "héllo",
236            "integer": 42,
237            "float": 1.5,
238            "boolean": true,
239            "null": null
240        });
241        assert_document_accessors::<SerdeJson>(&&document);
242    }
243
244    #[test_case(&json!(1), &json!(1.0), true; "integer equals float")]
245    #[test_case(&json!(1.0), &json!(1), true; "float equals integer")]
246    #[test_case(&json!(1), &json!(2), false; "different integers")]
247    #[test_case(&json!(true), &json!(1), false; "boolean is not a number")]
248    #[test_case(&json!({"a": [1, {"b": 1.0}]}), &json!({"a": [1.0, {"b": 1}]}), true; "nested numeric equality")]
249    #[test_case(&json!({"a": [1, {"b": 1.0}]}), &json!({"a": [1, {"b": 2}]}), false; "nested mismatch")]
250    fn equals_value_follows_json_schema_semantics(left: &Value, right: &Value, expected: bool) {
251        assert_eq!(left.equals_value(right), expected);
252    }
253
254    #[test_case("", 0; "empty")]
255    #[test_case("héllo", 5; "multi-byte")]
256    #[test_case("🦀🦀", 2; "astral plane")]
257    fn string_length_counts_code_points(input: &str, expected: u64) {
258        let value = json!(input);
259        assert_eq!((&value).string_length(), Some(expected));
260    }
261
262    #[test]
263    fn to_value_borrows() {
264        let document = json!({"a": 1});
265        let node = &document;
266        assert!(matches!(node.to_value(), Cow::Borrowed(_)));
267    }
268
269    fn assert_identity_stability<F: Json>(node: &F::Node<'_>) {
270        let child = node
271            .as_object()
272            .expect("object")
273            .get(&F::prepare_key("a"))
274            .expect("present");
275        assert_eq!(node.identity(), node.identity());
276        assert_ne!(node.identity(), child.identity());
277    }
278
279    #[test]
280    fn identity_is_stable_per_node() {
281        let document = json!({"a": {"b": 1}});
282        assert_identity_stability::<SerdeJson>(&&document);
283    }
284
285    fn assert_iteration_order<F: Json>(node: &F::Node<'_>) {
286        let object = node.as_object().expect("object");
287        let names: Vec<_> = object
288            .members()
289            .map(|(name, _)| name.as_ref().to_owned())
290            .collect();
291        assert_eq!(names, ["a", "b"]);
292
293        let items = object
294            .get(&F::prepare_key("b"))
295            .expect("present")
296            .as_array()
297            .expect("array");
298        let collected: Vec<Option<u64>> = items
299            .elements()
300            .map(|item| item.as_number().and_then(|number| number.as_u64()))
301            .collect();
302        assert_eq!(collected, [Some(10), Some(20)]);
303    }
304
305    #[test]
306    fn members_and_items_iterate_in_order() {
307        let document = json!({"a": 1, "b": [10, 20]});
308        assert_iteration_order::<SerdeJson>(&&document);
309    }
310}