Search code examples
rustserdeserde-json

Capture original payload through serde


I wonder whether there's a way to preserve the original String using serde_json? Consider this example:

#[derive(Debug, Serialize, Deserialize)]
struct User {
    #[serde(skip)]
    pub raw: String,
    pub id: u64,
    pub login: String,
}
{
  "id": 123,
  "login": "johndoe"
}

My structure would end up containing such values:

User {
    raw: String::from(r#"{"id": 123,"login": "johndoe"}"#),
    id: 1,
    login: String::from("johndoe")
}

Currently, I'm doing that by deserializing into Value, then deserializing this value into the User structure and assigning Value to the raw field, but that doesn't seem right, perhaps there's a better way to do so?


Solution

  • This solution uses the RawValue type from serde_json to first get the original input string. Then a new Deserializer is created from that String to deserialize the User type.

    This solution can work with readers, by using Box<serde_json::value::RawValue> as an intermediary type and it can also work with struct which borrow from the input, by using &'de serde_json::value::RawValue as the intermediary. You can test it in the solution by (un-)commenting the borrow field.

    use std::marker::PhantomData;
    
    #[derive(Debug, serde::Serialize, serde::Deserialize)]
    #[serde(remote = "Self")]
    struct User<'a> {
        #[serde(skip)]
        pub raw: String,
        pub id: u64,
        pub login: String,
        // Test for borrowing input data
        // pub borrow: &'a str,
        #[serde(skip)]
        pub ignored: PhantomData<&'a ()>,
    }
    
    impl serde::Serialize for User<'_> {
        fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
            Self::serialize(self, serializer)
        }
    }
    
    impl<'a, 'de> serde::Deserialize<'de> for User<'a>
    where
        'de: 'a,
    {
        fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
            use serde::de::Error;
    
            // Deserializing a `&'a RawValue` would also work here
            // but then you loose support for deserializing from readers
            let raw: Box<serde_json::value::RawValue> = Box::deserialize(deserializer)?;
            // Use this line instead if you have a struct which borrows from the input
            // let raw = <&'de serde_json::value::RawValue>::deserialize(deserializer)?;
    
            let mut raw_value_deserializer = serde_json::Deserializer::from_str(raw.get());
            let mut user =
                User::deserialize(&mut raw_value_deserializer).map_err(|err| D::Error::custom(err))?;
            user.raw = raw.get().to_string();
            Ok(user)
        }
    }
    
    fn main() {
        // Test serialization
        let u = User {
            raw: String::new(),
            id: 456,
            login: "USERNAME".to_string(),
            // Test for borrowing input data
            // borrow: "foobar",
            ignored: PhantomData,
        };
        let json = serde_json::to_string(&u).unwrap();
        println!("{}", json);
    
        // Test deserialization
        let u2: User = serde_json::from_str(&json).unwrap();
        println!("{:#?}", u2);
    }
    

    Test on the Playground.