Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Context-Dependent Serialization via Custom Field Attributes #2900

Copy link
Copy link
Open
@cernec1999

Description

@cernec1999
Issue body actions

Problem

Hello! I'd like to address an issue that has been raised numerous times in this repository. Namely, I'd like to propose context-dependent serialization based on certain attributes on fields in a struct.

Consider this simple example:

/// RcLogin packet.
#[derive(Debug, Serialize)]
pub struct RcLogin {
    /// Encryption key.
    pub encryption_key: u8,
    /// Version.
    pub version: String,
    /// Account.
    // TODO: This needs to be handled slightly differently by our custom serializer
    pub account: String,
    /// Password.
    // TODO: This needs to be handled slightly differently by our custom serializer
    pub password: String,
    /// PC IDs
    pub identification: Vec<String>,
}

The two fields above, account and password, are Strings encoded in a special format in the format I am writing the serializer for. More specifically, the custom serializer should encoded the length information of the string, but only for these two types. Everything else is "normal" serialization (i.e. just encoding the string with no additional information).

Currently, the way you can do this is by implementing serialize_with, deserialize_with, and with. The issue with this approach with respect to custom serializers is that as far as I'm aware, it's impossible for these functions to access the internal state of the custom serializer / deserializer. See: https://github.com/Preagonal/preagonal-client-rs/blob/253164d83aa52641d2dcaf066c0c7a1f650ab2b1/src/net/serialization/serialize.rs#L13-L15

The way I solved the above issue, and still getting the internal state of the serializer, is actually using a newtype (called GString) with some unsafe code, which is definitely not my preferred solution:

    fn serialize_newtype_struct<T>(
        self,
        name: &'static str,
        value: &T,
    ) -> Result<Self::Ok, Self::Error>
    where
        T: ?Sized + Serialize,
    {
        match name {
            "GString" => {
                // TODO(@ropguy): The following unsafe cast is used to extract a GString.
                let gstring: &GString = unsafe { &*(value as *const T as *const GString) };
                self.writer.write_gstring(&gstring.0)?;
                Ok(())
            }
            _ => todo!(),
        }
    }

And you might be wondering: why not impl Serialize for GString? As mentioned, I'm looking for a way to customize the serialization logic for only my serializer. If there was another serializer, like JSON, that wanted to serialize my type, it shouldn't be locked in to the GString serialization.

Previous Issues

There are a few other issues that have been raised in this repository with this as a feature request. Please see this comment on this closed issue: #2309 (comment)

In addition, this is a current open issue in Serde that has a similar request: #2877

Supporting XML-like documents is not a goal for serde. You would be better off using an XML-specific derive macro.

I understand not wanting to support XML-like documents, but something that is a global serialization / deserialization strategy should not be so opinionated when it comes to a quality-of-life feature such as custom attributes, especially when it doesn't just relate to XML documents.

Proposal

Like #2877, I'd like to propose a broader solution. Instead of adding namespace support, serde should allow custom attributes to be specified like so:

/// RcLogin packet.
#[derive(Debug, Serialize)]
pub struct RcLogin {
    /// Encryption key.
    pub encryption_key: u8,
    /// Version.
    pub version: String,
    /// Account.
    #[serde(attr = "gstring")]
    pub account: String,
    /// Password.
    #[serde(attr = "gstring")]
    pub password: String,
    /// PC IDs
    pub identification: Vec<String>,
}

Then, the custom serializer / deserializer will somehow be able to access the attr and perform more context-dependent serialization. In my case, I would forego

Other Libraries

I think the way Kotlin does things with its @SerialInfo annotation is a clean way to solve this: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-info/

Final Thoughts

Assuming the above aligns with the maintainer's design philosophy, I'm happy to file a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.