use oxml::{XPath, parse, xpath::Value};
const DOC: &str = r#"<?xml version="1.0"?>
<shop>
<item price="9.99" stock="3">Tea</item>
<item price="4.50" stock="0">Coffee</item>
<item price="12.00" stock="7">Cocoa</item>
</shop>
"#;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let doc = parse(DOC)?;
for expr in [
"//item", "string(//item[1])", "sum(//item/@price)", "count(//item) > 2", ] {
let compiled = XPath::compile(expr)?;
let value = compiled.evaluate(&doc);
let kind = match value {
Value::NodeSet(_) => "node-set",
Value::String(_) => "string",
Value::Number(_) => "number",
Value::Boolean(_) => "boolean",
};
println!("{expr:<24} -> {kind}");
}
println!("\n== converting between them ==");
let items = XPath::compile("//item")?.evaluate(&doc);
println!(" to_boolean : {}", items.to_boolean());
println!(" to_str : {:?}", items.to_str(&doc));
println!(" to_number : {}", items.to_number(&doc));
println!(
" nodes : {} found",
items.nodes().unwrap_or(&[]).len()
);
let empty = XPath::compile("//nothing")?.evaluate(&doc);
println!("\n empty node-set to_boolean: {}", empty.to_boolean());
let tea = XPath::compile("string(//item[1])")?.evaluate(&doc);
println!(
" {:?} as a number: {}",
tea.to_str(&doc),
tea.to_number(&doc)
);
let count = XPath::compile("count(//item)")?.evaluate(&doc);
println!("\n a number's to_str: {:?}", count.to_str(&doc));
println!(" a number's nodes: {:?}", count.nodes());
println!("\n== relative to a context node ==");
let rows = XPath::compile("//item")?;
let price = XPath::compile("@price")?;
let name = XPath::compile("string(.)")?;
for &node in rows.evaluate(&doc).nodes().unwrap_or(&[]) {
println!(
" {:<8} {}",
name.evaluate_from(&doc, node).to_str(&doc),
price.evaluate_from(&doc, node).to_number(&doc),
);
}
println!("\n== namespaces ==");
let ns = parse(
r#"<r xmlns:m="urn:u"><m:item>ns</m:item><item>plain</item></r>"#,
)?;
let bound = XPath::compile_with_namespaces("//q:item", &[("q", "urn:u")])?;
println!(
" //q:item bound to urn:u -> {:?}",
bound.evaluate(&ns).to_str(&ns)
);
let bare = XPath::compile("//item")?;
println!(
" //item (no namespace) -> {:?}",
bare.evaluate(&ns).to_str(&ns)
);
match XPath::compile("//m:item") {
Ok(_) => println!(" unexpectedly compiled"),
Err(e) => println!(" //m:item unbound -> {e}"),
}
println!("\n== the compiled form ==");
let compiled = XPath::compile("//item[@stock > 0]/@price")?;
println!(" {:?}", compiled.expr());
match XPath::compile("//item[") {
Ok(_) => println!("\n unexpectedly compiled"),
Err(e) => println!("\n rejected at compile time: {e}"),
}
Ok(())
}