Crates
hl7-2-xml-lite-helper
The small, dependency-free XML reader this family shares
What it is
It reads the subset that carries meaning in a data document — elements, attributes, text, and nesting — and skips the rest. No validation, no schema, no DTD, no namespace resolution, no streaming.
let xml = r#"<order id="7"><item qty="2">widget</item></order>"#;
let root = hl7_2_xml_lite_helper::parse(xml)?;
assert_eq!(root.attribute("id"), Some("7"));
assert_eq!(root.child("item").unwrap().text, "widget");The name says what it is for. Nothing in the code is HL7-specific, but the crate is scoped to
serve hl7-2-soap, hl7-3-soap, hl7-2-from-xml-into-er7, hl7-2-from-xsd-into-json-dictionary, and hl7-3 — and its trade-offs are chosen for the
documents those read.
What it is for — and what it is not
For: reading a document produced by a system you are talking to, where you know which elements you want and simply need them out — a SOAP envelope, an XML Schema, an HL7 v2.xml message. It exists because three crates in this family had each written their own version of exactly this, and three copies of a parser is three places for a bug.
Namespace prefixes are ignored, not resolved
This is the single most important thing to understand about this crate. Elements and attributes
are matched on their local name, so soapenv:Body, soap:Body, SOAP-ENV:Body and Body are the same element.
It is a deliberate trade. The prefix is chosen by whoever serialized the document, and code that insists on one prefix rejects valid documents from every other tool — which is the single most common way a working SOAP integration breaks when the other end changes stack.
The cost is that a document relying on the distinction between two namespaces that share local names will be misread. Reach for a namespace-aware parser there.
Finding things
let root = hl7_2_xml_lite_helper::parse(
"<PID><PID.3><CX.1>a</CX.1></PID.3><PID.3><CX.4><HD.1>NHS</HD.1></CX.4></PID.3></PID>",
)?;
// A path down to the first non-blank value, following *every* branch —
// a repeating field puts several elements of the same name side by side.
assert_eq!(root.text_at(&["PID.3", "CX.4", "HD.1"]), Some("NHS"));
// Or walk it yourself.
assert_eq!(root.child("PID.3").unwrap().child("CX.1").unwrap().text, "a");
assert_eq!(root.children_named("PID.3").count(), 2);
assert_eq!(root.find("HD.1").unwrap().text, "NHS"); // first descendanttext_at follows every branch down to the first non-blank value, which
matters because a repeating field puts several elements of the same name side by side — walking
only the first would silently miss the value.
What is skipped, and what is not
Skipped: the XML declaration, comments, processing instructions, and a DOCTYPE, wherever they appear.
Kept: CDATA content, as text. Entities — the five predefined ones and numeric character references — decode; anything else that looks like an entity is kept literally rather than rejected.
Whitespace-only text beside child elements is dropped, because it is indentation. Text in a leaf is kept exactly as it arrived, because a leading or trailing space can be part of a value.
Writing
escape covers all five predefined entities, so a value is safe in element content or
in an attribute. That is the whole of the writing surface — this crate reads.
Dependencies
None, and staying that way: a crate whose whole argument is that it is small enough to read cannot have dependencies you also have to read.
Related crates
hl7-2-soap
HL7 v2 carried in a SOAP envelope over HTTP
hl7-3-soap
HL7 v3 carried in a SOAP envelope over HTTP
hl7-2-from-xml-into-er7
v2.xml XML → ER7
hl7-2-from-xsd-into-json-dictionary
HL7 v2.xml XSDs → the JSON dictionary hl7-2 reads