Rmp-serde and deserialize_with problem

Hi there,

I'm trying to figure out why the code below works with serde_json but not with rmp-serde. The test function serde_json works but the test function rmp_serde doesn't. In particular, rmp-serde fails to decode.

The code is a snippet from a larger project where we want to serialize key as a string and later read it back (as string) and then do something to decide on the numeric key value.

use serde::{Deserialize, Deserializer, Serialize, Serializer};

#[derive(Deserialize, Serialize)]
pub struct MyData {
    pub id: usize,
    #[serde(serialize_with = "serialize_key", deserialize_with = "deserialize_key")]
    pub key: usize,
}

fn serialize_key<S>(_: &usize, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    serializer.serialize_str("secret")
}

fn deserialize_key<'de, D>(deserializer: D) -> Result<usize, D::Error>
where
    D: Deserializer<'de>,
{
    let _: &str = Deserialize::deserialize(deserializer)?;
    Ok(0)
}

#[cfg(test)]
mod tests {
    use crate::MyData;
    use serde::{Deserialize, Serialize};

    #[test]
    fn serde_json_should_work() -> Result<(), &'static str> {
        let a = MyData { id: 123, key: 456 };

        let json = serde_json::to_string(&a).map_err(|_| "encode failed")?;
        println!("{}", json);

        let b: MyData = serde_json::from_str(&json).map_err(|_| "decode failed")?;
        assert_eq!(b.id, 123);
        assert_eq!(b.key, 0);

        Ok(())
    }

    #[test]
    fn rmp_serde_should_work() -> Result<(), &'static str> {
        let a = MyData { id: 123, key: 456 };

        let mut bin = Vec::new();
        let mut ser = rmp_serde::Serializer::new(&mut bin);
        a.serialize(&mut ser).map_err(|_| "encode failed")?;
        assert!(bin.len() > 0);

        let mut des = rmp_serde::Deserializer::new(&bin[..]);
        let b: MyData = Deserialize::deserialize(&mut des).map_err(|_| "decode failed")?;
        assert_eq!(b.id, 123);
        assert_eq!(b.key, 0);

        Ok(())
    }
}

This is the Cargo.toml:

[package]
name = "test_rmp_serde"
version = "0.1.0"
edition = "2021"

[dependencies]
rmp-serde = "1.0.0"
serde = { version = "1.0.133", features = ["derive"] }
serde_json = "1.0.78"

Any help is appreciated.
Thanks.

This is a bad pattern in serde, as it forces the value to be borrowed from the input, even if you do not need it. Since the function returns a usize you probably want a transient str for which you must use a Visitor. Deserializing into a String will work too, but require an allocation.

The problem is that rmp_serde::Deserializer::new expects a Read, thus it cannot allow borrowing and deserializing a &str fails. It seems what you want is rmp_serde::Deserializer::from_read_ref.
You can get the JSON example to fail too, if you use a reader there. Just run it with serde_json::from_reader(json.as_bytes()).

Thank you for your input!

Later, I'll need the result from deserializing. For instance, I'll have to compute the key using something like this:

    let res: &str = Deserialize::deserialize(deserializer)?;
    let key = match res {
        "secret" => 0,
        "init" => 123,
        _ => 456,
    };
    Ok(key)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.