radlang — XML (xml)
This document covers the xml namespace: parsing XML text into a navigable
node tree, searching it, and building/serializing XML from radlang values.
For language fundamentals see the Overview.
Overview
xml is a compiler-intrinsic namespace. The name is reserved and dispatched
directly to the runtime — it needs no import:
fn main() {
XmlNode n = xml.parse("<CustomTag key=\"x1\">hello world!</CustomTag>")
sys.output(xml.tag(n)) // CustomTag
sys.output(xml.attr(n, "key")) // x1
sys.output(xml.text(n)) // hello world!
} One built-in type appears here:
XmlNode— an opaque handle to a parsed XML element. Every element has a tag name, a list of attributes, direct text content, and a recursive list of child elements. You never construct it directly; you get one fromxml.parseorxml.from.
The model is <Tag attr="value" ...>text<Child/>...</Tag>. Nested elements are
found recursively.
Two equivalent call forms
Every operation on a node can be written namespace-first or as a method on the node — they compile to the same thing, so use whichever reads better:
xml.tag(n) // namespace form
n.tag() // method form — identical
n.child(0).tag() // chains naturally
xml.tag(xml.child(n, 0)) Only xml.parse and xml.from (which create nodes) are namespace-only.
Parsing — xml.parse
xml.parse(string): XmlNode parses XML text and returns the root element. The
parser skips the <?xml ?> prolog, comments (<!-- -->), and <!DOCTYPE> declarations, decodes the five predefined entities (< > & " '), and understands self-closing tags (<br/>) and <![CDATA[...]]>.
XmlNode root = xml.parse("<book id='7' lang='en'>Dune</book>")
xml.tag(root) // "book"
xml.text(root) // "Dune"
xml.attr(root, "id") // "7"
xml.hasAttr(root, "x") // false If the input has no element, xml.parse returns a null node — see xml.isNull.
Navigating
| Call | Returns | Meaning |
|---|---|---|
xml.tag(node) | string | element name |
xml.text(node) | string | direct text content (entities decoded) |
xml.attr(node, key) | string | attribute value, or "" if absent |
xml.hasAttr(node, key) | bool | whether the attribute is present |
xml.attrs(node) | string[] | the attribute names |
xml.childCount(node) | int | number of direct child elements |
xml.child(node, i) | XmlNode | the i-th direct child |
xml.children(node) | XmlNode[] | all direct child elements |
xml.isNull(node) | bool | true for the missing/empty sentinel |
string doc = "<library><book><title>A</title></book><book><title>B</title></book></library>"
XmlNode lib = xml.parse(doc)
xml.childCount(lib) // 2
XmlNode first = xml.child(lib, 0) // the first <book>
for (XmlNode b in xml.children(lib)) {
XmlNode t = xml.find(b, "title")
sys.output(xml.text(t)) // A, then B
} Searching — xml.find / xml.findAll
Both search descendants recursively (depth-first), not just direct children.
xml.find(node, tag): XmlNode— the first descendant with a matching tag, or the null node if none.xml.findAll(node, tag): XmlNode[]— every descendant with a matching tag.
XmlNode lib = xml.parse(doc)
XmlNode t = xml.find(lib, "title") // first <title> anywhere
sys.output(xml.text(t)) // A
XmlNode[] titles = xml.findAll(lib, "title") // all <title> descendants
sys.output(titles.length()) // 2 The null node
A lookup that finds nothing returns a null node rather than crashing. Guard
with xml.isNull before reading it:
XmlNode maybe = xml.find(root, "author")
if (!xml.isNull(maybe)) {
sys.output(xml.text(maybe))
} Reading tag/text/attr from a null node is safe (they return "").
Building — xml.from
xml.from(any value): XmlNode builds a node tree from a radlang value — the
inverse direction of json.from, but producing a navigable/serializable XmlNode instead of a string.
- A struct becomes an element named after its type, with one child element per field.
- Nested structs recurse into nested elements.
- List fields become a repeated element per item.
- Scalars and strings become the element’s text.
type Point { int x, int y }
fn main() {
Point p = Point(3, 4)
XmlNode n = xml.from(p)
sys.output(xml.tag(n)) // Point
sys.output(xml.string(n)) // <Point><x>3</x><y>4</y></Point>
} Serializing — xml.string
xml.string(XmlNode): string renders a node tree back to XML text, escaping
special characters and emitting self-closing tags for empty elements. It round-
trips a parsed document and turns an xml.from tree into output.
XmlNode n = xml.parse("<CustomTag key=\"x1\">hello world!</CustomTag>")
sys.output(xml.string(n)) // <CustomTag key="x1">hello world!</CustomTag> API reference
| Call | Signature |
|---|---|
| parse | fn xml.parse(string body): XmlNode |
| from | fn xml.from(any value): XmlNode |
| string | fn xml.string(XmlNode node): string |
| tag | fn xml.tag(XmlNode node): string |
| text | fn xml.text(XmlNode node): string |
| attr | fn xml.attr(XmlNode node, string key): string |
| hasAttr | fn xml.hasAttr(XmlNode node, string key): bool |
| attrs | fn xml.attrs(XmlNode node): string[] |
| isNull | fn xml.isNull(XmlNode node): bool |
| child | fn xml.child(XmlNode node, int index): XmlNode |
| childCount | fn xml.childCount(XmlNode node): int |
| children | fn xml.children(XmlNode node): XmlNode[] |
| find | fn xml.find(XmlNode node, string tag): XmlNode |
| findAll | fn xml.findAll(XmlNode node, string tag): XmlNode[] |
Notes & limits
- Attribute values may be single- or double-quoted.
- Text content is the element’s own text; child element text is not folded in.
xml.frommaps struct field names to element names; there is no typedxml.parse<T>deserialization yet (use the node API to read known shapes).