Repository navigation
Change the representation of Enums #717
Copy link
Copy link
Open
Labels
help wantedquestionserdeIssues related to mapping from Rust types to XMLIssues related to mapping from Rust types to XML
Description
serde_json supports enums:
| Kind | Enum field |
|---|---|
| Unit | "field": "Unit", |
| Newtype | "field": { "Newtype": 42 }, |
| Tuple | "field": { "Tuple": [42, "answer"] }, |
| Struct | "field": { "Struct": {"q": 42, "a":"answer"} }, |
enum Kinds { // Enum name as:
Unit, // Value
Newtype(i32), // Key
Tuple((i32, String)), // Key
Struct(MyStruct), // Key
}https://docs.rs/quick-xml/latest/quick_xml/de/index.html#normal-enum-variant
so quick-xml should too:
| Kind | Enum field |
|---|---|
| Unit | <field>Unit</field> |
| Newtype | <field><Newtype>42</Newtype></field> |
| Tuple | <field><Tuple>42</Tuple><Tuple>answer</Tuple></field> |
| Struct | <field><Struct><q>42</q><a>answer</a></Struct></field> |
enum Kinds { // Enum name as:
Unit, // Value
Newtype(i32), // Tag
Tuple((i32, String)), // Tag
Struct(MyStruct), // Tag
}Currently the above XML won't be generated (gives an error) while JSON would even parse back into Rust enum.
Unsupported operation: cannot serialize enum newtype variant
[Details] Ser/deserialization of enum - crate comparison:
// [package]
// name = "xml_vs_json"
// version = "0.1.0"
// edition = "2021"
// [dependencies]
// eyre = "0.6.12"
// quick-xml = { version = "0.31.0", features = ["serde", "serialize"] }
// rmp-serde = "1.1.2"
// serde = { version = "1.0.197", features = ["derive"] }
// serde_json = "1.0.114"
// serde_yaml = "0.9.32"
// toml = "0.8.10"
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
pub struct MyStruct {
pub i8_value: i8,
pub choice_value: MyChoice,
pub choice_list: Vec<MyChoice>,
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
pub enum MyChoice {
ChoiceA(MyChoiceA),
ChoiceB(MyChoiceB),
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
pub struct MyChoiceA {
pub choice_i32: i32,
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
pub struct MyChoiceB {
pub choice_i64: i64,
pub choice_text: String,
}
fn main() {
let result = run();
if let Err(err) = result {
println!("err: {err}");
}
}
fn run() -> eyre::Result<()> {
let a = MyStruct {
i8_value: 8,
choice_value: MyChoice::ChoiceA(MyChoiceA { choice_i32: 32 }),
choice_list: vec![
MyChoice::ChoiceA(MyChoiceA { choice_i32: 32 }),
MyChoice::ChoiceB(MyChoiceB {
choice_i64: 64,
choice_text: "my text".to_string(),
}),
MyChoice::ChoiceA(MyChoiceA { choice_i32: 32 }),
],
};
try_reencode_all(&a)?;
Ok(())
}
fn try_reencode_all(a: &MyStruct) -> eyre::Result<()> {
// pass
let a_json = reencode_json(&a)?;
assert_eq!(a, &a_json);
// pass
let a_toml = reencode_toml(&a)?;
assert_eq!(a, &a_toml);
// pass
let a_yaml = reencode_yaml(&a)?;
assert_eq!(a, &a_yaml);
// pass
let a_msgpack = reencode_msgpack(&a)?;
assert_eq!(a, &a_msgpack);
// fail: Unsupported operation: cannot serialize enum newtype variant `MyChoice::ChoiceA`
let a_qxml = reencode_quick_xml(&a)?;
assert_eq!(a, &a_qxml);
Ok(())
}
fn reencode_json(input: &MyStruct) -> eyre::Result<MyStruct> {
let json_str: String = serde_json::to_string(&input)?;
println!("serde_json: {json_str}");
let output = serde_json::from_str(&json_str)?;
Ok(output)
}
fn reencode_toml(input: &MyStruct) -> eyre::Result<MyStruct> {
let toml_str: String = toml::to_string(&input)?;
println!("toml: {toml_str}");
let output = toml::from_str(&toml_str)?;
Ok(output)
}
fn reencode_yaml(input: &MyStruct) -> eyre::Result<MyStruct> {
let yaml_str: String = serde_yaml::to_string(&input)?;
println!("yaml: {yaml_str}");
let output = serde_yaml::from_str(&yaml_str)?;
Ok(output)
}
fn reencode_msgpack(input: &MyStruct) -> eyre::Result<MyStruct> {
let msgpack_bytes: Vec<u8> = rmp_serde::to_vec(&input)?;
println!("rmp_serde: {msgpack_bytes:?}");
let output = rmp_serde::from_slice(&msgpack_bytes)?;
Ok(output)
}
fn reencode_quick_xml(input: &MyStruct) -> eyre::Result<MyStruct> {
// serialization does not work
let qxml_str: String = quick_xml::se::to_string(&input)?;
println!("quick_xml::se: {qxml_str}");
let output = quick_xml::de::from_str(&qxml_str)?;
Ok(output)
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
help wantedquestionserdeIssues related to mapping from Rust types to XMLIssues related to mapping from Rust types to XML
Activity
Mingun commentedon Feb 24, 2024
You can find answer in this chapter about mapping of Rust types to XML. TL;DR: your enum variant is not a unit variant and it cannot be serialized in arbitrary-named field of struct.
dishmaker commentedon Feb 26, 2024
Then why does it work for
toml,yaml,msgpackandderbut does not forquick-xml?It forces me to use a feature flag in compile time - just to produce a little different code for
quick-xml.Is there any way to show
serde(rename)exclusively to this crate?dishmaker commentedon Feb 26, 2024
But still, even when using
#[serde(rename = "$value")]it does not serialize<any_name> ... </any_name>but only the inside variant.Mingun commentedon Feb 26, 2024
It is hard to answer to your questions because you do not provide your expectations. The mentioned piece of documentation shows how quick-xml performs mapping in a consistent manner. If you have concrete suggestions, please describe them and even better open a PR with them!
dishmaker commentedon Feb 26, 2024
My expectations:
Just like JSON:
dishmaker commentedon Mar 21, 2024
Issue should be reopened - other crates support enum lists.
Only quick-xml is a black sheep here.
Mingun commentedon Mar 21, 2024
You feel free to submit PR that would implement the desired behavior and make it in the consistent way. Probably this is possible. We also should keep the ability to use tag name as enum discriminator, because this is natural way how
xs:choicein XML is represented. It must be representable with Rustenum.[-]Enum/choice list does not deserialize[/-][+]Change the representation of Enums[/+]flotang-gtt commentedon Nov 12, 2024
If we accept that quick-xml does not do this - is the only way to get around this to write a custom serializer/deserializer with kinda "hardcoding" the variants? Do you see any obstacles with that approach?
DennisJensen95 commentedon Nov 12, 2024
@flotang-gtt I think you can get around it with this response on StackOverflow, I am working to try it out now: https://stackoverflow.com/questions/78444158/unsupportedcannot-serialize-enum-newtype-variant-exampledata 😊
DennisJensen95 commentedon Nov 12, 2024
@flotang-gtt i managed to get stuff working with struct wrappers like so:
Where i then have
So here the wrappers can handle the enum variant where we then use
$valuethat way it works for me at least 😊lethargy123 commentedon Sep 10, 2025
What is the current progress of this issue?
dishmaker commentedon Sep 16, 2025
I tried some refactor but this trait system is so hard for me I couldn't even compile a proof of concept 🫠