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:
- no two text children side by side:
xml-elementmerges them - no empty text child:
xml-elementdrops it - no attribute twice:
xml-elementraises - no namespace declaration among the attributes:
xml-elementraises, and the reader never gives one
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
attr-refcomment-valuecomment?descendantsdocument-doctypedocument-epilogdocument-prologdocument-rootdocument=?element-attrselement-childrenelement-children-onlyelement-nameelement-poselement-viewelement?node-casenode=?pi-datapi-targetpi?text-contenttext-valuetext?xml-commentxml-documentxml-elementxml-namexml-name-localxml-name-nsxml-pixml-text
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.
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.
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.
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