1use serde_json::Value;
2
3pub fn canonical_json_bytes(value: &Value) -> Result<Vec<u8>, serde_json::Error> {
6 let sorted = sort_value(value);
7 serde_json::to_vec(&sorted)
8}
9
10fn sort_value(value: &Value) -> Value {
11 match value {
12 Value::Object(map) => {
13 let mut pairs: Vec<(&String, Value)> =
14 map.iter().map(|(k, v)| (k, sort_value(v))).collect();
15 pairs.sort_by(|a, b| a.0.cmp(b.0));
16 let sorted_map: serde_json::Map<String, Value> =
17 pairs.into_iter().map(|(k, v)| (k.clone(), v)).collect();
18 Value::Object(sorted_map)
19 }
20 Value::Array(arr) => Value::Array(arr.iter().map(sort_value).collect()),
21 other => other.clone(),
22 }
23}
24
25#[cfg(test)]
26mod tests {
27 use super::*;
28
29 #[test]
30 fn keys_sorted_lexicographically() {
31 let input: Value = serde_json::from_str(r#"{"z":1,"a":2,"m":3}"#).unwrap();
32 let bytes = canonical_json_bytes(&input).unwrap();
33 let output = String::from_utf8(bytes).unwrap();
34 assert_eq!(output, r#"{"a":2,"m":3,"z":1}"#);
35 }
36
37 #[test]
38 fn nested_objects_sorted() {
39 let input: Value = serde_json::from_str(r#"{"b":{"z":1,"a":2},"a":1}"#).unwrap();
40 let bytes = canonical_json_bytes(&input).unwrap();
41 let output = String::from_utf8(bytes).unwrap();
42 assert_eq!(output, r#"{"a":1,"b":{"a":2,"z":1}}"#);
43 }
44
45 #[test]
46 fn arrays_preserve_order() {
47 let input: Value = serde_json::from_str(r#"{"a":[3,1,2]}"#).unwrap();
48 let bytes = canonical_json_bytes(&input).unwrap();
49 let output = String::from_utf8(bytes).unwrap();
50 assert_eq!(output, r#"{"a":[3,1,2]}"#);
51 }
52
53 #[test]
54 fn scalars_unchanged() {
55 let input: Value = serde_json::from_str(r#""hello""#).unwrap();
56 let bytes = canonical_json_bytes(&input).unwrap();
57 let output = String::from_utf8(bytes).unwrap();
58 assert_eq!(output, r#""hello""#);
59 }
60}