tafia / quick-xml

Rust high performance xml reader and writer
MIT License
1.22k stars 237 forks source link

Using `flatten` and `rename="$value"` on adjacent fields causes error #326

Open ch1nq opened 3 years ago

ch1nq commented 3 years ago

I have an issue similar to this issue in serde-xml-rs.

I have the following implementation, where i use flatten and rename="$value" on fields in the same struct:

use serde::Deserialize;
use quick_xml::de;

#[derive(Debug, Deserialize)]
struct ParentStruct {
    #[serde(flatten)]
    flat_struct: FlatStruct,
    #[serde(rename = "$value")]
    children: Vec<ChildStruct>,
}

#[derive(Debug, Deserialize)]
struct FlatStruct {
    a: String,
    b: String,
}

#[derive(Debug, Deserialize)]
struct ChildStruct {
    c: String,
    d: String,
}

fn main() {
    let xml = r##"
        <ParentStruct a="1" b="2">
            <ChildStruct c="3" d="4"/>
            <ChildStruct c="5" d="6"/>
        </ParentStruct>
    "##;

    let parent_struct: ParentStruct = de::from_str(xml).unwrap();
    dbg!(parent_struct);
}

I expect to get a struct similar to:

ParentStruct {
    flat_struct: FlatStruct {
        a: "1",
        b: "2",
    },
    children: [
        ChildStruct {
            c: "3",
            d: "4",
        },
        ChildStruct {
            c: "5",
            d: "6",
        },
    ],
}

But I get the following error:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("missing field `$value`")'

Using flatten or rename="$value" in isolation works perfectly, but the combination of the two seems to cause the error.

Is this a bug, or am I somehow using these macros wrong?

Mingun commented 2 years ago

@askepen, actually, your example is working since merging #490 after few changes:

#[test]
fn issue326() {
    #[derive(Debug, Deserialize, PartialEq)]
    struct ParentStruct {
        #[serde(flatten)]
        flat_struct: FlatStruct,

        #[serde(rename = "ChildStruct")]
        children: Vec<ChildStruct>,
    }

    #[derive(Debug, Deserialize, PartialEq)]
    struct FlatStruct {
        #[serde(rename = "@a")]
        a: String,
        #[serde(rename = "@b")]
        b: String,
    }

    #[derive(Debug, Deserialize, PartialEq)]
    struct ChildStruct {
        #[serde(rename = "@c")]
        c: String,
        #[serde(rename = "@d")]
        d: String,
    }

    assert_eq!(
        from_str::<ParentStruct>(
            r#"
            <ParentStruct a="1" b="2">
                <ChildStruct c="3" d="4"/>
                <ChildStruct c="5" d="6"/>
            </ParentStruct>
            "#
        )
        .unwrap(),
        ParentStruct {
            flat_struct: FlatStruct {
                a: "1".to_string(),
                b: "2".to_string(),
            },
            children: vec![
                ChildStruct {
                    c: "3".to_string(),
                    d: "4".to_string(),
                },
                ChildStruct {
                    c: "5".to_string(),
                    d: "6".to_string(),
                },
            ],
        }
    );
}

But I leave this bug open, because you spot a problem, that really exists:


#[test]
fn issue326_enum() {
    #[derive(Debug, Deserialize, PartialEq)]
    struct ParentStruct {
        #[serde(flatten)]
        flat_struct: FlatStruct,

        #[serde(rename = "$value")]
        children: Vec<Enum>,
    }

    #[derive(Debug, Deserialize, PartialEq)]
    struct FlatStruct {
        #[serde(rename = "@a")]
        a: String,
        #[serde(rename = "@b")]
        b: String,
    }

    #[derive(Debug, Deserialize, PartialEq)]
    enum Enum {
        ChildStruct {
            #[serde(rename = "@c")]
            c: String,
            #[serde(rename = "@d")]
            d: String,
        }
    }

    // Result:
    // thread 'issue326_enum' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("missing field `$value`")', 
    assert_eq!(
        from_str::<ParentStruct>(
            r#"
            <ParentStruct a="1" b="2">
                <ChildStruct c="3" d="4"/>
                <ChildStruct c="5" d="6"/>
            </ParentStruct>
            "#
        )
        .unwrap(),
        ParentStruct {
            flat_struct: FlatStruct {
                a: "1".to_string(),
                b: "2".to_string(),
            },
            children: vec![
                Enum::ChildStruct {
                    c: "3".to_string(),
                    d: "4".to_string(),
                },
                Enum::ChildStruct {
                    c: "5".to_string(),
                    d: "6".to_string(),
                },
            ],
        }
    );
}

Problem disappeared after commenting out the flat_struct field. I don't known, although, if it would be possible to fix the issue on the quick-xml side. serde(flatten) issues usually due serde-rs/serde#1183

avantgardnerio commented 5 months ago

I am also experiencing this. @Mingun , if you have any pointers, I can attempt a PR to fix.

Mingun commented 5 months ago

I debug such problems from expanding macros and passage though deserializer in my mind. When reason would be found, then I though how it can be fixed. I have no better suggestions rather that that.