xml

Read and write XML. xml.parse(text) gives a node map {tag, attrs, children, text} with find(path) and find_all(path) using a small XPath subset ("item", "a/b", ".//name", "item[@id='3']", "item[2]"). xml.stringify(node) writes a node (or a plain map with those keys) back out. Documents with a DOCTYPE are rejected, which protects against entity-expansion attacks.

import xml

Example

import xml

let doc = xml.parse(
  "<scan host=\"192.0.2.5\"><port id=\"22\" state=\"open\"/><port id=\"80\" state=\"closed\"/></scan>",
)
say(doc.tag, doc.attrs.host)
for p in doc.find_all("port") {
  say(p.attrs.id, p.attrs.state)
}
say(xml.stringify(xml.node("note", {"by": "Tanisha"}, ["checked"])))

Functions

parse(text)

returns node

Read XML text into a node: {"tag", "attrs" (map), "children" (list of nodes), "text"} plus find(path), find_all(path), value(path), attr(name), all_text(). A DOCTYPE is refused.

Visualizehow xml.parse works

xml.parse("<feed><item id=\"1\">Hello</item><item id=\"2\">Bye</item></feed>") gives {"tag": "feed", "attrs": {}, "children": [{"tag": "item", "attrs": {"id": "1"}, "children…

xml.parse: inputs flowing into the function and the resulttext<feed><item id="1">Hello</item><itemid="2">Bye</item></feed>xml.parsea node: tag, attrs, children, texttag"feed"attrs{}children[{"tag": "item", "attrs": {"id": "…nodes also have find(path), find_all(path), value(…

Real result: worked out by running this example with TB while the site was built.

stringify(node, options?)

returns text

Turn a node into XML text. Text is escaped for you. The node can come from parse, or be a plain map: {"tag": "item", "attrs": {...}, "children": [...], "text": "..."}.

Options

  • indent the indent text (default: " "). false puts it all on one line.
  • declaration true adds the <?xml ...?> header.
Visualizehow xml.stringify works

xml.stringify(xml.node("feed", {}, [xml.node("item", {"id": 1}, ["Hello"])]), {}) gives "<feed>\n <item id=\"1\">Hello</item>\n</feed>"

xml.stringify: inputs flowing into the function and the resultnodexml.node("feed", {}, [xml.node("item",{"id": 1}, ["Hello"])])options?{} (empty map)xml.stringifyXML text, escaped for you<feed> <item id="1">Hello</item></feed>nodexml.node("feed", {}, [xml.node("item",{"id": 1}, ["Hello"])])options?indentfalsexml.stringifyXML text, escaped for you<feed><item id="1">Hello</item></feed>nodexml.node("feed", {}, [xml.node("item",{"id": 1}, ["Hello"])])options?declarationtruexml.stringifyXML text, escaped for you<?xml version="1.0" encoding="UTF-8"?><feed> <item id="1">Hello</item></feed>

Real result: worked out by running this example with TB while the site was built.

read(path)

returns node

Read an XML file (see parse).

Visualizehow xml.read works

xml.read("feed.xml").find_all("item").map(n => n.text) gives ["Hello", "Bye"]

xml.read: inputs flowing into the function and the resultpath"feed.xml"feed.xml<feed> <itemid="1">Hello</item> <itemid="2">Bye</item></feed>xml.read(...).find_all("item")the items' text"Hello""Bye"

Real result: worked out by running this example with TB while the site was built.

write(path, node, options?)

Save a node as an XML file (with the <?xml ?> header).

Visualizehow xml.write works

xml.write("note.xml", xml.node("note", {"lang": "en"}, ["Hi & bye"]))

xml.write: inputs flowing into the function and the resultpath"note.xml"nodexml.node("note", {"lang": "en"}, ["Hi &bye"])xml.writenote.xml afterwards (read back)<?xml version="1.0" encoding="UTF-8"?><note lang="en">Hi &amp; bye</note>

Real result: worked out by running this example with TB while the site was built.

node(tag, attrs?, children?)

returns map

Make a node to build documents with: xml.node("item", {"id": 1}, ["Hello"]). children may hold nodes and plain text.

Visualizehow xml.node works

xml.node("item", {"id": 1}, ["Hello"]) gives {"tag": "item", "attrs": {"id": 1}, "children": [], "text": "Hello"}

xml.node: inputs flowing into the function and the resulttag"item"attrs?id1children?"Hello"xml.nodea node map, ready for stringify()tag"item"attrs{map}id1children[]text"Hello"

Real result: worked out by running this example with TB while the site was built.

escape(text)

returns text

Text made safe inside XML.

Changes

  • < becomes &lt;
  • > becomes &gt;
  • & becomes &amp;
Visualizehow xml.escape works

xml.escape("5 < 7 & 9 > 3") gives "5 &lt; 7 &amp; 9 &gt; 3"

xml.escape: inputs flowing into the function and the resulttext"5 < 7 & 9 > 3"xml.escapesafe inside XML5 &lt; 7 &amp; 9 &gt; 3

Real result: worked out by running this example with TB while the site was built.

quote_attr(text)

returns text

Text quoted as an attribute value, like "a &amp; b" with its quotes.

Visualizehow xml.quote_attr works

xml.quote_attr("R&D \"lab\"") gives "'R&amp;D \"lab\"'"

xml.quote_attr: inputs flowing into the function and the resulttext"R&D "lab""xml.quote_attran attribute value, quotes included'R&amp;D "lab"'

Real result: worked out by running this example with TB while the site was built.

is_node(x)

returns bool

True if x looks like an XML node (a map with a text "tag").

Visualizehow xml.is_node works

xml.is_node(xml.node("a")) gives true

xml.is_node: inputs flowing into the function and the resultxxml.node("a")xml.is_nodea map with a text tag?truextag"item"xml.is_nodea map with a text tag?truex"<a/>"xml.is_nodea map with a text tag?falsexname"x"xml.is_nodea map with a text tag?false

Real result: worked out by running this example with TB while the site was built.