4个版本

0.8.20 2024年3月31日
0.8.10 2023年5月12日
0.8.9 2023年5月12日
0.0.1 2020年5月5日

#58解析器实现

Download history 914/week @ 2024-04-23 986/week @ 2024-04-30 1135/week @ 2024-05-07 1093/week @ 2024-05-14 1179/week @ 2024-05-21 1717/week @ 2024-05-28 1427/week @ 2024-06-04 1347/week @ 2024-06-11 1741/week @ 2024-06-18 2270/week @ 2024-06-25 2163/week @ 2024-07-02 2283/week @ 2024-07-09 2125/week @ 2024-07-16 2549/week @ 2024-07-23 1798/week @ 2024-07-30 2336/week @ 2024-08-06

9,325 每月下载量
25 个crate中使用 (23 个直接使用)

MIT 协议

265KB
5K SLoC

Rust的XML库

CI crates.io docs

文档

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

[!NOTE] 此库既可用作 xml-rs,也可用作 xmlxml-rs crate 用于与现有项目兼容。下一个主要版本将转移到 xml crate。

功能

  • XML 规范符合性优于其他纯Rust库。

  • 基于 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.20"

该包公开了一个名为 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的保证,它最坏的情况是导致panic。您可以使用 ParserConfig 设置名称、属性、文本、实体等的最大长度限制。您还应该通过 io::Readtake(max) 方法设置最大文档大小。

编写XML文档

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

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

依赖项