json_unflattening/
flattening.rs

1// Copyright 2023 Fondazione LINKS
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7//     http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15
16
17use serde_json::{Value, Map, json};
18use crate::errors;
19
20
21/// Flattens a JSON Value into a key-value map.
22///
23/// # Arguments
24///
25/// * `value` - The JSON Value to be flattened (`serde_json::Value`).
26///
27/// # Returns
28///
29/// A Result containing a flattened JSON structure (`serde_json::Map<String, Value>`) or an error (`errors::Error`).
30///
31pub fn flatten(value: &Value) -> Result<Map<String, Value>, errors::Error> {
32    let mut flattened_json = Map::<String, Value>::new();
33
34    match value {
35        Value::Object(map) => {
36            if map.is_empty() {
37                return Ok(flattened_json);
38            }
39            flatten_object(&mut flattened_json, None, map)?;
40        }
41        _ => return Err(errors::Error::NotAnObject),
42    }
43    
44    
45    Ok(flattened_json)
46}
47
48fn flatten_object(result: &mut Map<String, Value>, property: Option<&str>, nested_json: &Map<String, Value>) -> Result<(), errors::Error>{
49    for (prop, value) in nested_json {
50        let flattened_prop = property.map_or_else(|| prop.clone(), |parent_key| format!("{}.{}", parent_key, prop));
51
52        match value {
53            Value::Array(array) => flatten_array(result, &flattened_prop, array),
54            Value::Object(sub_json) => flatten_object(result, Some(&flattened_prop), sub_json),
55            _ => flatten_value(result, &flattened_prop, value.clone()),
56        }?
57    }
58
59    Ok(())
60}
61
62fn flatten_array(result: &mut Map<String, Value>, property: &str, array: &Vec<Value>) -> Result<(), errors::Error> {
63    for (i, value) in array.iter().enumerate() {
64        let flattened_prop = format!("{}[{}]", property, i);
65
66        match value {
67            Value::Object(sub_json) => flatten_object(result, Some(&flattened_prop), sub_json),
68            Value::Array(sub_array) => flatten_array(result, &flattened_prop, sub_array),
69            _ => flatten_value(result, &flattened_prop, value.clone()),
70        }?
71    }
72
73    Ok(())
74}
75
76fn flatten_value(result: &mut Map<String, Value>, property: &str, val: Value) -> Result<(), errors::Error> {
77
78    if val.is_object() || val.is_array() {
79        return Err(errors::Error::NotAValue);
80    }
81
82    if let Some(v) = result.get_mut(property) {
83        if let Some(existing_array) = v.as_array_mut() {
84            existing_array.push(val);
85        } else {
86            let v = v.take();
87            result[property] = json!([v, val]);
88        }
89    } else {
90        result.insert(property.to_string(), json!(val));
91    }
92
93    Ok(())
94}
95
96
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102
103    #[test]
104    fn flattening_nested_arrays_and_objects_1() {
105        let json: Value = json!({
106            "x": [
107                ["y", "z"],
108                { "p": "q" },
109                ["r", "s"],
110                [
111                    { "u": "v" },
112                    { "w": "x" },
113                ],
114                ["y", "z"],
115            ]
116        });
117        
118        let flat = flatten(&json).unwrap();
119        let expected = json!({
120            "x[0][0]": "y",
121            "x[0][1]": "z",
122            "x[1].p": "q",
123            "x[2][0]": "r",
124            "x[2][1]": "s",
125            "x[3][0].u": "v",
126            "x[3][1].w": "x",
127            "x[4][0]": "y",
128            "x[4][1]": "z"
129          });
130
131
132        println!(
133            "got:\n{}\nexpected:\n{}\n",
134            serde_json::to_string_pretty(&flat).unwrap(),
135            serde_json::to_string_pretty(&expected).unwrap()
136        );
137
138        assert_eq!(
139            serde_json::to_value(&flat).unwrap(),
140            expected
141        );
142    }
143
144
145    #[test]
146    fn flattening_nested_arrays_and_objects_2() {
147        let json: Value = json!({
148            "x": [
149                "y",
150                ["z", "w"],
151                { "v": ["u", "t"] },
152                [
153                    { "s": "r" },
154                    { "v": ["q", { "y": "x" }] },
155                ],
156                ["a"],
157                "b",
158            ]
159        });
160       
161        let flat = flatten(&json).unwrap();
162        let expected = json!({
163            "x[0]": "y",
164            "x[1][0]": "z",
165            "x[1][1]": "w",
166            "x[2].v[0]": "u",
167            "x[2].v[1]": "t",
168            "x[3][0].s": "r",
169            "x[3][1].v[0]": "q",
170            "x[3][1].v[1].y": "x",
171            "x[4][0]": "a",
172            "x[5]": "b"
173          });
174
175        println!(
176            "got:\n{}\nexpected:\n{}\n",
177            serde_json::to_string_pretty(&flat).unwrap(),
178            serde_json::to_string_pretty(&expected).unwrap()
179        );
180
181
182        assert_eq!(
183            serde_json::to_value(&flat).unwrap(),
184            expected
185        );
186    }
187
188
189    #[test]
190    fn flattening_nested_arrays_and_objects_3() {
191        let json: Value = json!({
192            "a": {
193              "b": "c",
194              "d": [
195                {
196                  "e": "f",
197                  "g": ["h", "i"]
198                },
199                {
200                  "j": "k",
201                  "l": ["m", "n"]
202                }
203              ],
204              "o": {
205                "p": "q",
206                "r": ["s", "t"]
207              }
208            },
209            "u": [
210              {
211                "v": "w",
212                "x": ["y", "z"]
213              },
214              {
215                "aa": "ab",
216                "ac": [
217                  {
218                    "ad": "ae",
219                    "af": ["ag", "ah"]
220                  },
221                  "ai"
222                ]
223              }
224            ],
225            "aj": "ak"
226          });
227       
228        let flat = flatten(&json).unwrap();
229        let expected = json!({
230            "a.b": "c",
231            "a.d[0].e": "f",
232            "a.d[0].g[0]": "h",
233            "a.d[0].g[1]": "i",
234            "a.d[1].j": "k",
235            "a.d[1].l[0]": "m",
236            "a.d[1].l[1]": "n",
237            "a.o.p": "q",
238            "a.o.r[0]": "s",
239            "a.o.r[1]": "t",
240            "u[0].v": "w",
241            "u[0].x[0]": "y",
242            "u[0].x[1]": "z",
243            "u[1].aa": "ab",
244            "u[1].ac[0].ad": "ae",
245            "u[1].ac[0].af[0]": "ag",
246            "u[1].ac[0].af[1]": "ah",
247            "u[1].ac[1]": "ai",
248            "aj": "ak"
249          });
250
251        println!(
252            "got:\n{}\nexpected:\n{}\n",
253            serde_json::to_string_pretty(&flat).unwrap(),
254            serde_json::to_string_pretty(&expected).unwrap()
255        );
256
257
258        assert_eq!(
259            serde_json::to_value(&flat).unwrap(),
260            expected
261        );
262    }
263}