clickhouse_format/output/
json_compact.rs

1use core::marker::PhantomData;
2use std::collections::HashMap;
3
4use serde::de::DeserializeOwned;
5use serde_json::{Map, Value};
6
7use crate::format_name::FormatName;
8
9use super::{
10    json::{JsonData, JsonDataInfo},
11    Output, OutputResult,
12};
13
14pub struct JsonCompactOutput<T> {
15    phantom: PhantomData<T>,
16}
17impl<T> Default for JsonCompactOutput<T> {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22impl<T> JsonCompactOutput<T> {
23    pub fn new() -> Self {
24        Self {
25            phantom: PhantomData,
26        }
27    }
28}
29pub type GeneralJsonCompactOutput = JsonCompactOutput<HashMap<String, Value>>;
30
31impl<T> Output for JsonCompactOutput<T>
32where
33    T: DeserializeOwned,
34{
35    type Row = T;
36    type Info = JsonDataInfo;
37
38    type Error = serde_json::Error;
39
40    fn format_name() -> FormatName {
41        FormatName::JsonCompact
42    }
43
44    fn deserialize(&self, slice: &[u8]) -> OutputResult<Self::Row, Self::Info, Self::Error> {
45        self.deserialize_with::<Value>(slice)
46    }
47}
48
49impl<T> JsonCompactOutput<T>
50where
51    T: DeserializeOwned,
52{
53    pub(crate) fn deserialize_with<V>(
54        &self,
55        slice: &[u8],
56    ) -> OutputResult<<Self as Output>::Row, <Self as Output>::Info, <Self as Output>::Error>
57    where
58        V: DeserializeOwned + Into<Value>,
59    {
60        let json_data_tmp: JsonData<Vec<V>> = serde_json::from_slice(slice)?;
61
62        let keys: Vec<_> = json_data_tmp
63            .meta
64            .iter()
65            .map(|x| x.name.to_owned())
66            .collect();
67        let mut data: Vec<T> = vec![];
68        for values in json_data_tmp.data.into_iter() {
69            let map: Map<_, _> = keys
70                .iter()
71                .zip(values)
72                .map(|(k, v)| (k.to_owned(), v.into()))
73                .collect();
74            data.push(serde_json::from_value(Value::Object(map))?);
75        }
76
77        Ok((
78            data,
79            JsonDataInfo {
80                meta: json_data_tmp.meta,
81                rows: json_data_tmp.rows,
82                statistics: json_data_tmp.statistics,
83            },
84        ))
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    use std::{fs, path::PathBuf};
93
94    use crate::test_helpers::{TestRow, TEST_ROW_1};
95
96    #[test]
97    fn simple() -> Result<(), Box<dyn std::error::Error>> {
98        let file_path = PathBuf::new().join("tests/files/JSONCompact.json");
99        let content = fs::read_to_string(&file_path)?;
100
101        assert_eq!(
102            GeneralJsonCompactOutput::format_name(),
103            file_path
104                .file_stem()
105                .unwrap()
106                .to_string_lossy()
107                .parse()
108                .unwrap()
109        );
110
111        let (rows, info) = GeneralJsonCompactOutput::new().deserialize(content.as_bytes())?;
112        assert_eq!(
113            rows.first().unwrap().get("tuple1").unwrap(),
114            &Value::Array(vec![1.into(), "a".into()])
115        );
116        assert_eq!(info.rows, 2);
117
118        let (rows, info) = JsonCompactOutput::<TestRow>::new().deserialize(content.as_bytes())?;
119        assert_eq!(rows.first().unwrap(), &*TEST_ROW_1);
120        assert_eq!(info.rows, 2);
121
122        Ok(())
123    }
124}