Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use "flatten" like thing in custom Serialize and Deserialize

Tags:

rust

serde

I need to to use custom implementations of Serialize and Deserialize, but i could not figure out how to do something like #[serde(flatten)] does, does anyone know?

Note: i know i could completely re-write the full implementation of the lower elements into the higher one, but the lower elements implement Serialize (and Deserialize), so i am searching for a way to add that to something like serialize_struct.

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Nested {
    somefield2: String,
}

#[derive(Debug, PartialEq)]
struct TopLevel {
    somefield1: usize,

    nested: Nested,
}

impl Serialize for TopLevel {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // How to do this properly?
        let mut do_struct = serializer.serialize_struct("Named", 2)?;
        do_struct.serialize_field("somefield1", &self.somefield1)?;

        // how to add everything from "self.nested" as the same level as this one?
        // JSON example: { somefield1: 0, somefield2: 0 }

        return do_struct.end();
    }
}

impl<'de> Deserialize<'de> for TopLevel {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // Same question as in "Serialize", how to do this properly in here?
        // Here is currently no example code, because i try to figure out "Serialize" first
        todo!();
    }
}

Versions used:

  • serde: 1.0.133
  • rust: 1.58.1

Note: i have already read Implementing Serialize and tried to search issues / stackoverflow but could not find anything related to that.

like image 987
hasezoey Avatar asked Oct 14 '25 16:10

hasezoey


1 Answers

You could try doing something like this for Serialize and something similar for Deserialize:

struct FakeStructFlatteningSerializer<'a, SS: SerializeStruct>(&'a mut SS);

impl Serializer<'a, SS: SerializeStruct> for FakeStructFlatteningSerializer<'a, SS> {
    type Ok = ();
    type Error = SS::Error;
    type SerializeStruct = FakeStructFlatteningSerializeStruct<'a, SS>;
    
    // return Impossible for everything else
    
    fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct, Self::Error> {
        // ignore name and len!
        Ok(FakeStructFlatteningSerializeStruct(self.0))
    }
}

struct FakeStructFlatteningSerializeStruct<'a, SS: SerializeStruct>(&'a mut SS);

impl<'a, SS: SerializeStruct> SerializeStruct for FakeStructFlatteningSerializeStruct<'a, SS> {
    type Ok = ();
    type Error = SS::Error;

    fn serialize_field<T: Serialize + ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> {
        self.0.serialize_field(key, value)
    }
    
    fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> {
        self.0.skip_field(key)
    }
    
    fn end(self) -> Result<Self::Ok, Self::Error> {
        // ignore!
        Ok(())
    }
}



impl Serialize for TopLevel {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // len needs to include the flattened fields
        let mut do_struct = serializer.serialize_struct("Named", 3)?;
        do_struct.serialize_field("somefield1", &self.somefield1)?;
        self.nested.serialize(FakeStructFlatteningSerializer(&mut do_struct));
        return do_struct.end();
    }
}

You could alternatively try to figure out how Serde does it; this might be where: https://github.com/serde-rs/serde/blob/dc0c0dcba17dd8732cd8721a7ef556afcb04c6c0/serde_derive/src/ser.rs#L953-L1037, https://github.com/serde-rs/serde/blob/fb2fe409c8f7ad6c95e3096e5e9ede865c8cfb49/serde_derive/src/de.rs#L2560-L2578

like image 108
Solomon Ucko Avatar answered Oct 18 '25 01:10

Solomon Ucko