我正在try 对serdeserde_json进行一些有状态的JSON解析.我从How to pass options to Rust's serde that can be accessed in Deserialize::deserialize()?英镑开始 checkout ,虽然我几乎得到了我需要的东西,但我似乎错过了一些至关重要的东西.

我试着做的是两件事:

  1. 我的JSON非常大--太大了,不能直接将输入读入内存--所以我需要流传输.(FWIW,它也有很多嵌套的层,所以我需要使用disable_recursion_limit)
  2. 我需要一些有状态的处理,在那里我可以将一些数据传递给序列化程序,这些数据将影响从输入JSON中保留的数据以及在序列化期间如何转换这些数据.

例如,我的输入可能如下所示:

{ "documents": [
    { "foo": 1 },
    { "baz": true },
    { "bar": null }
    ],
    "journal": { "timestamp": "2023-04-04T08:28:00" }
}

在这里,‘Documents’数组中的每个对象都非常大,我只需要其中的一个子集.不幸的是,我需要首先找到键-值对"documents",然后需要访问该数组中的每个元素.目前,我不关心其他键-值对(例如"journal"),但这种情况可能会改变.

我目前的做法如下:

use serde::de::DeserializeSeed;
use serde_json::Value;

/// A simplified state passed to and returned from the serialization.
#[derive(Debug, Default)]
struct Stats {
    records_skipped: usize,
}

/// Models the input data; `Documents` is just a vector of JSON values,
/// but it is its own type to allow custom deserialization
#[derive(Debug)]
struct MyData {
    documents: Vec<Value>,
    journal: Value,
}

struct MyDataDeserializer<'a> {
    state: &'a mut Stats,
}

/// Top-level seeded deserializer only so I can plumb the state through
impl<'de> DeserializeSeed<'de> for MyDataDeserializer<'_> {
    type Value = MyData;

    fn deserialize<D>(mut self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let visitor = MyDataVisitor(&mut self.state);
        let docs = deserializer.deserialize_map(visitor)?;
        Ok(docs)
    }
}

struct MyDataVisitor<'a>(&'a mut Stats);

impl<'de> serde::de::Visitor<'de> for MyDataVisitor<'_> {
    type Value = MyData;

    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(formatter, "a map")
    }

    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    where
        A: serde::de::MapAccess<'de>,
    {
        let mut documents = Vec::new();
        let mut journal = Value::Null;

        while let Some(key) = map.next_key::<String>()? {
            println!("Got key = {key}");
            match &key[..] {
                "documents" => {
                    // Not sure how to handle the next value in a streaming manner
                    documents = map.next_value()?;
                }

                "journal" => journal = map.next_value()?,
                _ => panic!("Unexpected key '{key}'"),
            }
        }

        Ok(MyData { documents, journal })
    }
}

struct DocumentDeserializer<'a> {
    state: &'a mut Stats,
}

impl<'de> DeserializeSeed<'de> for DocumentDeserializer<'_> {
    type Value = Vec<Value>;

    fn deserialize<D>(mut self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let visitor = DocumentVisitor(&mut self.state);
        let documents = deserializer.deserialize_seq(visitor)?;
        Ok(documents)
    }
}

struct DocumentVisitor<'a>(&'a mut Stats);

impl<'de> serde::de::Visitor<'de> for DocumentVisitor<'_> {
    type Value = Vec<Value>;

    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(formatter, "a list")
    }

    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
    where
        A: serde::de::SeqAccess<'de>,
    {
        let mut agg_map = serde_json::Map::new();

        while let Some(item) = seq.next_element()? {
            // If `item` isn't a JSON object, we'll skip it:
            let Value::Object(map) = item else { continue };

            // Get the first element, assuming we have some
            let (k, v) = match map.into_iter().next() {
                Some(kv) => kv,
                None => continue,
            };

            // Ignore any null values; aggregate everything into a single map
            if v == Value::Null {
                self.0.records_skipped += 1;
                continue;
            } else {
                println!("Keeping {k}={v}");
                agg_map.insert(k, v);
            }
        }
        let values = Value::Object(agg_map);
        println!("Final value is {values}");

        Ok(vec![values])
    }
}

fn main() {
    let fh = std::fs::File::open("input.json").unwrap();
    let buf = std::io::BufReader::new(fh);
    let read = serde_json::de::IoRead::new(buf);

    let mut state = Stats::default();
    let mut deserializer = serde_json::Deserializer::new(read);

    let mydata = MyDataDeserializer { state: &mut state }
        .deserialize(&mut deserializer)
        .unwrap();

    println!("{mydata:?}");
}

这段代码成功运行并正确地反序列化了我的输入数据.问题是,我想不出如何一次一个元素地传输‘Documents’array.我不知道如何把documents = map.next_value()?;分变成一个能让该州降到DocumentDeserializer分的分数.它应该使用类似于maybe的内容:

let d = DocumentDeserializer { state: self.0 }
    .deserialize(&mut map)
    .unwrap();

.deserialize分预计是serde::Deserializer<'de>分,而map分是serde::de::MapAccess<'de>分.

不管怎样,这整件事似乎过于冗长,所以如果这不是普遍接受的或惯用的方法,我愿意接受另一种方法.正如链接问题中的OP所指出的,所有这些样板都令人反感.

推荐答案

你的问题研究得很好,所以我有点怀疑解决方案能这么简单,但你难道不想

"documents" => {
    documents = map.next_value_seed(DocumentDeserializer { self.0 })?;
}

Playground

(就我个人而言,我不会把这些东西命名为…Deserializer.…Seed也许吧?)

Json相关问答推荐

JQ如何获取特定子元素的所有父母

Oracle plsql:如何将json文件加载到嵌套表中

将部分数据字节解组到自定义 struct 中

使用 Powershell,如何将 Azure AD 组成员转换为 Json 对象(文件),然后可以更新?

Powershell解析JSON文件中的键或值

遍历 JSON,检索父值

如何使用 SQL Server 将 json 存储为字符串的列分解/规范化为行和列?

如何在 Apps 脚本中循环遍历 JSON 响应

将 JSON 字符串解析为 Kotlin Android 中的对象列表(MOSHI?)

如何加入或合并列表元素列表(未知长度)

从 PySpark 中的复杂 JSON 文件中高效清除 HTML 实体

是否可以在有条件的情况下将 json 对象转换为 JOLT 中的数组?

Serde JSON 反序列化枚举

判断 JSON 中的对象,而不是条件中提到的对象

在 json 嵌入的 YAML 文件中 - 使用 Python 仅替换 json 值

如何使用 Swiftui 判断 JSON 是否缺少键值对

apple-app-site-association json 文件是否会在应用程序中更新?

有 Json 标签但未导出

将 JsonArray 添加到 JsonObject

Laravel 5 控制器将 JSON 整数作为字符串发送