On this page:
1.1 Constructing XML fragments
xml
XML
xml*
XML*
xml-attrs
XML-ATTRS
opt-xml
OPT-XML
opt-xml-attr
OPT-XML-ATTR
1.1.1 XML expression syntax
1.1.2 Custom XML syntax
define-xml-syntax
1.2 Rendering XML and sending XML responses
1.2.1 Rendering XML in string form
xml->string
1.2.2 Sending HTTP responses with XML and XHTML content
make-xml-response
make-html-response
1.3 XML utilities
xml-quotable?
xml+ quotable?
xml-quote
xml-empty?
xhtml-1.0-strict-doctype
xhtml-1.0-transitional-doctype
xhtml-1.0-frameset-doctype
alist->attributes
1.4 XML abstract syntax tree
xml
atom
block
entity
comment
cdata
pi
raw
element
attribute
Version: 4.2.0.2

1 XML

The XML language in mirrors allows the programatic assembly of syntactically valid XML and XHTML, without the problems of traditional list-representations:

Constructing XML fragments describes the syntax for creating blocks of XML, Rendering XML and sending XML responses describes how to send XML responses in the PLT web server, XML utilities describes some useful blocks of XML such as a DOCTYPES, and XML abstract syntax tree describes the underlying AST representation.

1.1 Constructing XML fragments

 (require (planet untyped/mirrors/xml/syntax))

Mirrors provides the following macros for creating blocks of XML:

(xml xml-expr ...)

Builds a block of XML, optimising any immutable blocks of markup into !raw blocks to reduce memory consumption and rendering time. See XML expression syntax for the syntax of xml-expr.

Examples:

  > (let ([content "content"])
      (pretty-print (xml (span (@ [title "title"]) ,content))))

  #s((block xml 0)

     (#s((raw xml 0)

         ("<span title=\"title\">"))

      #s((atom xml 0) "content")

      #s((raw xml 0) ("</span>"))))

To prevent double-quoting errors, it is a syntax error to use the following Mirrors quote forms within an xml block: xml, xml*, xml-attrs, xml-attrs*, opt-xml, opt-xml-attr, js, opt-js:

  > (xml (xml))

  eval:3:0: mirrors/xml: cannot use this identifier here

  (possible double quoting error): switch the surrounding

  quote macro to "XML" or another uppercase equivalent at:

  xml in: (xml)

You can get around this restriction using the uppercase form, XML, described below.

(XML xml-expr ...)

Like xml, but permits the use of the lowercase Mirrors quote forms as xml-exprs:

  > (XML (xml)) ; '<xml/>' tag

  #s((raw xml 0) ("<xml />"))

It is a syntax error to use the following identifiers in expression position within an XML block: XML, XML*, XML-ATTRS, XML-ATTRS*, OPT-XML, OPT-XML-ATTR, JS, OPT-JS:

  > (XML (XML))

  eval:5:0: mirrors/xml: cannot use this identifier here

  (possible double quoting error): switch the surrounding

  quote macro to "xml" or another lowercase equivalent at:

  XML in: (XML)

(xml* xml-expr ...)

Like xml, but skips the optimisation of static markup, leaving the element structure intact for later traversal and manipulation:

Examples:

  > (let ([content "content"])
      (pretty-print (xml* (span (@ [title "title"]) ,content))))

  #s((element xml 0)

     span

     (#s(attribute

         title

         #s((atom xml 0) "title")))

     #s((atom xml 0) "content"))

(XML* xml-expr ...)

Like xml*, but follows the same double-quoting rules as XML.

(xml-attrs attr-expr ...)

Builds a list of attribute structures. Useful in conjunction with the unquote-splicing form of attr-expr:

  > (let ([attrs (xml-attrs [title "title"] [href "href"])])
      (xml->string (xml (a (@ ,@attrs) "text"))))

  "<a title=\"title\" href=\"href\">text</a>"

(XML-ATTRS attr-expr ...)

Like xml-attrs, but follows the same double-quoting rules as XML.

(opt-xml boolean-expr xml-expr ...)

Syntactic shorthand for:

  (if boolean-expr
      (xml xml-expr ...)
      (xml))

Examples:

  > (xml->string (opt-xml #t (span "This span will be printed....")))

  "<span>This span will be printed....</span>"

  > (xml->string (opt-xml #f (span "...but this span won't.")))

  ""

(OPT-XML boolean-expr xml-expr ...)

Like opt-xml, but follows the same double-quoting rules as XML.

(opt-xml-attr id)
(opt-xml-attr boolean-expr id)
(opt-xml-attr boolean-expr id expr)

Syntactic shorthand for including an optional XML attribute. The full three-argument form expands to:

  (if boolean-expr
      (xml-attrs [id ,expr])
      (xml-attrs))

The two- and one-argument forms are specialisations of the above. The two-argument form expands to:

  (if boolean-expr
      (xml-attrs [id ,id])
      (xml-attrs))

and the one-argument form expands to:

  (if id
      (xml-attrs [id ,id])
      (xml-attrs))

Examples:

  > (xml->string
     (xml ,@(for/list ([item (in-range 1 4)])
              (define class (if (even? item) "even" #f))
              (xml (li (@ ,(opt-xml-attr class)) "Item " ,item)))))

  "<li>Item 1</li><li class=\"even\">Item 2</li><li>Item 3</li>"

(OPT-XML-ATTR id)
(OPT-XML-ATTR boolean-expr id)
(OPT-XML-ATTR boolean-expr id expr)

Like opt-xml-attr, but follows the same double-quoting rules as XML.

1.1.1 XML expression syntax

The forms above use the same XML syntax, inspired by the syntax of Neil van Dyke’s HtmlPrag package:

  xml-expr = quotable-value
  | (custom-syntax-id xml-expr ...)
  | (tag xml-expr ...)
  | (tag (@ attr-expr ...) xml-expr ...)
  | (& code-expr)
  | (!comment value-expr)
  | (!cdata value-expr)
  | (!pi value-expr)
  | (!raw value-expr)
  | (unquote xml)
  | (unquote-splicing (listof xml))
     
  custom-syntax-id = id ; bound with define-xml-syntax
     
  tag = id
     
  code-expr = id
  | integer
  | (unquote (U symbol integer))
     
  value-expr = quotable-value
  | (unquote quotable-value)
     
  attr-expr = (id quotable-value)
  | (id (unquote quotable-value))
  | (unquote attribute)
  | (unquote-splicing (listof attribute))

1.1.2 Custom XML syntax

You can extend the XML language by introducing new syntax forms:

(define-xml-syntax (id arg ...) xml-expr)
(define-xml-syntax id xml-transformer)
(define-xml-syntax id xml-transformer expr-transformer)

Defines a custom syntactic form for use in xml and xml* blocks.

The first form above behaves like define-syntax-rule. xml-expr should be a regular xml block.

Examples:

  > (define-xml-syntax (!linkto url)
      (xml (a (@ [href url]) url)))
  > (xml->string (xml (!linkto "http://www.plt-scheme.org")))

  "<a href=\"http://www.plt-scheme.org\">http://www.plt-scheme.org</a>"

  > (!linkto "http://www.plt-scheme.org")

  eval:13:0: !linkto: must be used as an xml expression in:

  (!linkto "http://www.plt-scheme.org")

The second and third forms behave like define-match-expander. xml-transformer is a syntax transformer procedure used during XML expansion, which accepts an XML form as an input and returns a complete xml or xml* block representing the expansion. expr-transformer is a transformer procedure that is used in regular Scheme expansion. When xml-transformer is omitted, use of the syntax form outside of a XML block results in a syntax error.

Examples:

  > (define-xml-syntax !linkto
      (lambda (stx)
        (syntax-case stx ()
          [(_ url) #'(xml (a (@ [href url]) url))]))
      (lambda (stx)
        (syntax-case stx ()
          [(_ url) #'url])))
  > (xml->string (xml (!linkto "http://www.plt-scheme.org")))

  "<a href=\"http://www.plt-scheme.org\">http://www.plt-scheme.org</a>"

  > (!linkto "http://www.plt-scheme.org")

  "http://www.plt-scheme.org"

1.2 Rendering XML and sending XML responses

 (require (planet untyped/mirrors/xml/xml))

1.2.1 Rendering XML in string form

(xml->string val)  string?
  val : xml?

Renders an XML item as a compact string, with no line breaks and no indentation.

Mirrors does not contain a procedure for rendering XML as a multi-line string. This is largely because such a facility would be at odds with the pre-rendering behaviour of the xml macro. If you want to debug the XML or XHTML output of our web application, you may want to try one of the variety of HTML and XML prettification add-ons that are available for Firefox.

1.2.2 Sending HTTP responses with XML and XHTML content

The PLT web server has built-in support for the “xexpr” representation of the PLT xml package. This lets you write request handling procedures in short-hand:

  #lang scheme/base
  
  (require web-server/servlet
           xml/xml)
  
  ; request -> xexpr
  (define (start initial-request)
    '(head (body "Hello world.")))

instead of manually creating an HTTP response using make-respone/full:

  #lang scheme/base
  
  (require web-server/servlet
           xml/xml)
  
  ; request -> response
  (define (start initial-request)
    (make-response/full
     200
     "Okay"
     (current-seconds)
     #"text/html; charset=utf-8"
     null
     (list (xexpr->string '(head (body "Hello world."))))))

Naturally, the PLT web server does not have built-in support for Mirrors XML expressions. Mirrors provides a couple of useful procedures to help you send responses:

(make-xml-response [#:code code    
  #:message message    
  #:seconds seconds    
  #:mime-type mime-type    
  #:headers headers]    
  content)  response
  code : integer = 200
  message : string = "OK"
  seconds : integer = (current-seconds)
  mime-type : (U string bytes) = #"text/xml; charset=utf-8"
  headers : (alistof symbol string) = no-cache-http-headers
  content : xml

Takes an xml expression argument and wraps it in an HTTP response object that can be used with the PLT web server (including procedures such as send/suspend and send/suspend/dispatch). The keyword arguments correspond to the first five arguments of make-response/full.

(make-html-response [#:code code    
  #:message message    
  #:seconds seconds    
  #:mime-type mime-type    
  #:headers headers]    
  content)  response
  code : integer = 200
  message : string = "OK"
  seconds : integer = (current-seconds)
  mime-type : (U string bytes) = #"text/html; charset=utf-8"
  headers : (alistof symbol string) = no-cache-http-headers
  content : xml

Like make-xml-response but with a default MIME type of "text/html".

1.3 XML utilities

(xml-quotable? val)  boolean?
  val : any

Returns #t if val is a Scheme value that can be converted to XML by Mirrors.

Examples:

  > (xml-quotable? 123)

  #t

  > (xml-quotable? (xml 123))

  #f

  > (xml-quotable? (box 123))

  #f

(xml+quotable? val)  boolean?
  val : any

Returns #t if val is an xml fragment or a Scheme value that can be converted to XML by Mirrors.

Examples:

  > (xml+quotable? 123)

  #t

  > (xml+quotable? (xml 123))

  #t

  > (xml+quotable? (box 123))

  #f

(xml-quote val)  xml?
  val : (U xml? quotable-value?)

Turns quotable values into xml fragments. Passes existing xml fragments straight through.

Examples:

  > (equal? (xml-quote 123) (xml ,123))

  #t

  > (equal? (xml-quote (xml 123)) (xml 123))

  #t

(xml-empty? val)  boolean?
  val : xml?

Returns #t if val is an XML fragment that will produce no renderable XML source.

Examples:

  > (xml-empty? (xml))

  #t

  > (xml-empty? (xml "Hi"))

  #f

  > (xml-empty? (xml ,(xml)))

  #t

  ; Odd case: #f renders as an empty string:
  > (xml-empty? (xml #f))

  #t

 (require (planet untyped/mirrors/xml/util))

The following XML expressions are defined for convenience:

xhtml-1.0-strict-doctype : xml

The HTML 1.0 transitional DOCTYPE, complete with newline character:

  > (display (xml->string xhtml-1.0-strict-doctype))

  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

xhtml-1.0-transitional-doctype : xml

The HTML 1.0 transitional DOCTYPE, complete with newline character:

  > (display (xml->string xhtml-1.0-transitional-doctype))

  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

xhtml-1.0-frameset-doctype : xml

The HTML 1.0 frameset DOCTYPE, complete with newline character:

  > (display (xml->string xhtml-1.0-frameset-doctype))

  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">

(alist->attributes [alistof])  (listof attribute?)
  alistof : symbol? = quotable-value?

Converts an association list to a list of attributes.

1.4 XML abstract syntax tree

 (require (planet untyped/mirrors/xml/struct))

The xml and xml* macros expand into trees of structures of the following types. You shouldn’t normally have to manipulate these types explicitly unless you want to build or manipulate XML without the syntax layer:

(struct xml ())

XML data. This is an abstract type: concrete subtypes exist for the XML constructs such as elements, entities, CDATA sections and comments (see below).

(struct (atom xml) (data))
  data : quotable-value

An atomic datum. The rendered form of an atom is simply its data, appropriately quoted so it cannot be considered markup:

  > (xml->string (make-atom 12345))

  "12345"

  > (xml->string (make-atom #t))

  "yes"

  > (xml->string (make-atom "Apples & pears."))

  "Apples &amp; pears."

quotable-values include booleans, numbers, strings, symbols, byte strings, SRFI 19 times (UTC and TAI types only) and URLs from the net/url library.

(struct (block xml) (children))
  children : (listof xml)

A list of XML nodes. Blocks can be arbitrarily nested, and produce no markup other than that of their children:

  > (xml->string (make-block
                  (list (make-atom "Apples ")
                        (make-atom "&")
                        (make-atom " pears."))))

  "Apples &amp; pears."

(struct (entity xml) (code))
  code : (U integer symbol)

A character entity with a numeric or symbolic code:

  > (xml->string (make-entity 12345))

  "&#12345;"

  > (xml->string (make-entity 'nbsp))

  "&nbsp;"

(struct (comment xml) (data))
  data : quotable-value

A comment:

  > (xml->string (make-comment "Apples & pears"))

  "<!--Apples & pears-->"

(struct (cdata xml) (data))
  data : quotable-value

A CDATA (unparsed character data) section:

  > (xml->string (make-cdata "Apples & pears"))

  "<![CDATA[Apples & pears]]>"

(struct (pi xml) (data))
  data : quotable-value

A processing instruction:

  > (xml->string (make-pi "Apples & pears"))

  "<?Apples & pears?>"

(struct (raw xml) (data))
  data : quotable-value

A block of raw XML, equivalent to a CDATA section without the opening and closing markup:

  > (xml->string (make-raw "<ul><li>Apples</li><li>Pears</li></ul>"))

  "<ul><li>Apples</li><li>Pears</li></ul>"

(struct (element xml) (tag attributes child))
  tag : symbol
  attributes : (listof attribute)
  child : xml

An XML element:

  > (xml->string (make-element
                  'span
                  (list (make-attribute 'title "attribute"))
                  (make-atom "child element")))

  "<span title=\"attribute\">child element</span>"

(struct attribute (name value))
  name : symbol
  value : quotable-value

An element attribute.