#xml-parser #xml #xml-document #pull-parser #parser #writer #sax

bin+lib xml-rs

纯 Rust 编写的 XML 库

27 个版本

0.8.21 2024 年 8 月 5 日
0.8.20 2024 年 3 月 31 日
0.8.19 2023 年 9 月 23 日
0.8.16 2023 年 7 月 19 日
0.1.5 2014 年 12 月 30 日

#12 in 解析实现

Download history 337190/week @ 2024-05-04 381038/week @ 2024-05-11 391862/week @ 2024-05-18 363411/week @ 2024-05-25 404855/week @ 2024-06-01 397834/week @ 2024-06-08 373823/week @ 2024-06-15 378353/week @ 2024-06-22 363366/week @ 2024-06-29 385886/week @ 2024-07-06 390978/week @ 2024-07-13 400400/week @ 2024-07-20 377043/week @ 2024-07-27 385074/week @ 2024-08-03 462833/week @ 2024-08-10 361871/week @ 2024-08-17

1,654,453 个月下载量
用于 4,575 个crate(341 个直接使用)

MIT 许可证

260KB
5K SLoC

xml-rs,Rust 的 XML 库

CI crates.io docs

文档

xml-rs 是 Rust 编程语言的 XML 库。它支持以流式方式(不使用 DOM)读取和写入 XML 文档。

功能

  • 比其他纯 Rust 库更好地符合 XML 规范。

  • 基于 Iterator 和常规 String 的简单 API,易于使用,无需复杂的生命周期。

  • 支持 UTF-16、UTF-8、ISO-8859-1 和 ASCII 编码。

  • 完全使用安全 Rust 子集编写。设计用于安全处理不受信任的输入。

API 受 Java Streaming API for XML (StAX) 的强烈启发。它包含一个类似于 StAX 事件读取器的拉解析器。它提供了一个迭代器 API,因此您可以利用 Rust 现有的迭代器库功能。

它还提供了一个类似于 StAX 事件写入器的流式文档写入器。此写入器消耗其自己的事件集,但读取器事件可以轻松转换为写入器事件,因此可以以非常整洁的方式编写 XML 转换链。

该解析器功能齐全,但也有一些限制

  • 不支持旧版代码页和非 Unicode 编码;
  • 不支持 DTD 验证(但支持内部子集中定义的实体);
  • 不执行属性值归一化,也不对换行符进行归一化。

除此之外,解析器尽量符合 XML-1.1 规范。

写入器也功能齐全,但有以下限制

  • 不支持除 UTF-8 之外的其他编码。
  • 不支持发出 <!DOCTYPE> 声明;
  • 需要更多的输入验证,例如检查命名空间前缀是否有限制或注释是否格式正确。

构建和使用

xml-rs 使用 Cargo,因此使用 cargo add xml 或修改 Cargo.toml

[dependencies]
xml = "0.8.16"

该包公开了一个名为 xml 的单一 crate。

读取 XML 文档

xml::reader::EventReader 需要一个 Read 实例来读取。它可以是包裹在 BufReader 中的 File,或者一个 Vec<u8>,或者一个 &[u8] 切片。

EventReader 实现了 IntoIterator 特性,因此您可以直接在 for 循环中使用它

use std::fs::File;
use std::io::BufReader;

use xml::reader::{EventReader, XmlEvent};

fn main() -> std::io::Result<()> {
    let file = File::open("file.xml")?;
    let file = BufReader::new(file); // Buffering is important for performance

    let parser = EventReader::new(file);
    let mut depth = 0;
    for e in parser {
        match e {
            Ok(XmlEvent::StartElement { name, .. }) => {
                println!("{:spaces$}+{name}", "", spaces = depth * 2);
                depth += 1;
            }
            Ok(XmlEvent::EndElement { name }) => {
                depth -= 1;
                println!("{:spaces$}-{name}", "", spaces = depth * 2);
            }
            Err(e) => {
                eprintln!("Error: {e}");
                break;
            }
            // There's more: https://docs.rs/xml-rs/latest/xml/reader/enum.XmlEvent.html
            _ => {}
        }
    }

    Ok(())
}

文档解析可以正常结束或以错误结束。无论具体原因如何,解析过程都将停止,迭代器将正常终止。

您还可以使用其自己的 next() 方法更精细地控制从解析器中拉取下一个事件的时间

match parser.next() {
    ...
}

在文档结束时或出现错误后,解析器将记住最后一个事件,并在随后的 next() 调用中始终返回它。如果使用迭代器,则它将一次产生错误或文档结束事件,之后将产生 None

您还可以使用 xml::reader::ParserConfig 结构稍微调整解析过程。请参阅其文档以获取更多信息示例。

您可以在 src/analyze.rs 中找到使用 EventReader 的更广泛示例,这是一个小型程序(顺便说一下,它使用 cargo build 构建,并在构建后可以运行)它显示了指定 XML 文档的多种统计数据。它也可以用于检查 XML 文档的格式正确性 - 如果文档格式不正确,则此程序将错误退出。

解析不受信任的输入

解析器是用 Rust 的安全子集编写的,因此根据 Rust 的保证,最坏的情况是引发恐慌。您可以使用 ParserConfig 设置名称、属性、文本、实体等的最大长度限制。您还应该通过 io::Readtake(max) 方法设置最大文档大小。

编写 XML 文档

xml-rs 还提供了一个类似于 StAX 事件编写器的流式写入器。使用它,您可以向任何 Write 实现器写入 XML 文档。

use std::io;
use xml::writer::{EmitterConfig, XmlEvent};

/// A simple demo syntax where "+foo" makes `<foo>`, "-foo" makes `</foo>`
fn make_event_from_line(line: &str) -> XmlEvent {
    let line = line.trim();
    if let Some(name) = line.strip_prefix("+") {
        XmlEvent::start_element(name).into()
    } else if line.starts_with("-") {
        XmlEvent::end_element().into()
    } else {
        XmlEvent::characters(line).into()
    }
}

fn main() -> io::Result<()> {
    let input = io::stdin();
    let output = io::stdout();
    let mut writer = EmitterConfig::new()
        .perform_indent(true)
        .create_writer(output);

    let mut line = String::new();
    loop {
        line.clear();
        let bytes_read = input.read_line(&mut line)?;
        if bytes_read == 0 {
            break; // EOF
        }

        let event = make_event_from_line(&line);
        if let Err(e) = writer.write(event) {
            panic!("Write error: {e}")
        }
    }
    Ok(())
}

上面的代码示例还演示了如何从配置中创建一个写入器。类似的事情也适用于 EventReader

该库提供了一个 XML 事件构建 DSL,有助于构建复杂的事件,例如具有命名空间定义的事件。以下是一些示例

// <a:hello a:param="value" xmlns:a="urn:some:document">
XmlEvent::start_element("a:hello").attr("a:param", "value").ns("a", "urn:some:document")

// <hello b:config="name" xmlns="urn:default:uri">
XmlEvent::start_element("hello").attr("b:config", "value").default_ns("urn:defaul:uri")

// <![CDATA[some unescaped text]]>
XmlEvent::cdata("some unescaped text")

当然,也可以直接创建 XmlEvent 枚举变体,而不是使用构建 DSL。更多示例可以在 xml::writer::XmlEvent 文档中找到。

写入器有多个配置选项;有关更多信息,请参阅 EmitterConfig 文档。

错误报告

请在以下地址报告问题: https://github.com/kornelski/xml-rs/issues

无运行时依赖