Skip to content
This repository has been archived by the owner on Jul 9, 2023. It is now read-only.

Derive macros to serialize and deserialize struct as an array of values #3

Closed
dtolnay opened this issue Jan 20, 2019 · 6 comments
Closed

Comments

@dtolnay
Copy link
Owner

dtolnay commented Jan 20, 2019

By default, Serde will serialize a struct to JSON as a JSON map like {"s":"","i":0,"b":true}. In some cases it can be preferable to serialize as a JSON array like ["",0,true]. Serde serializes tuple structs like this, but then we lose the field names when working with the type in Rust code.

I would like there to be derive macros Serialize_tuple and Deserialize_tuple that let me write an ordinary braced struct with named fields but serialize it as if it were a tuple struct.

#[derive(Serialize_tuple, Deserialize_tuple)]
struct T {
    s: String,
    i: i32,
    b: bool,
}

See serde_repr as an existing example of an independent special-purpose derive macro for Serde traits.

// generated code

impl serde::Serialize for T {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        #[derive(serde::Serialize)]
        #[serde(rename = "T")]
        struct Helper<'a>(&'a String, &'a i32, &'a bool);

        let helper = Helper(&self.s, &self.i, &self.b);
        serde::Serialize::serialize(&helper, serializer)
    }
}

impl<'de> serde::Deserialize<'de> for T {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        #[derive(serde::Deserialize)]
        #[serde(rename = "T")]
        struct Helper(String, i32, bool);

        let helper: Helper = serde::Deserialize::deserialize(deserializer)?;
        Ok(T {
            s: helper.0,
            i: helper.1,
            b: helper.2,
        })
    }
}
@kardeiz
Copy link

kardeiz commented Jan 29, 2019

I found this issue from serde-rs/serde#637 while working on a JSONRPC related mechanism for deserializing a struct by name or position.

I've made a first attempt at this: https://github.com/kardeiz/serde_tuple, though it is still a little rough.

My implementation also lets you specify output position (e.g., if you want to change the order of values in the output array). It also offers a DeserializeMaybeTuple to deserialize from either the tuple or named style.

It is currently limited to no generics. I think this could be pretty easily lifted to just "no reference fields". It might be possible to be open to any serde-capable type, but I'm not sure if that is really feasible.

Question: is there a reason to prefer Serialize_tuple over SerializeTuple? I've chosen the latter as I think it looks nicer, but I'm open to changing it.

@dtolnay
Copy link
Owner Author

dtolnay commented Jan 30, 2019

Thanks for taking a look at this. Some notes on your implementation:

  • My implementation also lets you specify output position (e.g., if you want to change the order of values in the output array).

    What is the use case for this that is not solved by reordering the struct fields, such as putting field baz before bar in your simple.rs example code?

  • It also offers a DeserializeMaybeTuple to deserialize from either the tuple or named style.

    The existing Deserialize derive already behaves that way so I think this can be removed. Deserialize_tuple would be like a strict mode to reject non-tuple input, as well as an optimization to reduce compile time by only generating the tuple half.

  • Is there a reason to prefer Serialize_tuple over SerializeTuple? I've chosen the latter as I think it looks nicer, but I'm open to changing it.

    Yes, the naming convention is meant to indicate that this derive macro is deriving the Serialize trait, but it is the tuple version of that derive logic. I find SerializeTuple misleading because there is no such trait as SerializeTuple.

@kardeiz
Copy link

kardeiz commented Jan 30, 2019

Thanks for the comments!

I've never really thought about struct field position as meaningful, but that makes sense: the position attribute has to be in the struct definition, so you might as well go ahead and change the field order there.

I've been using serde for years, and I had no idea it would deserialize a tuple to a struct with named fields!

I've updated the names. I've also made everything generic (hopefully in a way that covers all cases).

https://github.com/kardeiz/serde_tuple/blob/master/src/lib.rs

@dtolnay
Copy link
Owner Author

dtolnay commented Jan 30, 2019

Nicely done. Let me know once the crate is documented and published to crates.io and I'll close out this issue.

@kardeiz
Copy link

kardeiz commented Jan 30, 2019

Thanks! See https://crates.io/crates/serde_tuple.

@dtolnay
Copy link
Owner Author

dtolnay commented Jan 30, 2019

Fantastic. I marked off the idea in the readme with a link to your crate.

@dtolnay dtolnay closed this as completed Jan 30, 2019
Repository owner deleted a comment from Mingun Jul 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants