RReverser / serde-xml-rs

xml-rs based deserializer for Serde (compatible with 1.0+)
https://crates.io/crates/serde-xml-rs
MIT License
269 stars 90 forks source link

Cannot Deserialize Recursive Elements #82

Open git-blame opened 5 years ago

git-blame commented 5 years ago

For example, consider a directory which can contain directories and files. We try to declare recursive enum and struct:

#[derive(Deserialize, Debug)]
#[serde(rename_all="lowercase")]
enum Inode {
    File {
        name: String,
        #[serde(rename(deserialize="$value"), default)]
        value: String,
    },
    Dir {
        name: String,
        #[serde(rename(deserialize="$value"), default)]
        inode: Vec<Box<Inode>>,
    }
}

#[derive(Deserialize, Debug)]
struct InodeEnumList {
    #[serde(rename="$value")]
    list: Vec<Inode>
}

#[derive(Deserialize, Debug)]
#[serde(rename_all="lowercase")]
struct InodeStruct {
    #[serde(default)]
    name: String,
    #[serde(rename(deserialize="$value"), default)]
    inode: Vec<Box<InodeStruct>>,
}

#[derive(Deserialize, Debug)]
struct InodeList {
    #[serde(rename="$value")]
    list: Vec<InodeStruct>
}
git-blame commented 5 years ago

Example output:

    <list>
        <file name="README.md"></file>
        <file name="LICENCE.md"></file>
        <dir name="empty" />
        <dir name="files" >
            <file name="main.rs"></file>
        </dir>
        <dir name="mix">
            <file name="lib.rs"></file>
            <dir name="test">
                <dir name="empty" />
                <file name="test.rs"></file>
                <dir name="subdir">
                    <file name="nested.rs"></file>
                </dir>
            </dir>
        </dir>
    </list>

InodeEnumList {
    list: [
        File {
            name: "README.md",
            value: ""
        },
        File {
            name: "LICENCE.md",
            value: ""
        },
        Dir {
            name: "empty",
            inode: []
        },
        Dir {
            name: "files",
            inode: []
        },
        Dir {
            name: "mix",
            inode: []
        }
    ]
}
InodeList {
    list: [
        InodeStruct {
            name: "README.md",
            inode: []
        },
        InodeStruct {
            name: "LICENCE.md",
            inode: []
        },
        InodeStruct {
            name: "empty",
            inode: []
        },
        InodeStruct {
            name: "files",
            inode: [
                InodeStruct {
                    name: "main.rs",
                    inode: []
                }
            ]
        },
        InodeStruct {
            name: "mix",
            inode: [
                InodeStruct {
                    name: "lib.rs",
                    inode: []
                },
                InodeStruct {
                    name: "test",
                    inode: [
                        InodeStruct {
                            name: "empty",
                            inode: []
                        },
                        InodeStruct {
                            name: "test.rs",
                            inode: []
                        },
                        InodeStruct {
                            name: "subdir",
                            inode: [
                                InodeStruct {
                                    name: "nested.rs",
                                    inode: []
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}

    <list>
        <file name="README.md">README.md</file>
        <file name="LICENCE.md">LICENCE.md</file>
        <dir name="empty" />
        <dir name="files" >
            <file name="main.rs">main.rs</file>
        </dir>
        <dir name="mix">
            <file name="lib.rs">lib.rs</file>
            <dir name="test">
                <dir name="empty" />
                <file name="test.rs">test.rs</file>
                <dir name="subdir">
                    <file name="nested.rs">nested.rs</file>
                </dir>
            </dir>
        </dir>
    </list>

InodeEnumList {
    list: [
        File {
            name: "README.md",
            value: "README.md"
        },
        File {
            name: "LICENCE.md",
            value: "LICENCE.md"
        },
        Dir {
            name: "empty",
            inode: []
        },
        Dir {
            name: "files",
            inode: []
        },
        Dir {
            name: "mix",
            inode: []
        }
    ]
}
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: 
Error(UnexpectedToken("XmlEvent::StartElement { name, attributes, .. }", "Characters(README.md)"), 
State { next_error: None, backtrace: None })', libcore/result.rs:1009:5
JRAndreassen commented 4 years ago

I have the same issue with:

<?xml version="1.0" encoding="UTF-8"?>
<ValueLookup id="123">
 <Lookups>
  <item state="None" value="0"  >Unknown</item>
  <item state="Ok"   value="1">It fails on this value</item>
 </Lookups>
</ValueLookup>

    UnexpectedToken(
        "XmlEvent::EndElement { name, .. }",
        "StartElement(SingleInt, {\"\": \"\", \"xml\": \"http://www.w3.org/XML/1998/namespace\", \"xmlns\": \"http://www.w3.org/2000/xmlns/\", \"xsi\": \"http://www.w3.org/2001/XMLSchema-instance\"}, [state -> Ok, value -> 1])",
    ),

It would also be awesome if the Error could indicate Line number and Column for the parsing errors. And perhaps the current buffer...

SeedyROM commented 3 years ago

@JRAndreassen I solved this issue by creating a struct for the root element, it seems that it's searching into the XML parser state but the end of because your ValueLookup hasn't been parsed.

JRAndreassen commented 3 years ago

@SeedyROM ,...

Not sure I follow.... "A section of code is with a 1K words" :)

Capicou commented 1 year ago

Has this been answered, having same problem.

JRatPrtgconsultants commented 1 year ago

@Capicou ... It's been so long I forget... I'll check when I got my head above water...

Capicou commented 1 year ago

Let me know, still have not figured out.

git-blame commented 1 year ago

@Capicou I am now using the crate quick-xml for XML deserialization but not for this specific issue. I wrote up a quick test and found that:

recurse.zip

Capicou commented 1 year ago

Thanks, I had figured it out.

punkstarman commented 1 year ago

@git-blame, the initial problem isn't really a bug. You can't use a plain String as the $value of an InodeStruct.

punkstarman commented 1 year ago

@JRatPrtgconsultants (or @JRAndreassen is it?), to help with the problem you encountered, I would need the Rust code you were using.

I agree that the error message could be better.