xml
import xml from "xml";
let doc: xml.Doc = xml.parse(r"""<book id="b1"><title>Ownership <and> you</title></book>""") or {
io.println("bad XML: {err}");
return 1;
};
let id: string = doc.attr_value(doc.root, "id") or { "" }; // "b1"
let title: i64 = doc.node(doc.root).first;
io.println("{doc.node(title).name}: {doc.text_of(title)}"); // title: Ownership <and> youxml reads XML 1.0 documents in UTF-8 into a tree. It does not validate
against a DTD, but it reads the general entities a DOCTYPE declares, so
files from SVG editors that declare their namespaces that way read
correctly. xml is a pure module.
The tree
A xml.Doc keeps every node in one vector, doc.nodes, and nodes point
at each other by index. doc.root is the document element; xml.NONE
(-1) means “no node”.
pub struct Node {
kind: i64; // xml.ELEMENT or xml.TEXT
name: string; // an element's name as written, prefix included ("svg:rect")
attrs: Vec_Attr; // an element's attributes as written, in order: name, value
text: string; // a text node's text
parent: i64; first: i64; last: i64; next: i64;
}| Function | Does |
|---|---|
doc.node(id) | The node with that index (a read-only pointer) |
doc.attr_value(id, name) -> ?string | An element’s attribute by its full name; null when it has none |
doc.text_of(id) -> string | The text of a node and everything under it |
xml.local(name) | A name without its prefix: "xlink:href" gives "href" |
Walk an element’s children from first along next:
var c: i64 = doc.node(parent).first;
while (c != xml.NONE) {
if (doc.node(c).kind == xml.ELEMENT) { io.println(doc.node(c).name); }
c = doc.node(c).next;
}What is in the tree: elements, their attributes, and text, with <,
&, é, é and declared entities replaced and line ends
made \n. CDATA sections become text; comments, processing instructions
and the <?xml ...?> declaration are left out. Prefixes are kept as
written, not resolved to namespace URIs.
Errors
xml.parse stops at the first problem with an xml.XmlError. To know
where, use a Parser:
var p: xml.Parser = xml.parser(text);
let doc: xml.Doc = p.parse() or {
io.println("{err} at line {p.line()}, column {p.col()}"); // a repeated attribute at line 2, column 12
return 1;
};XmlError | Prints |
|---|---|
UnexpectedEnd | unexpected end of input |
NoRoot | no document element |
TextOutsideRoot | text outside the document element |
SecondRoot | a second document element |
MissingName | a tag without a name |
BadAttribute | a broken attribute |
RepeatedAttribute | a repeated attribute |
UnknownReference | an unknown entity or character reference |
MismatchedTag | an end tag that does not match |
UnclosedElement | an element left open |
xml.parse_lenient(bytes) never fails: it reads a broken document as far
as it goes (an end tag that doesn’t match closes up to the element it
names; an unknown reference stays as written) and puts the first problem
in doc.error, "" when there was none:
let doc: xml.Doc = xml.parse_lenient("<a><b>kept</a>");
io.println(doc.text_of(doc.root)); // kept
io.println(doc.error); // an end tag that does not match at line 1, column 11Building a tree
var d: xml.Doc = xml.new_doc();
let svg: i64 = d.element(xml.NONE, "svg"); // the first element without a parent is the root
d.set_attr(svg, "viewBox", "0 0 24 24");
let t: i64 = d.element(svg, "text");
d.add_text(t, "hello");Values are stored as given: no &...; is replaced.
Not supported: namespace URIs, DTD validation and default attributes,
external entities, encodings other than UTF-8 (convert with
charset first), writing a tree back out.
Last modified: