Rust serde Как десериализовать xml «странные» списки?

#xml #rust #serde

#xml #Ржавчина #serde

Вопрос:

Я пытаюсь десериализовать этот стиль списка xml, который технически не является списком, но ведет себя как один:

 <list>
    <id-00001>
        <name type="string">Pedro</name>
        <age type="number">37</age>
    </id-00001>
    <id-00002>
        <name type="string">Alex</name>
        <age type="number">30</age>
    </id-00002>
<list>
 

Количество элементов в «списке» является переменным и будет увеличивать только число (x) в id-0000x .

Проблема в том, что я не могу придумать, как сопоставить это со структурой Rust с помощью serde.

Я пытаюсь сделать что-то вроде этого:

 
#[derive(Debug, Default, Deserialize)]
#[serde(rename = "list")]
struct List {
    people: Vec<Person>
}

#[derive(Debug, Default, Deserialize)]
struct Person {
    name: String,
    age: u8
}
 

но я не знаю, как обращаться с id-0000x тегами.

Редактировать: это зависимости, которые я использую:

 [dependencies]
serde = { version = "1.0.117", features = ["derive"] }
serde-xml-rs = "0.4.0"
 

Заранее спасибо за помощь.
С уважением

Комментарии:

1. То, что вам нужно, — это синтаксический анализатор, а не serde .

2. Почему age теги заканчиваются на prof ?

3. не могли бы вы, пожалуйста, указать мне на пример синтаксического анализатора, решающего аналогичную проблему?

Ответ №1:

Вероятно, проще всего просто десериализовать теги:

 struct List(BTreeMap<String, Person>);
 

Это даст вам упорядоченную коллекцию пар «id-bla» => Person.

Ответ №2:

Благодаря предложению Томаса Херста, если решить проблемы, используя что-то вроде этого:

 use serde::{Deserialize, Deserializer}; // 1.0.94
use serde_xml_rs;
use std::collections::HashMap;

const XML: amp;str = r#"
<?xml version="1.0" encoding="utf-8"?>
<root version="1">
    <list>
        <id-00001>
            <name type="string">Pedro</name>
            <age type="number">37</age>
        </id-00001>
        <id-00002>
            <name type="string">Alex</name>
            <age type="number">30</age>
        </id-00002>
    </list>
</root>
"#;

#[derive(Debug, Default, Deserialize)]
struct List {
    #[serde(deserialize_with = "deserialize_list")]
    list: Vec<Person>,
}

#[derive(Debug, Default, Deserialize, Clone)]
struct Person {
    name: String,
    age: u8,
}

fn deserialize_list<'de, D>(d: D) -> Result<Vec<Person>, D::Error>
where
    D: Deserializer<'de>,
{
    let people_raw: HashMap<String, Person> = Deserialize::deserialize(d)?;
    let people: Vec<Person> = people_raw.values().map(|person| person.clone()).collect();
    Ok(people)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let data: List = serde_xml_rs::from_str(XML)?;
    println!("{:?}", data);
    Ok(())
}

 

вывод:

 List { list: [Person { name: "Pedro", age: 37 }, Person { name: "Alex", age: 30 }] }
 

Комментарии:

1. Вместо .map(…) вы можете использовать .cloned() , хотя лучше всего может быть .into_iter() для получения принадлежащих значений напрямую. Также обратите внимание, что при использовании HashMap вы теряете порядок, поэтому я использую BTreeMap для упорядочивания по ключам.

2. В конце концов, меня не волнует порядок, мне просто нужен массив, я посмотрю на into_iter, чтобы избежать копирования. Спасибо за вашу помощь!!