xml
Parse XML, look things up with simple paths, change a document in place and write it back.
import "std:xml"xml reads, queries, edits and writes XML. Reach for it with RSS and Atom feeds, SVG, SOAP-style APIs and older config files. You parse text into a Document and ask it questions with short paths such as feed>entry>title.
import "std:xml"Parse and look things up#
xml.parse returns a result holding a document. text gives an element's own text and attr an attribute, both null when nothing matches.
import "std:xml"
string text = `<library name="City">
<book id="1"><title>Dune</title><year>1965</year></book>
<book id="2"><title>Emma</title><year>1815</year></book>
</library>`
auto doc = xml.parse(text).unwrap()
log doc.text("library>book>title")
log doc.attr("library", "name")
log doc.attr("book", "id")
log doc.text("missing")
log doc.has("year") doc.has("author")A path is a list of element names joined with >. Each name matches at any depth below the one before, so book>title works without spelling out library. The query returns the first match in document order.
Try it: ask for doc.attr("book", "missing") and see null.
Every match#
findAll returns each matching element as a record with name, text, attrs and children.
import "std:xml"
auto doc = xml.parse(`<lib><book id="1">Dune</book><book id="2">Emma</book></lib>`).unwrap()
for b in doc.findAll("book") (
log b.attrs["id"] b.text
)
log doc.findAll("book").len
log doc.count("lib")
log doc.findAll("magazine")count(path) counts the child elements of the first match, here the two books in lib. To count matches of a path, use findAll(path).len.
Errors#
Malformed XML is a ParseError with a line number. A file that doesn't exist is NotFoundError.
import "std:xml"
log xml.isValid("<a><b></a>") xml.isValid("<a/>")
switch xml.parse("<a><b></a>") (
case ok(d)
log "parsed"
case err(e)
log e.kind
)
log xml.parseFile("no-such.xml").unwrapErr().kindEdit in place#
setText, setAttr, remove and clear change the document, and toStr writes it back. Editing a path that matches nothing is a NotFoundError, so a typo doesn't silently do nothing. Attributes are written in sorted order and text is escaped.
import "std:xml"
auto doc = xml.parse(`<config><name>old</name><debug>1</debug><tmp>x</tmp></config>`).unwrap()
doc.setText("name", "new & improved").unwrap()
doc.setAttr("config", "version", 2).unwrap()
doc.remove("tmp").unwrap()
doc.clear("debug").unwrap()
log doc.toStr()
log doc.setText("nope", "x").unwrapErr().kindTry it: set the text of "nope" and read the error kind.
Real-world: reading a feed#
Walk the records findAll gives you and collect what you need:
import "std:xml"
string feed = `<rss><channel><title>News</title>
<item><title>First</title><link>http://x/1</link></item>
<item><title>Second</title><link>http://x/2</link></item>
</channel></rss>`
auto doc = xml.parse(feed).unwrap()
log "feed:" doc.text("channel>title")
for item in doc.findAll("item") (
string title = ""
for c in item.children (
if c.name == "title" ( title = c.text )
)
log "-" title
)The whole tree#
root() returns the root element with all its children as one nested record. That is handy for converting a document to JSON:
import "std:xml"
import "std:json"
auto doc = xml.parse(`<p class="x">hi <b>there</b></p>`).unwrap()
log json.stringify(doc.root())doc.text("book>title") when the file has three books returns...
List the items
Print id title for every item. The title is the item's own text.
1 Apple 2 Pear 3 Fig
API reference#
21 members, generated from the std:xml source of OSL 9.3.0.
Functions
xml.isValid(string text) booleanisValid reports whether xml.parse would accept text.
xml.parse(string text) result<*xml.Document, error>parse reads an XML document from text. Query it with doc.find, doc.text and doc.attr using paths such as "feed>entry>title": each name matches an element at any depth below the previous one. Fails with ParseError, naming the line, for malformed XML.
xml.parseFile(string path) result<*xml.Document, error>parseFile reads and parses the XML file at path like xml.parse. Fails with NotFoundError, PermissionError or IOError when the file can't be read and ParseError when it isn't valid XML.
*xml.Document
document.attr(string path, string name) string?attr returns the attribute name of the first element matching path, or null when nothing matches or the element has no such attribute.
document.clear(string path) result<unit, error>clear deletes the text and children of the first element matching path, in place, keeping the element and its attributes. Fails with NotFoundError when nothing matches.
document.count(string path) intcount returns how many child elements the first element matching path has, or 0 when nothing matches. To count the elements matching a path, use doc.findAll(path).len.
document.find(string path) {attrs: string[string], children: object[], name: string, text: string}?find returns the first element matching path as a record {name, text, attrs, children}, or null when nothing matches. text is the element's own text, joined and trimmed; attrs maps attribute names, prefixes included ("xlink:href"), to values; children are records too.
document.findAll(string path) {attrs: string[string], children: object[], name: string, text: string}[]findAll returns every element matching path as records (see doc.find), in document order; [] when nothing matches.
document.get(any path) objectDeprecated. Deprecated: use doc.find(path) instead.
get returns the first element matching path as an object, or null.
document.getAll(any path) arrayDeprecated. Deprecated: use doc.findAll(path) instead.
getAll returns every element matching path as objects.
document.getAttr(any path, any attr) anyDeprecated. Deprecated: use doc.attr(path, name) instead.
getAttr returns an attribute of the first element matching path, or null.
document.getText(any path) anyDeprecated. Deprecated: use doc.text(path) instead.
getText returns the text of the first element matching path, or null.
document.has(string path) booleanhas reports whether any element matches path.
document.hasAttr(string path, string name) booleanhasAttr reports whether the first element matching path has the attribute name.
document.remove(string path) result<unit, error>remove deletes the first element matching path, and its children, in place; text around it stays. Fails with NotFoundError when nothing matches.
document.root() {attrs: string[string], children: object[], name: string, text: string}?root returns the document's root element as a record with its whole tree (see doc.find), or null for an empty document.
document.setAttr(string path, string name, any value) result<unit, error>setAttr sets the attribute name of the first element matching path, in place. Fails with NotFoundError when nothing matches.
document.setText(string path, any value) result<unit, error>setText replaces the text of the first element matching path, in place, keeping its child elements. Fails with NotFoundError when nothing matches.
document.text(string path) string?text returns the own text of the first element matching path, joined and trimmed, or null when nothing matches.
document.toArr() arrayDeprecated. Deprecated: use doc.root() instead.
toArr lists the top-level elements as objects.
document.toStr() stringtoStr writes the document as XML text. Attributes are written in sorted order and text is escaped; an empty document gives "".