Search code examples
rustyamldeserializationserde

Deserializing multiple documents with `serde_yaml`


I am saving in append mode a stream of events on a YAML log file, where each event is represented by an indivual document, like this:

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

At some point later I want to iterate on these events, parsing each via serde_yaml. To my understanding though, serde_yaml doesn't seem to support parsing multiple documents from a single reader, as none of the available methods mention it, and trying to parse multiple documents at once results in a MoreThanOneDocument error.

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(())
}

I'm totally new to Rust, so maybe I completely missed the point of serde and just did not understand how to do it.

How would you parse such YAML, please?

I cooked up something that looks like a working solution, but I think I'll try to post it among the answers instead, because I don't want to bias other answers too much towards my solution. I kindly encourage you to have a look at it as well however, any feedback is welcome.


Solution

  • The documentation of serde_yaml::Deserializer shows an example very similar to yours. It would work like this:

    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);
        }
    }