(text xml)

Reference

Overview

XML trees: names, nodes, documents, and the views over them.

(import (text xml) (text xml read) (text xml write))

(match (parse-xml "<feed><entry id='1'>hello</entry></feed>")
  ((Ok doc)
   (def entry (list-head (element-children (document-root doc))))
   (println (attr-ref entry (xml-name "id")))    ; (Some 1)
   (println (text-content entry))                ; hello
   (println (document-root doc)))                ; (feed (entry (@ (id "1")) "hello"))
  ((Err e) (println e)))

(parse-xml "<feed><entry></feed>")
;; (Err ...), which prints as
;; malformed at line 1, column 16: The 'entry' start tag on line 1 position 8 does not ...

XML is three modules. (text xml) is the tree: names, nodes, documents, and the views over them. (text xml read) reads XML, as a stream of signals, as a fold, as a tree of your own or as a Document. (text xml write) writes it.

Only System.Xml and System.Xml.Linq are underneath: XmlReader reads, XmlWriter writes and escapes, and XName is a name.

The tree is opaque. A Node is an element, a text, a comment or a processing instruction. Nodes are built by xml-element, xml-text, xml-comment and xml-pi, and taken apart by the views, which is what keeps every tree, whoever built it, to four rules:

The reader, the writer and the modules still to come use only what is exported here, so the views are the whole of the access there is.

Whitespace is never dropped by a node: a tree read with the defaults has every character of text its document had.

Deep trees. Nothing here recurses per level of nesting. A tree can be built by hand as deep as memory allows, and a stack overflow is not something .NET lets a program catch, so every walk (node=?, eq-hash, text-content, descendants, the printer and the writer) keeps a stack of its own.

Printing. ->str shows a node or a document the way SXML writes one. A name in a namespace is written in Clark notation.

(p (@ (class "note")) "hi " (b "!"))
({http://www.w3.org/2005/Atom}entry (@ (href "#a")))
(*COMMENT* " a comment ")
(*PI* xml-stylesheet "href='a.css'")
(*TOP* (*DOCTYPE* html) (html ...))

It is for the REPL and for debugging. Nothing promises yet that it reads back.

Not here yet. Pattern matching, typed paths, decoding into records, HTML and asynchronous reading are later modules, over the views here. HTML parsing, XML Schema, XSLT, XML 1.1 and DTD validation are not planned.

See also: xml-name, xml-element, element-view, node-case, xml-document

Reference

Types

Functions

Values

Types

XmlName

record

A name: a namespace URI and a local name, with no prefix.

A prefix belongs to the text a name was read from or is written to, not to the name. So writing a parsed document may choose other prefixes than the input had, and means the same.

Names are interned, so = on two of them is a reference comparison, and ->str writes Clark notation, {http://www.w3.org/2005/Atom}entry, or the local name alone for a name in no namespace.

See also: xml-name, xml-name-local, xml-name-ns

XmlPos

record

Where an element begins: a line and a column.

line
The line, from 1.
column
The column of the element's name, from 1: one past the <.

As .NET reports them. Only an element carries a position; a text, a comment or a processing instruction has none.

See also: element-pos

XmlAttrs

type alias

An element’s attributes: names and values, in document order.

See also: attr-ref, element-attrs

Doctype

record

A document’s DOCTYPE: its name and its public and system ids.

name
The name of the root element it declares.
public-id
The public id, if there is one.
system-id
The system id, if there is one.

It is kept so that a document can be written back with it. Nothing validates against it.

See also: document-doctype

Node

union

An element, a text, a comment or a processing instruction.

The type is opaque: nodes are built by the functions of this module and taken apart by its views, which keep every tree to the rules in the module’s documentation.

= on two nodes is node=?, and eq-hash agrees with it, so a node is a sound Map key. ->str prints it as SXML.

See also: xml-element, xml-text, xml-comment, xml-pi, element-view, node-case, node=?

Document

record

A root element, what comes before and after it, and the doctype.

The comments and processing instructions before the root are the prolog, those after it the epilog. = on two documents is document=?, and ->str prints one as SXML’s *TOP*.

See also: xml-document, document-root

Functions

xml-name

function

(: xml-name (-> string (#:ns string) XmlName))
(xml-name local #:ns "")

A name, in a namespace or in none.

local
The local name: an XML name with no colon in it.
#:ns
The namespace URI; "" is no namespace.
returns
The name.
raises ArgumentException
When local is not a valid local name, such as "a:b", "" or "1a".
(xml-name "p")
(xml-name "entry" #:ns "http://www.w3.org/2005/Atom")

A bad local name raises, as an index out of range does: it is a programming error, not the document’s.

See also: xml-name-local, xml-name-ns, xml-namespace

xml-name-local

function

(: xml-name-local (-> XmlName string))
(xml-name-local n)

A name’s local name.

n
The name.
returns
The local name, without namespace or prefix.

See also: xml-name-ns

xml-name-ns

function

(: xml-name-ns (-> XmlName string))
(xml-name-ns n)

A name’s namespace URI.

n
The name.
returns
The namespace URI, or "" when the name is in no namespace.

See also: xml-name-local

xml-element

function

(: xml-element (-> XmlName XmlAttrs (List Node) (#:pos (Option XmlPos)) Node))
(xml-element name attrs children #:pos None)

An element.

name
The element's name.
attrs
Its attributes, in document order.
children
Its children, in document order.
#:pos
Where it was read from; None for an element built by hand.
returns
The element, with adjacent text children merged into one and empty ones dropped.
raises ArgumentException
When an attribute is given twice, or one is a namespace declaration (xmlns or xmlns:p). The writer declares namespaces itself.
(xml-element (xml-name "p")
             (list (Tuple (xml-name "class") "note"))
             (list (xml-text "hi ") (xml-element (xml-name "b") Nil (list (xml-text "!")))))

See also: xml-text, element-view

xml-text

function

(: xml-text (-> string Node))
(xml-text s)

A text node.

s
The text, unescaped.
returns
The node. An empty one is allowed on its own; xml-element drops it.

See also: xml-element, text-value

xml-comment

function

(: xml-comment (-> string Node))
(xml-comment s)

A comment.

s
What is between the comment's delimiters.
returns
The node.

Not checked here: a comment containing -- or ending in - is refused when it is written.

See also: comment-value

xml-pi

function

(: xml-pi (-> string string Node))
(xml-pi target data)

A processing instruction.

target
Its target, the name after the <?.
data
Its data, the rest.
returns
The node.

Not checked here, but when it is written, as a comment is.

See also: pi-target, pi-data

element?

function

(: element? (-> Node bool))
(element? n)

Whether a node is an element.

n
The node.
returns
#t for an element.

See also: text?, comment?, pi?, element-view

text?

function

(: text? (-> Node bool))
(text? n)

Whether a node is a text.

n
The node.
returns
#t for a text.

See also: element?, text-value

comment?

function

(: comment? (-> Node bool))
(comment? n)

Whether a node is a comment.

n
The node.
returns
#t for a comment.

See also: element?, comment-value

pi?

function

(: pi? (-> Node bool))
(pi? n)

Whether a node is a processing instruction.

n
The node.
returns
#t for a processing instruction.

See also: element?, pi-target, pi-data

element-name

function

(: element-name (-> Node XmlName))
(element-name n)

An element’s name.

n
An element.
returns
Its name.
raises ArgumentException
When n is not an element.

See also: element-view

element-attrs

function

(: element-attrs (-> Node XmlAttrs))
(element-attrs n)

An element’s attributes.

n
An element.
returns
Its attributes, in document order.
raises ArgumentException
When n is not an element.

See also: attr-ref, element-view

element-children

function

(: element-children (-> Node (List Node)))
(element-children n)

An element’s children.

n
An element.
returns
Its children, in document order.
raises ArgumentException
When n is not an element.

See also: element-children-only, element-view

element-pos

function

(: element-pos (-> Node (Option XmlPos)))
(element-pos n)

Where an element was read from.

n
An element.
returns
Its position, or None for an element built without one.
raises ArgumentException
When n is not an element.

See also: XmlPos

element-view

function

(: element-view (-> Node (Option (Tuple XmlName XmlAttrs (List Node)))))
(element-view n)

An element’s name, attributes and children, in one call.

n
Any node.
returns
(Some (Tuple name attrs children)) for an element, None for any other node.
(match n
  ((:view element-view (Some (Tuple name attrs kids))) (list-length kids))
  (_ 0))

It allocates nothing, since Option is a struct and Tuple a value tuple, which makes it the view a pattern can be compiled into.

See also: element?, node-case

text-value

function

(: text-value (-> Node string))
(text-value n)

A text node’s text.

n
A text.
returns
The text.
raises ArgumentException
When n is not a text.

See also: text-content

comment-value

function

(: comment-value (-> Node string))
(comment-value n)

A comment’s text.

n
A comment.
returns
What is between its delimiters.
raises ArgumentException
When n is not a comment.

pi-target

function

(: pi-target (-> Node string))
(pi-target n)

A processing instruction’s target.

n
A processing instruction.
returns
Its target.
raises ArgumentException
When n is not a processing instruction.

See also: pi-data

pi-data

function

(: pi-data (-> Node string))
(pi-data n)

A processing instruction’s data.

n
A processing instruction.
returns
Its data.
raises ArgumentException
When n is not a processing instruction.

See also: pi-target

node-case

function

(: node-case (-> Node (#:else (-> Node %r)) (#:element (-> XmlName XmlAttrs (List Node) (Option XmlPos) %r)) (#:text (-> string %r)) (#:comment (-> string %r)) (#:pi (-> string string %r)) %r))
(node-case n
                  #:else (fun (m) (no-clause m))
                  #:element (fun (name attrs kids pos) (else n))
                  #:text (fun (s) (else n))
                  #:comment (fun (s) (else n))
                  #:pi (fun (target data) (else n)))

Calls the handler for the kind of node it is given.

n
The node.
#:element
Called with an element's name, attributes, children and position.
#:text
Called with a text's string.
#:comment
Called with a comment's string.
#:pi
Called with a processing instruction's target and data.
#:else
Called with the node itself when its kind has no handler.
returns
What the handler answers.
raises ArgumentException
When the node's kind has no handler and there is no #:else.
(node-case n
  #:element (fun (name attrs kids pos) (xml-name-local name))
  #:text (fun (s) s)
  #:else (fun (other) ""))

A call that writes #:else keeps working if a kind of node is ever added.

See also: element-view

attr-ref

function

(: attr-ref (-> Node XmlName (Option string)))
(attr-ref n name)

The value of an element’s attribute.

n
The node.
name
The attribute's name.
returns
Some value, or None when the element has no such attribute, or n is not an element.

See also: element-attrs

element-children-only

function

(: element-children-only (-> Node (List Node)))
(element-children-only n)

The children of an element that are elements.

n
The node.
returns
The element children, in document order; Nil for a node that is not an element.

See also: element-children

text-content

function

(: text-content (-> Node string))
(text-content n)

All the text below a node, joined.

n
The node.
returns
Every text below n joined in document order; a text's own value; "" for a comment or a processing instruction.

See also: text-value, descendants

descendants

function

(: descendants (-> Node (Seq Node)))
(descendants n)

Every node below a node, in document order.

n
The node.
returns
A lazy Seq of the nodes below n, not n itself. Empty for a node that is not an element.

See also: element-children

node=?

function

(: node=? (-> Node Node bool))
(node=? a b)

Whether two nodes are the same tree.

a
A node.
b
Another.
returns
#t when they are the same tree, positions and the order of attributes aside.

This is what = on nodes is, and eq-hash agrees with it.

See also: document=?

xml-document

function

(: xml-document (-> Node (#:doctype (Option Doctype)) (#:prolog (List Node)) (#:epilog (List Node)) Document))
(xml-document root #:doctype None #:prolog Nil #:epilog Nil)

A document.

root
The root element.
#:doctype
The doctype, if it has one.
#:prolog
The comments and processing instructions before the root.
#:epilog
The comments and processing instructions after it.
returns
The document.
raises ArgumentException
When root is not an element, or the prolog or epilog holds anything but comments and processing instructions.

See also: document-root, Doctype

document-root

function

(: document-root (-> Document Node))
(document-root d)

A document’s root element.

d
The document.
returns
The root, an element.

See also: document-prolog, document-epilog

document-doctype

function

(: document-doctype (-> Document (Option Doctype)))
(document-doctype d)

A document’s doctype.

d
The document.
returns
The doctype, or None when it has none.

See also: Doctype

document-prolog

function

(: document-prolog (-> Document (List Node)))
(document-prolog d)

The comments and processing instructions before a document’s root.

d
The document.
returns
Them, in document order.

See also: document-epilog

document-epilog

function

(: document-epilog (-> Document (List Node)))
(document-epilog d)

The comments and processing instructions after a document’s root.

d
The document.
returns
Them, in document order.

See also: document-prolog

document=?

function

(: document=? (-> Document Document bool))
(document=? a b)

Whether two documents are the same.

a
A document.
b
Another.
returns
#t when their doctypes are equal and their nodes are, as node=? compares them.

This is what = on documents is.

See also: node=?

Values

xml-namespace

value

(: xml-namespace string)

The namespace of xml:lang and xml:space.

http://www.w3.org/XML/1998/namespace. The writer gives a name in it the prefix xml, which is never declared.

See also: xml-name