A small regex-based XML reader for the UN list file. The document is split into record strings, and fields are read from each record with regexes: element text by tag, nested blocks, and attributes of start tags. Character references are decoded on the way out.

This is not a general XML parser. It relies on the file format: record elements do not nest inside themselves, field elements hold plain text, and there are no CDATA sections or comments inside records.

Why not ::hot::xml for the whole file. Speed. from-xml parses 22 MB in under a second, but every call that takes part of a parsed tree copies it, and a record's fields take dozens of such calls. Strings are cheap to pass. Measured on a real 22 MB list file (6,418 records) with a debug build: 55 s here, 91 to 109 s walking the tree, with identical entries. Whole-tree parsing is worth revisiting if passing a tree to a function stops copying it.

Functions

attr

fn (start-tag: Str?, name: Str): Str?

An attribute of a start tag string, decoded and trimmed, or null when missing or blank.

Example

::un::sanctions::xml/attr("<nameAlias wholeName=\\"Abu Ali\\" strong=\\"true\\">", "wholeName")  // "Abu Ali"

block

fn (fragment: Str?, tag: Str): Str?

The inner content of the first <tag> element, or null.

blocks

fn (fragment: Str?, tag: Str): Vec<Str>

The raw inner content of every <tag> element in fragment, in order (for nested structures).

clean

fn (s: Str): Str?

decode

fn (s: Str): Str

Decode XML character references in text with ::hot::xml: the five named entities and numeric references (&#233;, &#xA;). &amp;lt; stays the literal text &lt;. Text the parser refuses comes back as it was.

Example

::un::sanctions::xml/decode("Smith &amp; Sons &#233;t&#xE9;")  // "Smith & Sons été"

element-pattern

fn (tag: Str): Str

records

fn (doc: Str, tag: Str): Vec<Str>

Every <tag ...>...</tag> element of a document as a raw string. The tag must not nest inside itself; <tag/> is skipped.

Example

::un::sanctions::xml/records("<a><r>1</r><r>2</r></a>", "r")  // ["<r>1</r>", "<r>2</r>"]

start-tags

fn (fragment: Str?, tag: Str): Vec<Str>

Every start (or empty-element) tag <tag .../> in fragment, as raw strings, for reading attributes.

text

fn (fragment: Str?, tag: Str): Str?

The text of the first non-blank <tag> element, or null.

texts

fn (fragment: Str?, tag: Str): Vec<Str>

The text of every <tag> element in fragment: references decoded, whitespace runs collapsed, trimmed, blanks dropped. Use it for elements that hold text only.

Example

::un::sanctions::xml/texts("<n><v>A &amp; B</v><v/><v> C </v></n>", "v")  // ["A & B", "C"]