79次发布 (35次重大变更)

0.36.1 2024年7月23日
0.35.0 2024年6月29日
0.31.0 2023年10月23日
0.30.0 2023年7月23日
0.1.8 2016年3月29日

#3解析器实现 中排名第三

Download history 617535/week @ 2024-05-03 661962/week @ 2024-05-10 679916/week @ 2024-05-17 635916/week @ 2024-05-24 777415/week @ 2024-05-31 774258/week @ 2024-06-07 739098/week @ 2024-06-14 824563/week @ 2024-06-21 766963/week @ 2024-06-28 820283/week @ 2024-07-05 812561/week @ 2024-07-12 801275/week @ 2024-07-19 818675/week @ 2024-07-26 808665/week @ 2024-08-02 838612/week @ 2024-08-09 846413/week @ 2024-08-16

3,470,402 每月下载量
2,908 个crate中使用了(562个直接使用)

MIT 许可证

1MB
18K SLoC

quick-xml

status Crate docs.rs codecov MSRV

高性能XML拉取读取器/写入器。

读取器

  • 几乎零拷贝(尽可能使用Cow
  • 对内存分配友好(API提供了一种重用缓冲区的方法)
  • 支持各种编码(使用encoding功能)、命名空间解析、特殊字符。

语法灵感来源于xml-rs

示例

读取器

use quick_xml::events::Event;
use quick_xml::reader::Reader;

let xml = r#"<tag1 att1 = "test">
                <tag2><!--Test comment-->Test</tag2>
                <tag2>Test 2</tag2>
             </tag1>"#;
let mut reader = Reader::from_str(xml);
reader.config_mut().trim_text(true);

let mut count = 0;
let mut txt = Vec::new();
let mut buf = Vec::new();

// The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s)
loop {
    // NOTE: this is the generic case when we don't know about the input BufRead.
    // when the input is a &str or a &[u8], we don't actually need to use another
    // buffer, we could directly call `reader.read_event()`
    match reader.read_event_into(&mut buf) {
        Err(e) => panic!("Error at position {}: {:?}", reader.error_position(), e),
        // exits the loop when reaching end of file
        Ok(Event::Eof) => break,

        Ok(Event::Start(e)) => {
            match e.name().as_ref() {
                b"tag1" => println!("attributes values: {:?}",
                                    e.attributes().map(|a| a.unwrap().value)
                                    .collect::<Vec<_>>()),
                b"tag2" => count += 1,
                _ => (),
            }
        }
        Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()),

        // There are several other `Event`s we do not consider here
        _ => (),
    }
    // if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low
    buf.clear();
}

写入器

use quick_xml::events::{Event, BytesEnd, BytesStart};
use quick_xml::reader::Reader;
use quick_xml::writer::Writer;
use std::io::Cursor;

let xml = r#"<this_tag k1="v1" k2="v2"><child>text</child></this_tag>"#;
let mut reader = Reader::from_str(xml);
reader.config_mut().trim_text(true);
let mut writer = Writer::new(Cursor::new(Vec::new()));
loop {
    match reader.read_event() {
        Ok(Event::Start(e)) if e.name().as_ref() == b"this_tag" => {

            // crates a new element ... alternatively we could reuse `e` by calling
            // `e.into_owned()`
            let mut elem = BytesStart::new("my_elem");

            // collect existing attributes
            elem.extend_attributes(e.attributes().map(|attr| attr.unwrap()));

            // copy existing attributes, adds a new my-key="some value" attribute
            elem.push_attribute(("my-key", "some value"));

            // writes the event to the writer
            assert!(writer.write_event(Event::Start(elem)).is_ok());
        },
        Ok(Event::End(e)) if e.name().as_ref() == b"this_tag" => {
            assert!(writer.write_event(Event::End(BytesEnd::new("my_elem"))).is_ok());
        },
        Ok(Event::Eof) => break,
        // we can either move or borrow the event to write, depending on your use-case
        Ok(e) => assert!(writer.write_event(e).is_ok()),
        Err(e) => panic!("Error at position {}: {:?}", reader.error_position(), e),
    }
}

let result = writer.into_inner().into_inner();
let expected = r#"<my_elem k1="v1" k2="v2" my-key="some value"><child>text</child></my_elem>"#;
assert_eq!(result, expected.as_bytes());

Serde

当使用serialize功能时,quick-xml可以与serde的Serialize/Deserialize特性一起使用。XML和Rust类型之间的映射,特别是允许您指定元素和属性区分的语法,在反序列化文档中有详细描述。

致谢

这很大程度上受到了serde-xml-rs的启发。quick-xml遵循其反序列化约定,包括$value特殊名称。

解析标签的"值"

如果您有一个形式为 <foo abc="xyz">bar</foo> 的输入,并且您想获取 bar,您可以使用特殊名称 $text 或特殊名称 $value

struct Foo {
    #[serde(rename = "@abc")]
    pub abc: String,
    #[serde(rename = "$text")]
    pub body: String,
}

请参阅文档了解两者之间的区别。

性能

请注意,尽管没有专注于性能(存在一些不必要的复制),但它的速度仍然比 serde-xml-rs 快约 10 倍。

特性

  • encoding:支持非 utf8 xmls
  • serialize:支持 serde Serialize/Deserialize

性能

基准测试很困难,结果取决于您的输入文件和您的机器。

在我的特定文件上,quick-xml 的速度比 xml-rs 包快约 50 倍

// quick-xml benches
test bench_quick_xml            ... bench:     198,866 ns/iter (+/- 9,663)
test bench_quick_xml_escaped    ... bench:     282,740 ns/iter (+/- 61,625)
test bench_quick_xml_namespaced ... bench:     389,977 ns/iter (+/- 32,045)

// same bench with xml-rs
test bench_xml_rs               ... bench:  14,468,930 ns/iter (+/- 321,171)

// serde-xml-rs vs serialize feature
test bench_serde_quick_xml      ... bench:   1,181,198 ns/iter (+/- 138,290)
test bench_serde_xml_rs         ... bench:  15,039,564 ns/iter (+/- 783,485)

要比较特性和性能,您还可以查看 RazrFalcon 的解析器比较表

贡献

任何 PR 都受欢迎!

许可证

MIT

依赖项

~0.1–2.4MB
~57K SLoC