Home > Software design >  Parse XML tag with multiple possible types [Rust, serde, serde-xml-rs]
Parse XML tag with multiple possible types [Rust, serde, serde-xml-rs]

Time:10-05

I want to parse an XML element that may contain different data types.

The following is the most simple case I could think of:

use serde_derive::{Deserialize, Serialize};
use serde_xml_rs::{from_str};

const XML: &str = r#"
<element>
    foo
</element>
"#;
const XML2: &str = r#"
<element>
    123
</element>
"#;

#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Element {
    #[serde(rename="element")]
    Text(String),
    #[serde(rename="element")]
    Number(i32),
}

fn main() {
    let el: Element = from_str(&XML).unwrap();
    println!("{:?}", &el);
    println!("\n\n\n\n");
    let el2: Element = from_str(&XML2).unwrap();
    println!("{:?}", &el2);
}

The current code parses both elements as Text(String), instead of Text(String), Number(i32) respectively.

If I reverse the order the program crashes, because "foo" cannot be parsed as integer.

#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Element {
    #[serde(rename="element")]
    Number(i32),
    #[serde(rename="element")]
    Text(String),
}

How can I correctly implement my program?

CodePudding user response:

If I understand your post correctly, you want Number whenever the value can be parsed as a i32 and Text otherwise. You can implement that using the FromStr trait.

#[derive(Serialize, serde_with::DeserializeFromStr, Debug, PartialEq)]
enum Element {
    #[serde(rename="element")]
    Text(String),
    #[serde(rename="element")]
    Number(i32),
}

impl std::str::FromStr for Element {
    type Err = std::convert::Infallible;
    
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Ok(i) = s.parse::<i32>() {
            Ok(Element::Number(i))
        } else {
            Ok(Element::Text(s.to_string()))
        }
    }
}
  • Related