1#![doc = include_str!("../README.md")]
2
3use serde_json::{json, Map, Value};
4
5pub fn flatten(json: &Map<String, Value>) -> Map<String, Value> {
6 let mut obj = Map::new();
7 insert_object(&mut obj, None, json);
8 obj
9}
10
11fn insert_object(
12 base_json: &mut Map<String, Value>,
13 base_key: Option<&str>,
14 object: &Map<String, Value>,
15) {
16 for (key, value) in object {
17 let new_key = base_key.map_or_else(|| key.clone(), |base_key| format!("{base_key}.{key}"));
18
19 if let Some(array) = value.as_array() {
20 insert_array(base_json, &new_key, array);
21 } else if let Some(object) = value.as_object() {
22 insert_object(base_json, Some(&new_key), object);
23 } else {
24 insert_value(base_json, &new_key, value.clone());
25 }
26 }
27}
28
29fn insert_array(base_json: &mut Map<String, Value>, base_key: &str, array: &Vec<Value>) {
30 for value in array {
31 if let Some(object) = value.as_object() {
32 insert_object(base_json, Some(base_key), object);
33 } else if let Some(sub_array) = value.as_array() {
34 insert_array(base_json, base_key, sub_array);
35 } else {
36 insert_value(base_json, base_key, value.clone());
37 }
38 }
39}
40
41fn insert_value(base_json: &mut Map<String, Value>, key: &str, to_insert: Value) {
42 debug_assert!(!to_insert.is_object());
43 debug_assert!(!to_insert.is_array());
44
45 if let Some(value) = base_json.get_mut(key) {
47 if let Some(array) = value.as_array_mut() {
49 array.push(to_insert);
50 } else {
52 let value = std::mem::take(value);
53 base_json[key] = json!([value, to_insert]);
54 }
55 } else {
57 base_json.insert(key.to_string(), json!(to_insert));
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn no_flattening() {
67 let mut base: Value = json!({
68 "id": "287947",
69 "title": "Shazam!",
70 "release_date": 1553299200,
71 "genres": [
72 "Action",
73 "Comedy",
74 "Fantasy"
75 ]
76 });
77 let json = std::mem::take(base.as_object_mut().unwrap());
78 let flat = flatten(&json);
79
80 println!(
81 "got:\n{}\nexpected:\n{}\n",
82 serde_json::to_string_pretty(&flat).unwrap(),
83 serde_json::to_string_pretty(&json).unwrap()
84 );
85
86 assert_eq!(flat, json);
87 }
88
89 #[test]
90 fn flatten_object() {
91 let mut base: Value = json!({
92 "a": {
93 "b": "c",
94 "d": "e",
95 "f": "g"
96 }
97 });
98 let json = std::mem::take(base.as_object_mut().unwrap());
99 let flat = flatten(&json);
100
101 assert_eq!(
102 &flat,
103 json!({
104 "a.b": "c",
105 "a.d": "e",
106 "a.f": "g"
107 })
108 .as_object()
109 .unwrap()
110 );
111 }
112
113 #[test]
114 fn flatten_array() {
115 let mut base: Value = json!({
116 "a": [
117 { "b": "c" },
118 { "b": "d" },
119 { "b": "e" },
120 ]
121 });
122 let json = std::mem::take(base.as_object_mut().unwrap());
123 let flat = flatten(&json);
124
125 assert_eq!(
126 &flat,
127 json!({
128 "a.b": ["c", "d", "e"],
129 })
130 .as_object()
131 .unwrap()
132 );
133
134 let mut base: Value = json!({
136 "a": [
137 42,
138 { "b": "c" },
139 { "b": "d" },
140 { "b": "e" },
141 ]
142 });
143 let json = std::mem::take(base.as_object_mut().unwrap());
144 let flat = flatten(&json);
145
146 assert_eq!(
147 &flat,
148 json!({
149 "a": 42,
150 "a.b": ["c", "d", "e"],
151 })
152 .as_object()
153 .unwrap()
154 );
155 }
156
157 #[test]
158 fn collision_with_object() {
159 let mut base: Value = json!({
160 "a": {
161 "b": "c",
162 },
163 "a.b": "d",
164 });
165 let json = std::mem::take(base.as_object_mut().unwrap());
166 let flat = flatten(&json);
167
168 assert_eq!(
169 &flat,
170 json!({
171 "a.b": ["c", "d"],
172 })
173 .as_object()
174 .unwrap()
175 );
176 }
177
178 #[test]
179 fn collision_with_array() {
180 let mut base: Value = json!({
181 "a": [
182 { "b": "c" },
183 { "b": "d", "c": "e" },
184 [35],
185 ],
186 "a.b": "f",
187 });
188 let json = std::mem::take(base.as_object_mut().unwrap());
189 let flat = flatten(&json);
190
191 assert_eq!(
192 &flat,
193 json!({
194 "a.b": ["c", "d", "f"],
195 "a.c": "e",
196 "a": 35,
197 })
198 .as_object()
199 .unwrap()
200 );
201 }
202
203 #[test]
204 fn flatten_nested_arrays() {
205 let mut base: Value = json!({
206 "a": [
207 ["b", "c"],
208 { "d": "e" },
209 ["f", "g"],
210 [
211 { "h": "i" },
212 { "d": "j" },
213 ],
214 ["k", "l"],
215 ]
216 });
217 let json = std::mem::take(base.as_object_mut().unwrap());
218 let flat = flatten(&json);
219
220 assert_eq!(
221 &flat,
222 json!({
223 "a": ["b", "c", "f", "g", "k", "l"],
224 "a.d": ["e", "j"],
225 "a.h": "i",
226 })
227 .as_object()
228 .unwrap()
229 );
230 }
231
232 #[test]
233 fn flatten_nested_arrays_and_objects() {
234 let mut base: Value = json!({
235 "a": [
236 "b",
237 ["c", "d"],
238 { "e": ["f", "g"] },
239 [
240 { "h": "i" },
241 { "e": ["j", { "z": "y" }] },
242 ],
243 ["l"],
244 "m",
245 ]
246 });
247 let json = std::mem::take(base.as_object_mut().unwrap());
248 let flat = flatten(&json);
249
250 println!("{}", serde_json::to_string_pretty(&flat).unwrap());
251
252 assert_eq!(
253 &flat,
254 json!({
255 "a": ["b", "c", "d", "l", "m"],
256 "a.e": ["f", "g", "j"],
257 "a.h": "i",
258 "a.e.z": "y",
259 })
260 .as_object()
261 .unwrap()
262 );
263 }
264}