将XML文件读入结构体

16
我正试图编写一个程序,将XML文件读取到先前定义的Rust结构体中。
类似于这样:
<?xml version="1.0" encoding="UTF-8"?>
<note name="title">
  <body name="main_body">
    <layer content_type="something" count="99">
      <data id="13">
        Datacontent
      </data>
    </layer>
  </body>
</note>

变成这样:

struct Note {
    name: String,
    Body: Body 
}

struct Body {
    name: String,
    layers: Vec<Layer>,
}

struct Layer {
    content_type: String,
    count: u8,
    data: Vec<Data>,
}

struct Data {
    id: u8,
    // Datacontent?
}

我查看了xml-rs,因为它当前似乎是最受欢迎的XML库。由于我对Rust还很陌生,所以我很难弄清楚如何执行此任务。

1个回答

34

Rust提供了很好的自动生成(反)序列化代码的支持。有遗留的rustc-serialize,它需要非常少的设置。然后有一个完全新的(反)序列化框架serde,允许许多格式和详细的自定义配置,但需要更多的初始设置。

我将说明如何使用serde+ serde_xml_rs 将XML反序列化为Rust-structs。

将crates添加到您的Cargo.toml

我们可以手动实现反序列化代码,也可以通过使用serde_derivecrate自动生成它。

[dependencies]
serde_derive = "1.0"
serde = "1.0"
serde-xml-rs = "0.3.1"

给你的结构体添加注解

Serde需要了解你的结构体。为了帮助它并避免为项目中的每个结构体生成代码,你需要注解你想要的结构体。 Debug 派生是为了我们可以轻松使用 println! 打印结构体以检查是否一切正常。 Deserialize 约束是通知 serde 生成代码的条件。如果你想将XML标签的内容视为文本,你需要“重命名”应该包含文本的字段为 $value。在创建 serde_xml_rs crate 时,对 $value 的命名非常随意,但永远不会与实际字段冲突,因为字段名称不能包含 $ 符号。

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_xml_rs;

#[derive(Deserialize, Debug)]
struct Note {
    name: String,
    body: Body,
}

#[derive(Deserialize, Debug)]
struct Body {
    name: String,
    #[serde(rename="layer")]
    layers: Vec<Layer>,
}

#[derive(Deserialize, Debug)]
struct Layer {
    content_type: String,
    count: u8,
    data: Vec<Data>,
}

#[derive(Deserialize, Debug)]
struct Data {
    id: u8,
    #[serde(rename="$value")]
    content: String,
}

将包含xml的字符串转换为对象

现在来到了简单的部分。在你的字符串上调用 serde_xml::from_str,你会得到一个Node类型的值或者一个错误:

fn main() {
    let note: Note = serde_xml_rs::deserialize(r##"
<?xml version="1.0" encoding="UTF-8"?>
<note name="title">
  <body name="main_body">
    <layer content_type="something" count="99">
      <data id="13">
        Datacontent
      </data>
    </layer>
  </body>
</note>
    "##.as_bytes()).unwrap();
    println!("{:#?}", note);
}

1
我必须将这个添加到 Cargo.toml 文件中,而不是:serde-xml-rs = "0.2.1"。 - Frederick Ollinger
2
解析的说明文本引用了 serde_xml::from_str,但代码使用了 serde_xml_rs::deserialize。(它们是否已经被编辑过?)在 serde_xml_rs 0.3 中删除了 deserialize 方法,但是在当前版本 0.4.0 中可以使用 from_reader 解析字节(因为 &[u8] 实现了 Read)。但由于 API 的变化以及其对一些正常的 XML 内容(如 Unicode BOM 或 DOCTYPE 声明)的敏感性,serde_xml_rs 看起来并不特别适合目前的需求。serde_xml 已经被废弃。 - pndc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接