serde icon indicating copy to clipboard operation
serde copied to clipboard

Serializing fields from a getter method.

Open majidsajadi opened this issue 1 year ago • 4 comments

Is there any way to serialize a struct field using a getter method?

#[derive(Serialize)]
struct MyStruct {
    #[serde(serialize_getter(get_value_with_prefix))]
    value: String,
}

impl MyStruct {
    fn get_value_with_prefix(&self) -> String {
        format!("prefix-{}", self.value)
    }
}

I have found this workaround to write a custom serializer

impl Serialize for MyStruct {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state = serializer.serialize_struct("MyStruct", 1)?;
        state.serialize_field("value", &self.get_value_with_prefix())?;
        state.end()
    }
}

But the problem is that I lost the functionality of other field attributes such as:

#[serde(skip_serializing_if = "Option::is_none")]

Is there any other approach to achieve this but still can use the other field attributes?

majidsajadi avatar Jul 17 '24 13:07 majidsajadi

Have you tried wrapping that field in its own struct and implementing Serialize on that instead, or do you need to use those attributes on the same field?

devnetsec avatar Aug 13 '24 00:08 devnetsec

Attribute #[serde(getter = "...")] already exist but it can be used only with #[serde(remote = "...")] on the type. I think, that this restriction can be eliminated

Mingun avatar Aug 13 '24 04:08 Mingun

As a workaround, you can use #[serde(remote = "Self")]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=21178bb66886241df7c505f95152f6b4

Mingun avatar Aug 13 '24 05:08 Mingun

This feature request is a special case of #271 where the extra field has the same name as one of the existing fields in the data structure.

I think my preference is the same as #271 (and #760), to leave this in the domain of handwritten Serialize impls. Serialize impls are generally pretty approachable compared to Deserialize impls, and I don't feel the need to expose more attribute-based customization of them through the derive macro.

dtolnay avatar Aug 16 '24 00:08 dtolnay

I agree, and our docs already suggest writing a custom impl if the existing attributes do not serve your purpose

oli-obk avatar Sep 03 '24 16:09 oli-obk

As I already noted, I see no rational reasons why this cannot be implemented. The derive code already exists and everything that is needed to make getter works is to remove if. The generic lib, such as serde, should solve problems instead of creating them and I do not understand why you think the opposite.

(Also, closing this issue with the "completed" resolution does not reflect the current status of things).

Mingun avatar Sep 04 '24 04:09 Mingun

It isn't a design goal for derive to be able to express a larger diversity of serde trait impls. Derive is a good fit when the behavior of a trait impl closely corresponds with the structure of a data structure. We expose attributes to tweak the correspondence between behavior and data structure in moderate ways, but when behavior and data structure diverge more, "programming in attributes" starts to be a larger obfuscation than just writing Rust code.

dtolnay avatar Sep 04 '24 05:09 dtolnay

All these considerations would apply if the requested attribute did not exist and some code had to be written to support it. But it already is! Why don't your considerations apply in the case of remote types? There, too, you can just write the impl by hand. So far, this limitation looks like "just because".

Mingun avatar Sep 04 '24 05:09 Mingun