Rick Rainey
Rick Rainey

Reputation: 11246

Serialize a remote struct with private String

I need to serialize a struct from a remote crate and all of the fields in the struct are private. There are getter's implemented in the remote struct to get those values. I am following this guidance and got it to work just fine for primitive types. However, I'm struggling with how to implement this for non-primitive types (ie: String) that the remote struct contains.

Below is a small piece of what I've implemented to frame the issue. The DataWrapper struct simply wraps Data, where Data is the remote struct.

#[derive(Serialize)]
pub struct DataWrapper {
    #[serde(with = "DataDef")]
    pub data: Data,
}

#[derive(Serialize)]
#[serde(remote = "remote_crate::data::Data")]
pub struct DataDef {
    #[serde(getter = "Data::image_id")]     // This works
    image_id: u16,

    #[serde(getter = "Data::description")]  // This does not work
    description: String,
}

The error I get when compiling this is

#[derive(Serialize)]
         ^^^^^^^^^ expected struct `std::string::String`, found `&str`

This makes sense, since the getter Data::description returns &str rather than a String. But, I'm not seeing a way in my code to coerce this so the compiler is happy.

If I change DataDef::description to be &str instead of String, then I have to implement lifetimes. But, when I do that, the compiler then says the remote "struct takes 0 lifetime arguments".

Appreciate any tips on how I can serialize this and other non-primitive types.

Upvotes: 3

Views: 1032

Answers (2)

vgel
vgel

Reputation: 3335

I couldn't get it to work with an &str, but if you're OK with an allocation, you can write a custom getter:

mod local {
    use super::remote::RemoteData;
    use serde::{Deserialize, Serialize};

    fn get_owned_description(rd: &RemoteData) -> String {
        rd.description().into()
    }

    #[derive(Serialize)]
    #[serde(remote = "RemoteData")]
    pub struct DataDef {
        #[serde(getter = "get_owned_description")]
        description: String,
    }
}

mod remote {
    use serde::{Deserialize, Serialize};

    #[derive(Serialize, Deserialize)]
    pub struct RemoteData {
        description: String,
    }

    impl RemoteData {
        pub fn description(&self) -> &str {
            &self.description
        }
    }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e9c7c0b069d7e16b6faac2fa2b840c72

Upvotes: 0

SeedyROM
SeedyROM

Reputation: 2441

One approach you could do, so that you have full control of the serialization. Is to have the data wrapper be a copy of the struct fields you need, instead of the entire remote struct. Then you can implement From<remote_crate::data::Data> for DataWrapper and use serde without trying to coerce types.

#[derive(Serialize)]
pub struct Data {
    image_id: u16,
    description: String,
}

impl From<remote_crate::data::Data> for Data {
    fn from(val: remote_crate::data::Data) -> Self {
        Self {
            image_id: val.image_id,
            description: val.description.to_string(),
        }
    }
}


// Then you could use it like this:
// let my_data: Data = data.into();

Upvotes: 0

Related Questions