使用“serde_yaml”反序列化多个文档
Deserializing multiple documents with `serde_yaml`
我正在以追加模式在 YAML 日志文件中保存事件流,其中每个事件都由一个单独的文档表示,如下所示:
---
type: event
id: 1
---
type: trigger
id: 2
稍后我想迭代这些事件,通过 serde_yaml
解析每个事件。不过据我了解,serde_yaml
似乎不支持从单个 reader 解析多个文档,因为 none 的可用方法提到了它,并且尝试一次解析多个文档结果在 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?
我想出了一些看起来可行的解决方案,但我想我会尝试 post 把它放在答案中,因为我不想让其他答案过于偏向我的解决方案.我也鼓励您也看一看,欢迎任何反馈。
我真的希望找到一个仅使用 serde
和 serde_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
一个额外的方法,returns 一个可选的 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 上提供了完整的源代码。
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);
}
}
我正在以追加模式在 YAML 日志文件中保存事件流,其中每个事件都由一个单独的文档表示,如下所示:
---
type: event
id: 1
---
type: trigger
id: 2
稍后我想迭代这些事件,通过 serde_yaml
解析每个事件。不过据我了解,serde_yaml
似乎不支持从单个 reader 解析多个文档,因为 none 的可用方法提到了它,并且尝试一次解析多个文档结果在 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?
我想出了一些看起来可行的解决方案,但我想我会尝试 post 把它放在答案中,因为我不想让其他答案过于偏向我的解决方案.我也鼓励您也看一看,欢迎任何反馈。
我真的希望找到一个仅使用 serde
和 serde_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
一个额外的方法,returns 一个可选的 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 上提供了完整的源代码。
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);
}
}