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