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