1#![allow(unknown_lints)]
2#![deny(missing_docs)]
3#![deny(rustdoc::missing_doc_code_examples)]
4
5use serde_json::Map;
14use serde_json::Value;
15use serde_json::json;
16
17pub struct Flattener<'a> {
25 pub separator: &'a str,
32 pub alt_array_flattening: bool,
39 pub preserve_arrays: bool,
46}
47
48impl<'a> Default for Flattener<'a> {
49 fn default() -> Self {
50 Flattener {
51 separator: ".",
52 alt_array_flattening: false,
53 preserve_arrays: false,
54 }
55 }
56}
57
58impl<'a> Flattener<'a> {
74 pub fn new() -> Self {
82 Flattener {
83 ..Default::default()
84 }
85 }
86
87 fn build_key(&self, prefix: &str, suffix: &str) -> String {
98 let mut key = String::with_capacity(prefix.len() + self.separator.len() + suffix.len());
99 key.push_str(prefix);
100 key.push_str(self.separator);
101 key.push_str(suffix);
102 key
103 }
104
105 pub fn flatten(&self, json: &Value) -> Value {
133 let mut flattened_val = Map::<String, Value>::new();
134 match json {
135 Value::Array(obj_arr) => self.flatten_array(&mut flattened_val, "", obj_arr),
136 Value::Object(obj_val) => self.flatten_object(&mut flattened_val, None, obj_val, false),
137 _ => self.flatten_value(&mut flattened_val, "", json, false),
138 }
139 Value::Object(flattened_val)
140 }
141
142 fn flatten_object(
143 &self,
144 builder: &mut Map<String, Value>,
145 identifier: Option<&str>,
146 obj: &Map<String, Value>,
147 arr: bool,
148 ) {
149 for (k, v) in obj {
150 let expanded_identifier = match identifier {
151 None => k.clone(),
152 Some(id) => self.build_key(id, k),
153 };
154
155 match v {
156 Value::Object(obj_val) => {
157 self.flatten_object(builder, Some(expanded_identifier.as_str()), obj_val, arr)
158 }
159 Value::Array(obj_arr) => {
160 self.flatten_array(builder, expanded_identifier.as_str(), obj_arr)
161 }
162 _ => self.flatten_value(builder, expanded_identifier.as_str(), v, arr),
163 }
164 }
165 }
166
167 fn flatten_array(&self, builder: &mut Map<String, Value>, identifier: &str, obj: &[Value]) {
168 if obj.is_empty() {
170 builder.insert(identifier.to_string(), Value::Array(vec![]));
171 return;
172 }
173
174 use std::fmt::Write;
175 let mut index_buf = String::new();
176
177 for (k, v) in obj.iter().enumerate() {
178 write!(&mut index_buf, "{}", k).unwrap();
179 let with_key = self.build_key(identifier, &index_buf);
180 let current_identifier = if self.preserve_arrays {
181 with_key.as_str()
182 } else {
183 identifier
184 };
185
186 match v {
187 Value::Object(obj_val) => self.flatten_object(
188 builder,
189 Some(current_identifier),
190 obj_val,
191 self.alt_array_flattening,
192 ),
193 Value::Array(obj_arr) => self.flatten_array(builder, current_identifier, obj_arr),
194 _ => self.flatten_value(builder, current_identifier, v, self.alt_array_flattening),
195 }
196
197 index_buf.clear();
198 }
199 }
200
201 fn flatten_value(
202 &self,
203 builder: &mut Map<String, Value>,
204 identifier: &str,
205 obj: &Value,
206 arr: bool,
207 ) {
208 let key = identifier.to_string();
209
210 match builder.entry(key) {
211 serde_json::map::Entry::Occupied(mut entry) => {
212 let value = entry.get_mut();
213 if let Some(array) = value.as_array_mut() {
214 array.push(obj.clone());
215 } else {
216 let existing = value.clone();
217 *value = json!(vec![existing, obj.clone()]);
218 }
219 }
220 serde_json::map::Entry::Vacant(entry) => {
221 entry.insert(if arr {
222 json!(vec![obj.clone()])
223 } else {
224 obj.clone()
225 });
226 }
227 }
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 use serde_json::json;
236
237 #[test]
238 fn serde_example() {
239 let flattener = Flattener::new();
240 let base: Value = json!({
241 "name": "John Doe",
242 "age": 43,
243 "address": {
244 "street": "10 Downing Street",
245 "city": "London"
246 },
247 "phones": [
248 "+44 1234567",
249 "+44 2345678"
250 ]
251 });
252
253 let flat = flattener.flatten(&base);
254
255 assert_eq!(
256 flat,
257 json!({
258 "name": "John Doe",
259 "age": 43,
260 "address.street": "10 Downing Street",
261 "address.city": "London",
262 "phones": [
263 "+44 1234567",
264 "+44 2345678"
265 ]
266 })
267 );
268 }
269
270 #[test]
271 fn collision_object() {
272 let flattener = Flattener::new();
273 let base: Value = json!({
274 "a": {
275 "b": "c",
276 },
277 "a.b": "d",
278 });
279 let flat = flattener.flatten(&base);
280
281 assert_eq!(
282 flat,
283 json!({
284 "a.b": ["c", "d"],
285 })
286 );
287 }
288
289 #[test]
290 fn collision_array() {
291 let flattener = Flattener::new();
292 let flattener_alt = Flattener {
293 alt_array_flattening: true,
294 ..Default::default()
295 };
296
297 let base: Value = json!({
298 "a": [
299 { "b": "c" },
300 { "b": "d", "c": "e" },
301 [35],
302 ],
303 "a.b": "f",
304 });
305
306 let flat = flattener.flatten(&base);
307 let flat_alt = flattener_alt.flatten(&base);
308
309 assert_eq!(
310 flat,
311 json!({
312 "a.b": ["c", "d", "f"],
313 "a.c": "e",
314 "a": 35,
315 })
316 );
317
318 assert_eq!(
319 flat_alt,
320 json!({
321 "a.b": ["c", "d", "f"],
322 "a.c": ["e"],
323 "a": [35],
324 })
325 );
326 }
327
328 #[test]
329 fn nested_arrays() {
330 let flattener = Flattener::new();
331 let flattener_alt = Flattener {
332 alt_array_flattening: true,
333 ..Default::default()
334 };
335
336 let base: Value = json!({
337 "a": [
338 ["b", "c"],
339 { "d": "e" },
340 ["f", "g"],
341 [
342 { "h": "i" },
343 { "d": "j" },
344 ],
345 ["k", "l"],
346 ]
347 });
348 let flat = flattener.flatten(&base);
349 let flat_alt = flattener_alt.flatten(&base);
350
351 assert_eq!(
352 flat,
353 json!({
354 "a": ["b", "c", "f", "g", "k", "l"],
355 "a.d": ["e", "j"],
356 "a.h": "i",
357 })
358 );
359
360 assert_eq!(
361 flat_alt,
362 json!({
363 "a": ["b", "c", "f", "g", "k", "l"],
364 "a.d": ["e", "j"],
365 "a.h": ["i"],
366 })
367 );
368 }
369
370 #[test]
371 fn nested_arrays_and_objects() {
372 let flattener = Flattener::new();
373 let flattener_alt = Flattener {
374 alt_array_flattening: true,
375 ..Default::default()
376 };
377
378 let base: Value = json!({
379 "a": [
380 "b",
381 ["c", "d"],
382 { "e": ["f", "g"] },
383 [
384 { "h": "i" },
385 { "e": ["j", { "z": "y" }] },
386 ],
387 ["l"],
388 "m",
389 ]
390 });
391 let flat = flattener.flatten(&base);
392 let flat_alt = flattener_alt.flatten(&base);
393
394 assert_eq!(
395 flat,
396 json!({
397 "a": ["b", "c", "d", "l", "m"],
398 "a.e": ["f", "g", "j"],
399 "a.h": "i",
400 "a.e.z": "y",
401 })
402 );
403
404 assert_eq!(
405 flat_alt,
406 json!({
407 "a": ["b", "c", "d", "l", "m"],
408 "a.e": ["f", "g", "j"],
409 "a.h": ["i"],
410 "a.e.z": ["y"],
411 })
412 )
413 }
414
415 #[test]
416 fn custom_separator() {
417 let flattener = Flattener {
418 separator: "$",
419 ..Default::default()
420 };
421
422 let input: Value = json!({
423 "a": {
424 "b": 1
425 }});
426
427 let result: Value = flattener.flatten(&input);
428 assert_eq!(
429 result,
430 json!({
431 "a$b": 1
432 })
433 );
434 }
435 #[test]
436 fn object() {
437 let flattener = Flattener::new();
438
439 let input: Value = json!({
440 "a": {
441 "b": "1",
442 "c": "2",
443 "d": "3"
444 }
445 });
446
447 let result: Value = flattener.flatten(&input);
448 assert_eq!(
449 result,
450 json!({
451 "a.b": "1",
452 "a.c": "2",
453 "a.d": "3"
454 })
455 );
456 }
457
458 #[test]
459 fn array() {
460 let flattener = Flattener::new();
461
462 let input: Value = json!({
463 "a": [
464 {"b": "1"},
465 {"b": "2"},
466 {"b": "3"},
467 ]
468 });
469
470 let result: Value = flattener.flatten(&input);
471 assert_eq!(
472 result,
473 json!({
474 "a.b": ["1", "2", "3"]
475 })
476 );
477 }
478
479 #[test]
480 fn array_preserve() {
481 let flattener = Flattener {
482 preserve_arrays: true,
483 ..Default::default()
484 };
485
486 let input: Value = json!({
487 "a": [
488 {"b": "1"},
489 {"b": "2"},
490 {"b": "3"},
491 ]
492 });
493
494 let result: Value = flattener.flatten(&input);
495 assert_eq!(
496 result,
497 json!({
498 "a.0.b": "1",
499 "a.1.b": "2",
500 "a.2.b": "3"
501 })
502 );
503 }
504
505 #[test]
506 fn array_no_collision() {
507 let flattener = Flattener::new();
508 let flattener_alt = Flattener {
509 alt_array_flattening: true,
510 ..Default::default()
511 };
512
513 let input: Value = json!({
514 "a": [
515 {"b": ["1"]}
516 ]
517 });
518
519 let flat: Value = flattener.flatten(&input);
520 let flat_alt = flattener_alt.flatten(&input);
521
522 assert_eq!(
523 flat,
524 json!({
525 "a.b": "1"
526 })
527 );
528
529 assert_eq!(
530 flat_alt,
531 json!({
532 "a.b": ["1"]
533 })
534 );
535 }
536
537 #[test]
539 fn arr_no_key() {
540 let flattener = Flattener::new();
541
542 let input: Value = json!(["a", "b"]);
543
544 let result: Value = flattener.flatten(&input);
545
546 assert_eq!(result, json!({"": ["a", "b"]}));
547 }
548
549 #[test]
551 fn arr_empty_key() {
552 let flattener = Flattener::new();
553
554 let input: Value = json!({
555 "": [
556 "a",
557 "b",
558 {"b": ["1"]}
559 ],
560 });
561 let result: Value = flattener.flatten(&input);
562
563 assert_eq!(result, json!({"": ["a", "b"], ".b": "1"}));
564 }
565
566 #[test]
567 fn only_value() {
568 let flattener = Flattener::new();
569
570 let input: Value = json!("abc");
571 let result: Value = flattener.flatten(&input);
572
573 assert_eq!(result, json!({"": "abc"}));
574 }
575
576 #[test]
577 fn nested_array_preserve() {
578 let flattener = Flattener {
579 preserve_arrays: true,
580 ..Default::default()
581 };
582
583 let input: Value = json!({
584 "a": [
585 "b",
586 ["c", "d"],
587 { "e": ["f", "g"] },
588 [
589 { "h": "i" },
590 { "e": ["j", { "z": "y" }] }
591 ],
592 ["l"],
593 "m"
594 ]
595 });
596
597 let result: Value = flattener.flatten(&input);
598
599 assert_eq!(
600 result,
601 json!({
602 "a.0": "b",
603 "a.1.0": "c",
604 "a.1.1": "d",
605 "a.2.e.0": "f",
606 "a.2.e.1": "g",
607 "a.3.0.h": "i",
608 "a.3.1.e.0": "j",
609 "a.3.1.e.1.z": "y",
610 "a.4.0": "l",
611 "a.5": "m"
612 })
613 )
614 }
615
616 #[test]
617 fn keys_with_separator() {
618 let flattener = Flattener::new();
619
620 let input: Value = json!({
621 "a.b": "value1",
622 "a": {
623 "b": "value2"
624 }
625 });
626
627 let result = flattener.flatten(&input);
628
629 match &result["a.b"] {
632 Value::Array(arr) => {
633 assert_eq!(arr.len(), 2);
634 assert!(arr.contains(&Value::String("value1".to_string())));
635 assert!(arr.contains(&Value::String("value2".to_string())));
636 }
637 _ => panic!("Expected array for collided keys"),
638 }
639 }
640
641 #[test]
642 fn null_values() {
643 let flattener = Flattener::new();
644
645 let input: Value = json!({
646 "a": null,
647 "b": {
648 "c": null,
649 "d": "value"
650 },
651 "e": [null, "text", null]
652 });
653
654 let result = flattener.flatten(&input);
655
656 assert_eq!(
657 result,
658 json!({
659 "a": null,
660 "b.c": null,
661 "b.d": "value",
662 "e": [null, "text", null]
663 })
664 );
665 }
666
667 #[test]
668 fn collision_stress_test() {
669 let flattener = Flattener::new();
670
671 let input: Value = json!({
673 "x": "value1",
674 "data": [
675 { "x": "value2" },
676 { "x": "value3" },
677 { "x": "value4" },
678 { "x": "value5" }
679 ]
680 });
681
682 let result = flattener.flatten(&input);
683
684 assert!(result.get("x").is_some());
686 assert!(result.get("data.x").is_some());
687
688 match &result["x"] {
689 Value::String(s) => assert_eq!(s, "value1"),
690 _ => panic!("Expected string for 'x'"),
691 }
692
693 match &result["data.x"] {
694 Value::Array(arr) => {
695 assert_eq!(arr.len(), 4);
696 assert_eq!(arr[0], Value::String("value2".to_string()));
697 assert_eq!(arr[1], Value::String("value3".to_string()));
698 assert_eq!(arr[2], Value::String("value4".to_string()));
699 assert_eq!(arr[3], Value::String("value5".to_string()));
700 }
701 _ => panic!("Expected array for 'data.x'"),
702 }
703 }
704}