在 Rust 中解析任意大的 JSON 数组

Mic*_*mer 1 parsing json rust serde

假设我们有一个 JSON 文档,其中属性之一是任意大的对象数组:

\n
{\n    "type": "FeatureCollection",\n    "features":[\n      {"type": "feature", ...},\n      {"type": "feature", ...},\n      ... many, many more objects ...\n    ]\n}\n
Run Code Online (Sandbox Code Playgroud)\n

该文档甚至可能通过网络发送,因此数组内的对象数量可能事先未知。

\n

该文档可能有几 GB 大。

\n

如何在 Rust 中解析这样的文档(最好使用 Serde)而不先将其加载到内存中?我只对数组中的对象感兴趣。\xe2\x80\x98parent\xe2\x80\x99 对象(如果愿意)可以被忽略。

\n

Cae*_*sar 5

如果您的features数组相当接近 JSON 结构的“顶部”(即仅向下一层),则可以使用 serde 合理地做到这一点。

可悲的是,通常的#[derive(Deserialize)]机制通常不能在 JSON 结构的外部级别上使用,因为您通常需要某种状态来处理功能流,但派生的反序列化器是无状态的。所以你必须实现两个DeserializeSeed.

#[derive(Deserialize)]第一个替换外部结构上的the ,但调用next_value_seed而不是next_valueon features。这都是样板文件,我仍在等待有人将其添加到 serde 的派生宏中:

struct FeatureCollectionStream<F>(F);
impl<'de, F: FnMut(Feature)> DeserializeSeed<'de> for FeatureCollectionStream<F> {
    type Value = ();

    fn deserialize<D>(self, d: D) -> Result<Self::Value, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        return d.deserialize_struct("FeatureCollection", &["type", "features"], FCV(self.0));
        struct FCV<F>(F);
        impl<'de, F: FnMut(Feature)> Visitor<'de> for FCV<F> {
            type Value = ();

            fn visit_map<A: MapAccess<'de>>(mut self, mut map: A) -> Result<Self::Value, A::Error> {
                while let Some(k) = map.next_key::<String>()? {
                    match k.as_str() {
                        "type" => {
                            map.next_value::<String>()?;
                        }
                        "features" => map.next_value_seed(FeatureStream(&mut self.0))?,
                        s => return Err(todo!()),
                    }
                }
                Ok(())
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

下一个序列化器是您真正想要的序列化器,它将序列处理Feature为流:

struct FeatureStream<F>(F);
impl<'de, F: FnMut(Feature)> DeserializeSeed<'de> for FeatureStream<F> {
    type Value = ();

    fn deserialize<D>(self, d: D) -> Result<Self::Value, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        return d.deserialize_seq(FV(self.0));
        struct FV<F>(F);
        impl<'de, F: FnMut(Feature)> Visitor<'de> for FV<F> {
            type Value = ();

            fn visit_seq<A: serde::de::SeqAccess<'de>>(
                mut self,
                mut seq: A,
            ) -> Result<Self::Value, A::Error> {
                while let Some(f) = seq.next_element()? {
                    (self.0)(f)
                }
                Ok(())
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

可爱的部分是你仍然可以对内部结构使用导出魔法。

#[derive(Deserialize, Default)]
#[serde(rename_all = "snake_case")]
enum FeatureType {
    #[default]
    Feature,
}

#[derive(Deserialize, Default)]
struct Feature {
    #[allow(unused)]
    r#type: FeatureType,
    // ...   
}
Run Code Online (Sandbox Code Playgroud)

使用这种混合物:

FeatureCollectionStream(|f: Feature| todo!("Do something with each feature"))
    .deserialize(&mut serde_json::Deserializer::from_reader(x))?;
Run Code Online (Sandbox Code Playgroud)

带有遗漏错误处理的Playground

(参见使用相同“技巧”的另一个答案)