dtolnay / serde-yaml

Strongly typed YAML library for Rust
Apache License 2.0
960 stars 157 forks source link

Crash with Custom Type Deserializer #373

Closed FishArmy100 closed 1 year ago

FishArmy100 commented 1 year ago

This code crashes with serde_yaml, but not with serde_json. The original example for this code was here

use serde::ser::SerializeMap;
use serde::{Serialize, Serializer, de::Visitor, de::MapAccess, Deserialize, Deserializer};
use std::fmt;

#[derive(Debug)]
struct Custom(String, u32);

impl Serialize for Custom {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut seq = serializer.serialize_map(Some(2))?;
        seq.serialize_entry("first", &self.0)?;
        seq.serialize_entry("second", &self.1)?;
        seq.end()
    }
}

impl<'de> Deserialize<'de> for Custom {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>
    {
        //deserializer.deserialize_any(CustomVisitor)
        deserializer.deserialize_map(CustomVisitor)
    }
}

struct CustomVisitor;

impl<'de> Visitor<'de> for CustomVisitor {
    type Value = Custom;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "a map with keys 'first' and 'second'")
    }

    fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>
    {
        let mut first = None;
        let mut second = None;

        while let Some(k) = map.next_key::<&str>()? {
            if k == "first" {
                first = Some(map.next_value()?);
            }
            else if k == "second" {
                second = Some(map.next_value()?);
            }
            else {
                return Err(serde::de::Error::custom(&format!("Invalid key: {}", k)));
            }
        }

        if first.is_none() || second.is_none() {
            return Err(serde::de::Error::custom("Missing first or second"));
        }

        Ok(Custom(first.unwrap(), second.unwrap()))
    }
}

fn main() {
    let stru = Custom("lala".to_string(), 123);
    println!("Orig {:?}", stru);

    let serialized = serde_yaml::to_string(&stru).expect("err ser");

    println!("Seri {}", serialized);

    let unse : Custom = serde_yaml::from_str(&serialized).expect("err unser");
    println!("New {:?}", unse);
}

Error message:

thread 'main' panicked at 'err unser: Message("invalid type: string \"first\", expected a borrowed string", Some(Pos { marker: Marker { index: 4, line: 2, cker { index: 4, line: 2, col: 0 }, path: "." }))', src\main.rs:75:59
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
dtolnay commented 1 year ago

Seems to work for me. Here is the output I get:

Orig Custom("lala", 123)
Seri first: lala
second: 123

New Custom("lala", 123)
mpalmer commented 1 year ago

In case it helps anyone who comes across this issue, I was having the same error reported in this issue, and it turned out that I was using serde_yaml 0.8 (because that was what was listed in the example I was starting from). Upgrading to serde_yaml 0.9 fixed the problem.