rust - 如何使用 serde_json 从 JSON 数组内部流式传输元素?

标签 rust serde-json

我有一个 5GB 的 JSON 文件,它是一个具有固定结构的对象数组:

[
  {
    "first": "John",
    "last": "Doe",
    "email": "john.doe@yahoo.com"
  },
  {
    "first": "Anne",
    "last": "Ortha",
    "email": "anne.ortha@hotmail.com"
  },
  ....
]
我知道我可以尝试使用 How can I deserialize JSON with a top-level array using Serde? 中显示的代码来解析这个文件。 :
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct User {
    first: String,
    last: String,
    email: String,
}

let users: Vec<User> = serde_json::from_str(file)?;
有多个问题:
  • 它首先作为一个字符串整体读取
  • 读取为字符串后,将其转换为 User 的向量结构(我不想要)

  • 我试过 How I can I lazily read multiple JSON values from a file/stream in Rust?但它在打印任何内容之前读取整个文件,并在循环内立即打印整个结构。我期待在循环中一次一个对象:
    enter image description here
    理想情况下,(已解析的)用户对象的解析和处理应该在两个单独的线程/任务/例程中同时发生,或者通过使用 channel 进行。

    最佳答案

    从 JSON 数组流式传输元素是可能的,但需要一些工作。您必须跳过前导 [和间歇性 ,自己,以及检测最终] .要解析单个数组元素,您需要使用 StreamDeserializer 并从中提取单个项目(这样您就可以放下它并重新获得对 IO 读取器的控制)。例如:

    use serde::de::DeserializeOwned;
    use serde_json::{self, Deserializer};
    use std::io::{self, Read};
    
    fn read_skipping_ws(mut reader: impl Read) -> io::Result<u8> {
        loop {
            let mut byte = 0u8;
            reader.read_exact(std::slice::from_mut(&mut byte))?;
            if !byte.is_ascii_whitespace() {
                return Ok(byte);
            }
        }
    }
    
    fn invalid_data(msg: &str) -> io::Error {
        io::Error::new(io::ErrorKind::InvalidData, msg)
    }
    
    fn deserialize_single<T: DeserializeOwned, R: Read>(reader: R) -> io::Result<T> {
        let next_obj = Deserializer::from_reader(reader).into_iter::<T>().next();
        match next_obj {
            Some(result) => result.map_err(Into::into),
            None => Err(invalid_data("premature EOF")),
        }
    }
    
    fn yield_next_obj<T: DeserializeOwned, R: Read>(
        mut reader: R,
        at_start: &mut bool,
    ) -> io::Result<Option<T>> {
        if !*at_start {
            *at_start = true;
            if read_skipping_ws(&mut reader)? == b'[' {
                // read the next char to see if the array is empty
                let peek = read_skipping_ws(&mut reader)?;
                if peek == b']' {
                    Ok(None)
                } else {
                    deserialize_single(io::Cursor::new([peek]).chain(reader)).map(Some)
                }
            } else {
                Err(invalid_data("`[` not found"))
            }
        } else {
            match read_skipping_ws(&mut reader)? {
                b',' => deserialize_single(reader).map(Some),
                b']' => Ok(None),
                _ => Err(invalid_data("`,` or `]` not found")),
            }
        }
    }
    
    pub fn iter_json_array<T: DeserializeOwned, R: Read>(
        mut reader: R,
    ) -> impl Iterator<Item = Result<T, io::Error>> {
        let mut at_start = false;
        std::iter::from_fn(move || yield_next_obj(&mut reader, &mut at_start).transpose())
    }
    
    用法示例:
    fn main() {
        let data = r#"[
      {
        "first": "John",
        "last": "Doe",
        "email": "john.doe@yahoo.com"
      },
      {
        "first": "Anne",
        "last": "Ortha",
        "email": "anne.ortha@hotmail.com"
      }
    ]"#;
        use serde::{Deserialize, Serialize};
    
        #[derive(Serialize, Deserialize, Debug)]
        struct User {
            first: String,
            last: String,
            email: String,
        }
    
        for user in iter_json_array(io::Cursor::new(&data)) {
            let user: User = user.unwrap();
            println!("{:?}", user);
        }
    }
    
    Playground
    在生产中使用它时,您可以将其打开为 File而不是将其读取为字符串。一如既往,不要忘记包装 FileBufReader .

    关于rust - 如何使用 serde_json 从 JSON 数组内部流式传输元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68641157/

    相关文章:

    rust - Rust试图维护自定义列表: cannot borrow `self.array` as mutable more than once at a time

    rust - 无法创建目录时出现 panic

    rust - 序列化 null 值或默认值时如何避免生成 JSON?

    rust - 函数返回 serde 反序列化类型时如何修复生命周期错误?

    rust - 函数返回 serde 反序列化类型时如何修复生命周期错误?

    json - 用rust反序列化具有多个可能值的JSON

    random - 特征 `rand_core::CryptoRng` 未针对 `OsRng` 实现

    rust - 在FnOnce中使用struct代替函数

    rust - HashMap 键的生命周期不够长

    rust - 使用 serde::from_value 反序列化为泛型类型