# quick-xml **Repository Path**: rustup/quick-xml ## Basic Information - **Project Name**: quick-xml - **Description**: from crate.io - **Primary Language**: Rust - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-06-01 - **Last Updated**: 2021-06-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # quick-xml [![Build Status](https://travis-ci.org/tafia/quick-xml.svg?branch=master)](https://travis-ci.org/tafia/quick-xml) [![Crate](http://meritbadge.herokuapp.com/quick-xml)](https://crates.io/crates/quick-xml) High performance xml pull reader/writer. The reader: - is almost zero-copy (use of `Cow` whenever possible) - is easy on memory allocation (the API provides a way to reuse buffers) - support various encoding (with `encoding` feature), namespaces resolution, special characters. [docs.rs](https://docs.rs/quick-xml) Syntax is inspired by [xml-rs](https://github.com/netvl/xml-rs). ## Example ### Reader ```rust use quick_xml::Reader; use quick_xml::events::Event; let xml = r#" Test Test 2 "#; let mut reader = Reader::from_str(xml); reader.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 { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => { match e.name() { b"tag1" => println!("attributes values: {:?}", e.attributes().map(|a| a.unwrap().value).collect::>()), b"tag2" => count += 1, _ => (), } }, Ok(Event::Text(e)) => txt.push(e.unescape_and_decode(&reader).unwrap()), Ok(Event::Eof) => break, // exits the loop when reaching end of file Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), _ => (), // 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(); } ``` ### Writer ```rust use quick_xml::Writer; use quick_xml::Reader; use quick_xml::events::{Event, BytesEnd, BytesStart}; use std::io::Cursor; use std::iter; let xml = r#"text"#; let mut reader = Reader::from_str(xml); reader.trim_text(true); let mut writer = Writer::new(Cursor::new(Vec::new())); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { Ok(Event::Start(ref e)) if e.name() == b"this_tag" => { // crates a new element ... alternatively we could reuse `e` by calling // `e.into_owned()` let mut elem = BytesStart::owned(b"my_elem".to_vec(), "my_elem".len()); // 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(ref e)) if e.name() == b"this_tag" => { assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"my_elem"))).is_ok()); }, Ok(Event::Eof) => break, // you can use either `e` or `&e` if you don't want to move the event Ok(e) => assert!(writer.write_event(&e).is_ok()), Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), } buf.clear(); } let result = writer.into_inner().into_inner(); let expected = r#"text"#; assert_eq!(result, expected.as_bytes()); ``` ## Serde When using the `serialize` feature, quick-xml can be used with serde's `Serialize`/`Deserialize` traits. Here is an example deserializing crates.io source: ```rust // Cargo.toml // [dependencies] // serde = { version = "1.0", features = [ "derive" ] } // quick-xml = { version = "0.21", features = [ "serialize" ] } extern crate serde; extern crate quick_xml; use serde::Deserialize; use quick_xml::de::{from_str, DeError}; #[derive(Debug, Deserialize, PartialEq)] struct Link { rel: String, href: String, sizes: Option, } #[derive(Debug, Deserialize, PartialEq)] #[serde(rename_all = "lowercase")] enum Lang { En, Fr, De, } #[derive(Debug, Deserialize, PartialEq)] struct Head { title: String, #[serde(rename = "link", default)] links: Vec, } #[derive(Debug, Deserialize, PartialEq)] struct Script { src: String, integrity: String, } #[derive(Debug, Deserialize, PartialEq)] struct Body { #[serde(rename = "script", default)] scripts: Vec }"; let html: Html = from_str(xml)?; assert_eq!(&html.head.title, "crates.io: Rust Package Registry"); Ok(html) } ``` ### Credits This has largely been inspired by [serde-xml-rs](https://github.com/RReverser/serde-xml-rs). quick-xml follows its convention for deserialization, including the [`$value`](https://github.com/RReverser/serde-xml-rs#parsing-the-value-of-a-tag) special name. ### Parsing the "value" of a tag If you have an input of the form `bar`, and you want to get at the `bar`, you can use the special name `$value`: ```rust,ignore struct Foo { pub abc: String, #[serde(rename = "$value")] pub body: String, } ``` ### Performance Note that despite not focusing on performance (there are several unecessary copies), it remains about 10x faster than serde-xml-rs. # Features - `encoding`: support non utf8 xmls - `serialize`: support serde `Serialize`/`Deserialize` ## Performance Benchmarking is hard and the results depend on your input file and your machine. Here on my particular file, quick-xml is around **50 times faster** than [xml-rs](https://crates.io/crates/xml-rs) crate. ``` // 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) ``` For a feature and performance comparison, you can also have a look at RazrFalcon's [parser comparison table](https://github.com/RazrFalcon/roxmltree#parsing). ## Contribute Any PR is welcomed! ## License MIT