Skip to content

Custom newtype deserialization logic is skipped when used with #[serde(flatten)] #2106

@patrickfreed

Description

@patrickfreed

In order to specially handle the (de)serialization of a custom newtype, I added logic to the serialize_newtype_struct and deserialize_newtype_struct methods that only executes if the name of the newtype is recognized, according to the pattern described here (the context is supporting a custom Uuid wrapper in BSON).

However, this pattern breaks when attempting to deserialize structs that are part of a #[serde(flatten)] chain, since the root deserializer's deserialize_newtype_struct method is never actually invoked. Serialization appears unaffected.

e.g. given the following

const NEWTYPE_NAME: &str = "$__myspecialstringtype";

struct MyNewtype(String);

impl Serialize for MyNewtype {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_newtype_struct(NEWTYPE_NAME, &self.0)
    }
}

impl<'de> Deserialize<'de> for MyNewtype {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct MyVisitor;

        impl<'de> Visitor<'de> for MyVisitor {
            type Value = MyNewtype;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                todo!()
            }

            /* custom visitor stuff here */
        }

        deserializer.deserialize_newtype_struct(NEWTYPE_NAME, MyVisitor)
    }
}


impl<'de> Deserializer<'de> for MyDeserializer {
    // ... other deserialize stuff here ...

    fn deserialize_newtype_struct<V>(
        self,
        name: &'static str,
        visitor: V,
    ) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        if name == NEWTYPE_NAME {
            // visit something else
        } else {
            visitor.visit_newtype_struct(self)
        }
    }
}

The following struct's derived Deserialize implementation never calls MyDeserializer::deserialize_newtype_struct, skipping the custom logic. Instead, it calls it on serde::de::private::ContentDeserializer, which just calls into visit_newtype_struct.

#[derive(Deserialize, Serialize)]
struct MyData {
    header: String,
    #[serde(flatten)]
    body: BodyData
}

#[derive(Deserialize, Serialize)]
struct BodyData {
    field: i32,
    nt: MyNewtype
}

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