serde icon indicating copy to clipboard operation
serde copied to clipboard

can serialize but can't deserilize when combined `tag = ""` with `flatten`

Open SuniRein opened this issue 3 months ago • 1 comments

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Package {
    #[serde(default, flatten)]
    pub kind: PackageType,

    #[serde(default)]
    pub var: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "kind", rename_all = "kebab-case", deny_unknown_fields)]
pub enum PackageType {
    Local {},

    Git(GitPkg),
}

impl Default for PackageType {
    fn default() -> Self {
        Self::Local {}
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct GitPkg {
    url: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let pkg = Package {
        kind: PackageType::Local {},
        var: "a".into(),
    };
    let json = serde_json::to_string_pretty(&pkg)?;
    println!("{json}");

    let pkg = serde_json::from_str::<Package>(&json)?;
    dbg!(pkg);

    Ok(())
}

This is the output:

{
  "kind": "local",
  "var": "a"
}
Error: Error("unknown field `kind`", line: 4, column: 1)

SuniRein avatar Sep 19 '25 09:09 SuniRein

This error produced because of #[serde(deny_unknown_fields)] on Package. When this option is enabled and flatten fields are present, then content firstly buffered in FlatMapDeserializer. Then internally tagged enum deserialized from it using deserialize_any, which forwarded to deserialize_map https://github.com/serde-rs/serde/blob/179954784683f35942ac2e1f076e0361b47f8178/serde/src/private/de.rs#L3249-L3258 which provides FlatMapAccess which does not consume data from the deserializer: https://github.com/serde-rs/serde/blob/179954784683f35942ac2e1f076e0361b47f8178/serde/src/private/de.rs#L3358-L3374

Therefore, when it checks for the remaining fields, it sees those processed but not consumed fields.

Related: #1909.

Mingun avatar Sep 21 '25 19:09 Mingun