base_d/encoders/algorithms/schema/serializers/
json.rs

1use crate::encoders::algorithms::schema::fiche::NEST_SEP;
2use crate::encoders::algorithms::schema::serializers::OutputSerializer;
3use crate::encoders::algorithms::schema::types::*;
4use serde_json::{Map, Value, json};
5use std::collections::HashMap;
6
7pub struct JsonSerializer;
8
9impl OutputSerializer for JsonSerializer {
10    type Error = SchemaError;
11
12    fn serialize(ir: &IntermediateRepresentation, pretty: bool) -> Result<String, Self::Error> {
13        if ir.header.row_count == 0 {
14            return Err(SchemaError::InvalidInput(
15                "No rows to serialize".to_string(),
16            ));
17        }
18
19        // Build rows
20        let mut rows = Vec::new();
21        for row_idx in 0..ir.header.row_count {
22            let mut row_map = HashMap::new();
23
24            for (field_idx, field) in ir.header.fields.iter().enumerate() {
25                let value = ir
26                    .get_value(row_idx, field_idx)
27                    .ok_or_else(|| SchemaError::InvalidInput("Missing value".to_string()))?;
28
29                let json_value = if ir.is_null(row_idx, field_idx) {
30                    Value::Null
31                } else {
32                    schema_value_to_json(value)?
33                };
34
35                row_map.insert(field.name.clone(), json_value);
36            }
37
38            rows.push(row_map);
39        }
40
41        // Unflatten each row
42        let mut unflattened_rows = Vec::new();
43        for row_map in rows {
44            let unflattened = unflatten_object(row_map);
45            unflattened_rows.push(unflattened);
46        }
47
48        // Determine output format
49        let result = if ir.header.row_count == 1 && ir.header.metadata.is_none() {
50            // Single row without metadata - output as object
51            unflattened_rows.into_iter().next().unwrap()
52        } else {
53            // Multiple rows OR single row with metadata - output as array
54            Value::Array(unflattened_rows)
55        };
56
57        // Apply root key and metadata if present
58        let final_result = if let Some(root_key) = &ir.header.root_key {
59            let mut obj = Map::new();
60
61            // Add metadata fields first (if present)
62            if let Some(ref metadata) = ir.header.metadata {
63                for (key, value) in metadata {
64                    // Convert ∅ symbol back to JSON null
65                    let json_value = if value == "∅" {
66                        Value::Null
67                    } else {
68                        // Try to parse as number, bool, or keep as string
69                        if let Ok(num) = value.parse::<i64>() {
70                            json!(num)
71                        } else if let Ok(num) = value.parse::<f64>() {
72                            json!(num)
73                        } else if value == "true" {
74                            json!(true)
75                        } else if value == "false" {
76                            json!(false)
77                        } else {
78                            json!(value)
79                        }
80                    };
81                    obj.insert(key.clone(), json_value);
82                }
83            }
84
85            // Add array data under root key
86            obj.insert(root_key.clone(), result);
87            Value::Object(obj)
88        } else {
89            result
90        };
91
92        // Serialize to JSON string
93        if pretty {
94            serde_json::to_string_pretty(&final_result)
95                .map_err(|e| SchemaError::InvalidInput(format!("JSON serialization failed: {}", e)))
96        } else {
97            serde_json::to_string(&final_result)
98                .map_err(|e| SchemaError::InvalidInput(format!("JSON serialization failed: {}", e)))
99        }
100    }
101}
102
103/// Convert SchemaValue to JSON Value
104fn schema_value_to_json(value: &SchemaValue) -> Result<Value, SchemaError> {
105    match value {
106        SchemaValue::U64(n) => Ok(json!(*n)),
107        SchemaValue::I64(n) => Ok(json!(*n)),
108        SchemaValue::F64(n) => Ok(json!(*n)),
109        SchemaValue::String(s) => Ok(json!(s)),
110        SchemaValue::Bool(b) => Ok(json!(*b)),
111        SchemaValue::Null => Ok(Value::Null),
112        SchemaValue::Array(arr) => {
113            let mut json_arr = Vec::new();
114            for item in arr {
115                json_arr.push(schema_value_to_json(item)?);
116            }
117            Ok(Value::Array(json_arr))
118        }
119    }
120}
121
122/// Unflatten nested keys back to nested objects
123fn unflatten_object(flat: HashMap<String, Value>) -> Value {
124    let mut result = Map::new();
125
126    for (key, value) in flat {
127        let parts: Vec<&str> = key.split(NEST_SEP).collect();
128        insert_nested(&mut result, &parts, value);
129    }
130
131    Value::Object(result)
132}
133
134/// Insert a value into nested structure
135fn insert_nested(obj: &mut Map<String, Value>, parts: &[&str], value: Value) {
136    if parts.is_empty() {
137        return;
138    }
139
140    if parts.len() == 1 {
141        obj.insert(parts[0].to_string(), value);
142        return;
143    }
144
145    let key = parts[0];
146    let remaining = &parts[1..];
147
148    let nested = obj
149        .entry(key.to_string())
150        .or_insert_with(|| Value::Object(Map::new()));
151
152    if let Value::Object(nested_obj) = nested {
153        insert_nested(nested_obj, remaining, value);
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160
161    #[test]
162    fn test_simple_object() {
163        let fields = vec![
164            FieldDef::new("id", FieldType::U64),
165            FieldDef::new("name", FieldType::String),
166        ];
167        let header = SchemaHeader::new(1, fields);
168        let values = vec![
169            SchemaValue::U64(1),
170            SchemaValue::String("alice".to_string()),
171        ];
172        let ir = IntermediateRepresentation::new(header, values).unwrap();
173
174        let output = JsonSerializer::serialize(&ir, false).unwrap();
175        let parsed: Value = serde_json::from_str(&output).unwrap();
176
177        assert_eq!(parsed["id"], json!(1));
178        assert_eq!(parsed["name"], json!("alice"));
179    }
180
181    #[test]
182    fn test_array_of_objects() {
183        let fields = vec![FieldDef::new("id", FieldType::U64)];
184        let header = SchemaHeader::new(2, fields);
185        let values = vec![SchemaValue::U64(1), SchemaValue::U64(2)];
186        let ir = IntermediateRepresentation::new(header, values).unwrap();
187
188        let output = JsonSerializer::serialize(&ir, false).unwrap();
189        let parsed: Value = serde_json::from_str(&output).unwrap();
190
191        assert!(parsed.is_array());
192        assert_eq!(parsed[0]["id"], json!(1));
193        assert_eq!(parsed[1]["id"], json!(2));
194    }
195
196    #[test]
197    fn test_nested_object() {
198        let fields = vec![FieldDef::new("user჻profile჻name", FieldType::String)];
199        let header = SchemaHeader::new(1, fields);
200        let values = vec![SchemaValue::String("alice".to_string())];
201        let ir = IntermediateRepresentation::new(header, values).unwrap();
202
203        let output = JsonSerializer::serialize(&ir, false).unwrap();
204        let parsed: Value = serde_json::from_str(&output).unwrap();
205
206        assert_eq!(parsed["user"]["profile"]["name"], json!("alice"));
207    }
208
209    #[test]
210    fn test_root_key() {
211        let mut header = SchemaHeader::new(1, vec![FieldDef::new("id", FieldType::U64)]);
212        header.root_key = Some("users".to_string());
213        header.set_flag(FLAG_HAS_ROOT_KEY);
214
215        let values = vec![SchemaValue::U64(1)];
216        let ir = IntermediateRepresentation::new(header, values).unwrap();
217
218        let output = JsonSerializer::serialize(&ir, false).unwrap();
219        let parsed: Value = serde_json::from_str(&output).unwrap();
220
221        assert!(parsed["users"].is_object());
222        assert_eq!(parsed["users"]["id"], json!(1));
223    }
224
225    #[test]
226    fn test_null_handling() {
227        let mut header = SchemaHeader::new(
228            1,
229            vec![
230                FieldDef::new("name", FieldType::String),
231                FieldDef::new("age", FieldType::U64),
232            ],
233        );
234
235        // Mark age as null
236        let mut null_bitmap = vec![0u8; 1];
237        null_bitmap[0] |= 1 << 1; // Set bit 1
238        header.null_bitmap = Some(null_bitmap);
239        header.set_flag(FLAG_HAS_NULLS);
240
241        let values = vec![SchemaValue::String("alice".to_string()), SchemaValue::Null];
242        let ir = IntermediateRepresentation::new(header, values).unwrap();
243
244        let output = JsonSerializer::serialize(&ir, false).unwrap();
245        let parsed: Value = serde_json::from_str(&output).unwrap();
246
247        assert_eq!(parsed["name"], json!("alice"));
248        assert_eq!(parsed["age"], Value::Null);
249    }
250
251    #[test]
252    fn test_homogeneous_array() {
253        let fields = vec![FieldDef::new(
254            "scores",
255            FieldType::Array(Box::new(FieldType::U64)),
256        )];
257        let header = SchemaHeader::new(1, fields);
258        let values = vec![SchemaValue::Array(vec![
259            SchemaValue::U64(1),
260            SchemaValue::U64(2),
261            SchemaValue::U64(3),
262        ])];
263        let ir = IntermediateRepresentation::new(header, values).unwrap();
264
265        let output = JsonSerializer::serialize(&ir, false).unwrap();
266        let parsed: Value = serde_json::from_str(&output).unwrap();
267
268        assert_eq!(parsed["scores"], json!([1, 2, 3]));
269    }
270
271    #[test]
272    fn test_empty_array() {
273        let fields = vec![FieldDef::new(
274            "items",
275            FieldType::Array(Box::new(FieldType::Null)),
276        )];
277        let header = SchemaHeader::new(1, fields);
278        let values = vec![SchemaValue::Array(vec![])];
279        let ir = IntermediateRepresentation::new(header, values).unwrap();
280
281        let output = JsonSerializer::serialize(&ir, false).unwrap();
282        let parsed: Value = serde_json::from_str(&output).unwrap();
283
284        assert_eq!(parsed["items"], json!([]));
285    }
286
287    #[test]
288    fn test_deep_nesting() {
289        let fields = vec![FieldDef::new("a჻b჻c჻d", FieldType::U64)];
290        let header = SchemaHeader::new(1, fields);
291        let values = vec![SchemaValue::U64(1)];
292        let ir = IntermediateRepresentation::new(header, values).unwrap();
293
294        let output = JsonSerializer::serialize(&ir, false).unwrap();
295        let parsed: Value = serde_json::from_str(&output).unwrap();
296
297        assert_eq!(parsed["a"]["b"]["c"]["d"], json!(1));
298    }
299
300    #[test]
301    fn test_unflatten_object() {
302        let mut flat = HashMap::new();
303        flat.insert("a჻b".to_string(), json!(1));
304
305        let unflattened = unflatten_object(flat);
306
307        assert_eq!(unflattened["a"]["b"], json!(1));
308    }
309
310    #[test]
311    fn test_pretty_output() {
312        let fields = vec![
313            FieldDef::new("id", FieldType::U64),
314            FieldDef::new("name", FieldType::String),
315        ];
316        let header = SchemaHeader::new(1, fields);
317        let values = vec![
318            SchemaValue::U64(1),
319            SchemaValue::String("alice".to_string()),
320        ];
321        let ir = IntermediateRepresentation::new(header, values).unwrap();
322
323        // Test compact output
324        let compact = JsonSerializer::serialize(&ir, false).unwrap();
325        assert!(!compact.contains('\n'));
326        assert_eq!(compact, r#"{"id":1,"name":"alice"}"#);
327
328        // Test pretty output
329        let pretty = JsonSerializer::serialize(&ir, true).unwrap();
330        assert!(pretty.contains('\n'));
331        assert!(pretty.contains("  ")); // Indentation
332
333        // Both should parse to same JSON value
334        let compact_value: Value = serde_json::from_str(&compact).unwrap();
335        let pretty_value: Value = serde_json::from_str(&pretty).unwrap();
336        assert_eq!(compact_value, pretty_value);
337    }
338
339    #[test]
340    fn test_metadata_with_null() {
341        use std::collections::HashMap;
342
343        let fields = vec![FieldDef::new("id", FieldType::U64)];
344        let mut header = SchemaHeader::new(2, fields);
345        header.root_key = Some("users".to_string());
346        header.set_flag(FLAG_HAS_ROOT_KEY);
347
348        let mut metadata = HashMap::new();
349        metadata.insert("note".to_string(), "∅".to_string());
350        metadata.insert("total".to_string(), "2".to_string());
351        header.metadata = Some(metadata);
352
353        let values = vec![SchemaValue::U64(1), SchemaValue::U64(2)];
354        let ir = IntermediateRepresentation::new(header, values).unwrap();
355
356        let output = JsonSerializer::serialize(&ir, false).unwrap();
357        let parsed: Value = serde_json::from_str(&output).unwrap();
358
359        // Check metadata was reconstructed
360        assert_eq!(parsed["note"], Value::Null);
361        assert_eq!(parsed["total"], json!(2));
362
363        // Check array data
364        assert!(parsed["users"].is_array());
365        assert_eq!(parsed["users"][0]["id"], json!(1));
366        assert_eq!(parsed["users"][1]["id"], json!(2));
367    }
368}