Skip to content

Field $value is consumed too early when deserializing nested externally/internally tagged enums #905

@ahraman

Description

@ahraman

Suppose I have the following prototype XML:

<container>
    <a />
    <b kind="first" />
    <b kind="second">This child has <italic>mixed</italic> <bold>content</bold>, so I <shout>DO</shout> need `$value` to parse properly.</b>
</container>

The natural way to represent this using serde would be to use an externally tagged enum to distinguish between <a> and <b> tags, and then a secondary internally tagged enum for distinguishing <b> tags in terms of attribute @kind, as follows:

#[derive(Serialize, Deserialize)]
#[serde(rename = "container")]
struct Container {
    #[serde(rename = "$value")]
    pub items: Vec<Child>,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum Child {
    A(()),
    B(ChildB),
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "lowercase", tag = "@kind")]
enum ChildB {
    First(()),
    Second(ChildBSecond),
}

#[derive(Serialize, Deserialize)]
struct ChildBSecond {
    #[serde(rename = "$value")]
    pub items: Vec<ChildBItem>,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum ChildBItem {
    #[serde(rename = "$text")]
    Text(String),
    Italic(String),
    Bold(String),
    Shout(String),
}

However, attempting to parse the given XML input:

let container: Container = quick_xml::de::from_str(TEXT).unwrap();

with this structure using quick_xml::de deserializer panics with the following message:

called `Result::unwrap()` on an `Err` value: Custom("missing field `$value`")`

The problem seems to be from my debugging that during deserialization of ChildB, that the field $value is consumed and converted into its constituent parts, and so when it comes to ChildBItem's turn, the field is missing, but the deserialized key-value sequence is present.

Now, I'm not sure if this is a known issue, but I've run into this problem while attempting to deserialize the Vulkan XML registry file, which frequently uses this XML structures to represent various types and enums. I tried various methods to circumvent this issue, but I couldn't find an elegant method, so now I'm rolling out my own deserializer.

It would be to my own convenience if anyone here knows how to fix this, or if a fix to this is possible within the library itself.

EDIT: added clarification as to what the source of the problem might be.

EDIT 2: removed extraneous tabs from the code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    serdeIssues related to mapping from Rust types to XML

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions