19
19
use std:: collections:: BTreeMap ;
20
20
use std:: num:: NonZeroU32 ;
21
21
22
+ use anyhow:: anyhow;
22
23
use chrono:: { DateTime , Duration , Utc } ;
23
24
use serde_json:: map:: Map ;
24
25
use serde_json:: value:: Value ;
@@ -273,52 +274,83 @@ pub fn flatten_array_objects(
273
274
/// Recursively flattens a JSON value.
274
275
/// - If the value is an array, it flattens all elements of the array.
275
276
/// - If the value is an object, it flattens all nested objects and arrays.
277
+ /// - If the JSON value is heavily nested (with more than 4 levels of hierarchy), returns error
276
278
/// - Otherwise, it returns the value itself in a vector.
277
279
///
278
280
/// Examples:
279
281
/// 1. `{"a": 1}` ~> `[{"a": 1}]`
280
282
/// 2. `[{"a": 1}, {"b": 2}]` ~> `[{"a": 1}, {"b": 2}]`
281
283
/// 3. `[{"a": [{"b": 1}, {"c": 2}]}]` ~> `[{"a": {"b": 1)}}, {"a": {"c": 2)}}]`
282
- /// 3. `{"a": [{"b": 1}, {"c": 2}], "d": {"e": 4}}` ~> `[{"a": {"b":1}, "d": {"e":4}}, {"a": {"c":2}, "d": {"e":4}}]`
283
- pub fn flatten_json ( value : & Value ) -> Vec < Value > {
284
+ /// 4. `{"a": [{"b": 1}, {"c": 2}], "d": {"e": 4}}` ~> `[{"a": {"b":1}, "d": {"e":4}}, {"a": {"c":2}, "d": {"e":4}}]`
285
+ /// 5. `{"a":{"b":{"c":{"d":{"e":["a","b"]}}}}}` ~> returns error - heavily nested, cannot flatten this JSON
286
+ pub fn flatten_json ( value : & Value ) -> Result < Vec < Value > , anyhow:: Error > {
287
+ if has_more_than_four_levels ( value, 1 ) {
288
+ return Err ( anyhow ! ( "heavily nested, cannot flatten this JSON" ) ) ;
289
+ }
290
+
284
291
match value {
285
- Value :: Array ( arr) => arr. iter ( ) . flat_map ( flatten_json) . collect ( ) ,
286
- Value :: Object ( map) => map
292
+ Value :: Array ( arr) => Ok ( arr
287
293
. iter ( )
288
- . fold ( vec ! [ Map :: new( ) ] , |results, ( key, val) | match val {
289
- Value :: Array ( arr) => arr
290
- . iter ( )
291
- . flat_map ( flatten_json)
292
- . flat_map ( |flattened_item| {
293
- results. iter ( ) . map ( move |result| {
294
- let mut new_obj = result. clone ( ) ;
295
- new_obj. insert ( key. clone ( ) , flattened_item. clone ( ) ) ;
296
- new_obj
294
+ . flat_map ( |flatten_item| flatten_json ( flatten_item) . unwrap_or_default ( ) )
295
+ . collect ( ) ) ,
296
+ Value :: Object ( map) => {
297
+ let results = map
298
+ . iter ( )
299
+ . fold ( vec ! [ Map :: new( ) ] , |results, ( key, val) | match val {
300
+ Value :: Array ( arr) => arr
301
+ . iter ( )
302
+ . flat_map ( |flatten_item| flatten_json ( flatten_item) . unwrap_or_default ( ) )
303
+ . flat_map ( |flattened_item| {
304
+ results. iter ( ) . map ( move |result| {
305
+ let mut new_obj = result. clone ( ) ;
306
+ new_obj. insert ( key. clone ( ) , flattened_item. clone ( ) ) ;
307
+ new_obj
308
+ } )
309
+ } )
310
+ . collect ( ) ,
311
+ Value :: Object ( _) => flatten_json ( val)
312
+ . unwrap_or_default ( )
313
+ . iter ( )
314
+ . flat_map ( |nested_result| {
315
+ results. iter ( ) . map ( move |result| {
316
+ let mut new_obj = result. clone ( ) ;
317
+ new_obj. insert ( key. clone ( ) , nested_result. clone ( ) ) ;
318
+ new_obj
319
+ } )
297
320
} )
298
- } )
299
- . collect ( ) ,
300
- Value :: Object ( _) => flatten_json ( val)
301
- . iter ( )
302
- . flat_map ( |nested_result| {
303
- results. iter ( ) . map ( move |result| {
304
- let mut new_obj = result. clone ( ) ;
305
- new_obj. insert ( key. clone ( ) , nested_result. clone ( ) ) ;
306
- new_obj
321
+ . collect ( ) ,
322
+ _ => results
323
+ . into_iter ( )
324
+ . map ( |mut result| {
325
+ result. insert ( key. clone ( ) , val. clone ( ) ) ;
326
+ result
307
327
} )
308
- } )
309
- . collect ( ) ,
310
- _ => results
311
- . into_iter ( )
312
- . map ( |mut result| {
313
- result. insert ( key. clone ( ) , val. clone ( ) ) ;
314
- result
315
- } )
316
- . collect ( ) ,
317
- } )
318
- . into_iter ( )
319
- . map ( Value :: Object )
320
- . collect ( ) ,
321
- _ => vec ! [ value. clone( ) ] ,
328
+ . collect ( ) ,
329
+ } ) ;
330
+
331
+ Ok ( results. into_iter ( ) . map ( Value :: Object ) . collect ( ) )
332
+ }
333
+ _ => Ok ( vec ! [ value. clone( ) ] ) ,
334
+ }
335
+ }
336
+
337
+ /// recursively checks the level of nesting for the serde Value
338
+ /// if Value has more than 4 levels of hierarchy, returns true
339
+ /// example -
340
+ /// 1. `{"a":{"b":{"c":{"d":{"e":["a","b"]}}}}}` ~> returns true
341
+ /// 2. `{"a": [{"b": 1}, {"c": 2}], "d": {"e": 4}}` ~> returns false
342
+ fn has_more_than_four_levels ( value : & Value , current_level : usize ) -> bool {
343
+ if current_level > 4 {
344
+ return true ;
345
+ }
346
+ match value {
347
+ Value :: Array ( arr) => arr
348
+ . iter ( )
349
+ . any ( |item| has_more_than_four_levels ( item, current_level) ) ,
350
+ Value :: Object ( map) => map
351
+ . values ( )
352
+ . any ( |val| has_more_than_four_levels ( val, current_level + 1 ) ) ,
353
+ _ => false ,
322
354
}
323
355
}
324
356
@@ -340,7 +372,9 @@ pub fn convert_to_array(flattened: Vec<Value>) -> Result<Value, JsonFlattenError
340
372
341
373
#[ cfg( test) ]
342
374
mod tests {
343
- use crate :: utils:: json:: flatten:: flatten_array_objects;
375
+ use crate :: utils:: json:: flatten:: {
376
+ flatten_array_objects, flatten_json, has_more_than_four_levels,
377
+ } ;
344
378
345
379
use super :: { flatten, JsonFlattenError } ;
346
380
use serde_json:: { json, Map , Value } ;
@@ -598,4 +632,29 @@ mod tests {
598
632
JsonFlattenError :: FieldContainsPeriod ( _)
599
633
) ;
600
634
}
635
+
636
+ #[ test]
637
+ fn unacceptable_levels_of_nested_json ( ) {
638
+ let value = json ! ( { "a" : { "b" : { "c" : { "d" : { "e" : [ "a" , "b" ] } } } } } ) ;
639
+ assert_eq ! ( has_more_than_four_levels( & value, 1 ) , true ) ;
640
+ }
641
+
642
+ #[ test]
643
+ fn acceptable_levels_of_nested_json ( ) {
644
+ let value = json ! ( { "a" : { "b" : { "e" : [ "a" , "b" ] } } } ) ;
645
+ assert_eq ! ( has_more_than_four_levels( & value, 1 ) , false ) ;
646
+ }
647
+
648
+ #[ test]
649
+ fn flatten_json_success ( ) {
650
+ let value = json ! ( { "a" : { "b" : { "e" : [ "a" , "b" ] } } } ) ;
651
+ let expected = vec ! [ json!( { "a" : { "b" : { "e" : "a" } } } ) , json!( { "a" : { "b" : { "e" : "b" } } } ) ] ;
652
+ assert_eq ! ( flatten_json( & value) . unwrap( ) , expected) ;
653
+ }
654
+
655
+ #[ test]
656
+ fn flatten_json_error ( ) {
657
+ let value = json ! ( { "a" : { "b" : { "c" : { "d" : { "e" : [ "a" , "b" ] } } } } } ) ;
658
+ assert ! ( flatten_json( & value) . is_err( ) ) ;
659
+ }
601
660
}
0 commit comments