rust 使用“serde_yaml”反序列化多个文档

a11xaf1n  于 2022-11-12  发布在  其他
关注(0)|答案(2)|浏览(160)

我以附加模式在YAML日志文件中保存了一个事件流,其中每个事件都由一个单独的文档表示,如下所示:

---
type: event
id: 1
---
type: trigger
id: 2

稍后,我想迭代这些事件,通过serde_yaml解析每个事件。但据我所知,serde_yaml似乎不支持解析来自单个读取器的多个文档,因为没有任何可用的方法提到它,并且试图一次解析多个文档会导致MoreThanOneDocument错误。

use std::io::{self, BufRead};
use serde_yaml;
use serde::{self, Deserialize};

# [derive(Deserialize, Debug)]

# [serde(tag = "type", rename_all = "snake_case")]

pub enum Message {
    Event { id: i32 },
    Trigger { id: i32}, 
}

fn main() -> io::Result<()> {
    let yaml = "---\ntype: event\nid: 1\n---\n\ntype: trigger\nid: 2";

    let v: Message = serde_yaml::from_reader(yaml.as_bytes()).unwrap();
    println!("{:?}", v);

    Ok(())
}

我对Rust完全是新手,所以也许我完全错过了serde的要点,只是不知道如何做。
请问您如何解析这样的YAML?
我想出了一个看起来像是可行的解决方案,但我想我会试着把它贴在答案中,因为我不想让其他答案对我的解决方案产生太大的偏见。我真诚地鼓励你也看看它,但是,欢迎任何反馈。

a9wyjsp7

a9wyjsp71#

serde_yaml::Deserializer的文档显示了一个与您的示例非常相似的示例。它的工作方式如下:

use serde::Deserialize;

# [derive(Deserialize, Debug)]

# [serde(tag = "type", rename_all = "snake_case")]

pub enum Message {
    Event { id: i32 },
    Trigger { id: i32 },
}

fn main() {
    let yaml = "---\ntype: event\nid: 1\n---\ntype: trigger\nid: 2\n";

    for document in serde_yaml::Deserializer::from_str(yaml) {
        let v = Message::deserialize(document).unwrap();
        println!("{:?}", v);
    }
}
xpcnnkqh

xpcnnkqh2#

我真的希望通过只使用serdeserde_yaml来找到一个本地解决方案,但在此之前,我让它工作的方式如下。

trait BufReaderYamlExt {
    fn read_next_yaml(&mut self) -> io::Result<Option<String>>;
}

impl<T: io::Read> BufReaderYamlExt for io::BufReader<T> {
    fn read_next_yaml(&mut self) -> io::Result<Option<String>> {
        const sep : &str = "\n---\n";
        let mut doc = String::with_capacity(200);
        while self.read_line(&mut doc)? > 0 {
            if doc.len() > sep.len() && doc.ends_with(sep) {
                doc.truncate(doc.len() - sep.len());
                break;
            }
        }
        if !doc.is_empty() {
            doc.shrink_to_fit();
            Ok(Some(doc))
        } else {
            Ok(None)
        }
    }
}

该特征使用一个额外的方法扩展BufReader,该方法返回一个可选的拥有的String(或流末尾的None),其中仅包含具有单个YAML文档的部分。
通过对它进行迭代,可以应用serde_json::from_str()将文档解析为Message结构。

fn main() -> io::Result<()> {
    let yaml = "---\ntype: event\nid: 1\n\n---\n\ntype: trigger\nid: 2\n";
    let mut r = io::BufReader::new(yaml.as_bytes());

    while let Some(next) = r.read_next_yaml()? {
        let d: Message = serde_yaml::from_str(&next).unwrap();
        println!("parsed: {:?}", d);
    }
    Ok(())
}

我还在rust playground上提供了完整的源代码。

相关问题