serde-xml-rs
serde-xml-rs copied to clipboard
Cannot Deserialize Recursive Elements
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>
}
- A recursive enum will correctly decode but only for 1 level. That is, the
$valueis not found by deserializer for child elements. - A recursive struct will correctly decode but only if there is no text. If there is text, there is an error. Furthermore, the type of the element is lost (eg, file vs. dir).
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
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...
@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.
@SeedyROM ,...
Not sure I follow.... "A section of code is with a 1K words" :)
Has this been answered, having same problem.
@Capicou ... It's been so long I forget... I'll check when I got my head above water...
Let me know, still have not figured out.
@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:
- Latest
serde-xml-rs(0.6.0) still has this issue quick-xml(0.26.0) is able to correctly deserialize into recursive enum. It has problems with recursive structs but there may be a way to organize or add tags in such a way as to make it work.
Thanks, I had figured it out.
@git-blame, the initial problem isn't really a bug. You can't use a plain String as the $value of an InodeStruct.
@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.