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 |
|
#12 in 解析实现
1,654,453 个月下载量
用于 4,575 个crate(341 个直接使用)
260KB
5K SLoC
xml-rs,Rust 的 XML 库
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::Read
的 take(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。