3 releases (stable)

1.0.1 Oct 12, 2024
1.0.0 May 31, 2024
0.0.0 May 27, 2024

#1017 in Configuration

MIT license

120KB
2K SLoC

KISS-XML: Keep It Super Simple XML

GitHub Workflow Build Status GitHub Workflow Test Status codecov Crate.io Redistribution license

This Rust library provides an easy-to-use Document Object Model (DOM) for reading and writing XML files. Unlike many other XML parsers, KISS-XML simply parses the given XML to a full DOM, which you can then modify and serialize back to XML. No schemas or looping required.

What's included:

KISS-XML provides the basics for XML documents, including:

  • Parse XML files and strings to a DOM
  • XML elements, text, and comments
  • DOM is mutable and can be saved as a string and to files
  • XML namespaces (with and without prefixes)
  • CDATA
  • Easy to use

What's NOT included:

  • Schema handling
  • Document type declarations (DTDs will be preserved but not interpreted)
  • Parsing character encodings other than UTF-8
  • Typed XML data (eg integer attribute values)
  • Performance optimizations (prioritizing easy-to-use over fast)

If you need any of the above excluded XML features, then this library is too simple for your needs. Try another XML parsing crate instead.

Quickstart Guide

First, add the following to your Cargo.toml file:

kiss_xml = "1"

Then to parse an XML file, all you need to do is call the kiss_xml::parse_filepath(...) function, like this:

fn main() -> Result<(), kiss_xml::errors::KissXmlError> {
	use kiss_xml;
	let doc = kiss_xml::parse_filepath("some-file.xml")?;
	println!("{}", doc.to_string());
	Ok(())
}

The XML content will be converted into a Document Object Model (DOM) with a single root element. A DOM is a tree-like data structure made up of XML Element, Text, and Comment nodes. You can explore the DOM element-by-element with the .elements_by_name(&str) and .first_element_by_name(&str) methods, scan the children of an element with the .children() and .child_elements() methods, or do a recursive search using the .search(...) and .search_*(...) methods.

For example:

fn main() -> Result<(), kiss_xml::errors::KissXmlError> {
	use kiss_xml;
	use kiss_xml::dom::*;
	use kiss_xml::errors::*;
	let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<config>
	<name>My Settings</name>
	<sound>
		<property name="volume" value="11" />
		<property name="mixer" value="standard" />
	</sound>
</config>
"#;
	// parse XML to a document object model (DOM)
	let dom = kiss_xml::parse_str(xml)?;
	// print all sound properties
	let properties = dom.root_element()
		.first_element_by_name("sound")?
		.elements_by_name("property");
	for prop in properties {
		println!(
			"{} = {}",
			prop.get_attr("name").ok_or(DoesNotExistError::default())?,
			prop.get_attr("value").ok_or(DoesNotExistError::default())?
		);
	}
	// print children of the root element
	for e in dom.root_element().child_elements() {
		println!("child element <{}>", e.name())
	}
	// print all elements
	for e in dom.root_element().search_elements(|_| true) {
		println!("found element <{}>", e.name())
	}
	Ok(())
}

To modify the DOM, use the .*_mut(...) methods to get mutable references to the elements, and you can convert the DOM to a string with the .to_string() method or write it to a file with .write_to_filepath(...).

For example:

fn main() -> Result<(), kiss_xml::errors::KissXmlError> {
	use kiss_xml;
	use kiss_xml::dom::*;
	use kiss_xml::errors::*;
	// make a DOM from scratch
	let mut doc = Document::new(Element::new_from_name("politicians")?);
	doc.root_element_mut().insert(0, Element::new_with_text("person", "John Adams")?);
	doc.root_element_mut().append(Element::new_with_text("person", "Hillary Clinton")?);
	doc.root_element_mut().append(Element::new_with_text("person", "Jimmy John")?);
	doc.root_element_mut().append(Element::new_with_text("person", "Nanny No-Name")?);
	// remove element by index
	let _removed_element = doc.root_element_mut().remove_element(3)?;
	// remove element(s) by use of a predicate function
	let _num_removed = doc.root_element_mut().remove_elements(|e| e.text() == "Jimmy John");
	// print first element content
	println!("First politician: {}", doc.root_element().first_element_by_name("person")?.text());
	// write to file
	doc.write_to_filepath("tests/politics.xml");
	Ok(())
}

For more details and examples, see the documentation.

How to Contribute

Found a bug? Want to add a new feature? Great! Here's what to do:

First, create an issue on the official KISS-XML GitHub page. Make sure that your issue description includes examples and use-cases.

Next, create one or more unit tests that fails unless the bug-fix/feature is correctly implemented. The unit tests can be proposed in your issue description or you can fork the KISS-XML repo and add them to the file tests/issues.rs. Make sure all test functions start with "test_issue_##" and contain a link to the GitHub issue thread in the description.

Finally, if you've implemented it yourself in a fork, create a pull request from your fork into the staging branch (PRs to main will be rejected).

Thank you!

License

This library is open source, licensed under the MIT License. You may use it as-is or with modification, without any limitations.

Dependencies

~2.2–3MB
~54K SLoC