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 在 解析器实现
9,325 每月下载量
在 25 个crate中使用 (23 个直接使用)
265KB
5K SLoC
Rust的XML库
xml-rs 是 Rust 编程语言的 XML 库。它支持以流式方式(无 DOM)读取和写入 XML 文档。
[!NOTE] 此库既可用作
xml-rs
,也可用作xml
。xml-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::Read
的 take(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
文档。