You want to filter some XML. For example, you want to make substitutions in the body of a document, or add a genre to every song in described described in an XML document, or you want to change:
<book id="1"> to <book><id>1</id></book>
The nifty thing about SXML is that the representation is simply a list, so the rules for altering lists apply equally well here. Here's a quick guide to manipulating tree structures:
1
car gives you the name of a node
n.
1
cdr selects the children of a node in the tree. You can
(define children cdr) to make that more obvious.
1 Based on the last rule, the
cadr of a node
n is is the first child of
n.
1 If an element has attributes, the first child will be
'@, the list of attributes of a node
n will then be
(cdr (children n)).
1 When dealing with attributes, the value of the attribute is the
cadr of the list representing that attribute. Therefore,
(set-car! (cdr n) "some value") will change the value of an attribute
n in place.
1 Given a list of children of a node
n,
append! will append a child to the end of the list of children. Rember that the child you're appending has to be a list, if you're appending an entire subtree, you have to wrap it in a list.
1 Given a a node
n, you can
set-cdr! to the node to replace the children of a node. If you
cons a new node to the
cdr or
cddr of a node (depending on whether it has attributes), that's the same as adding a child to the beginning of the list of children.
These rules work a bit differently than DOM: in DOM, children of an element are everything
but attributes, attributes have their own access methods. Here, we're treating attributes more or less on equal footing as other children. However, this can get you into a little trouble, so later we'll need to take this into account.
Let's try some examples, starting with a sample document:
(require (planet "myenv.ss" ("lizorkin" "ssax.plt" 1 3))
(planet "ssax.ss" ("lizorkin" "ssax.plt" 1 3))
(prefix list: (lib "1.ss" "srfi")))
(define sample-doc "<flights lastUpdate='2004-08-02T17:09:34-07:00'>
<flight vendor='AA' number='1431' departs='2004-08-22T08:30'
boardpoint='DEN' offpoint='DCA'/>
<flight vendor='UA' number='156' departs='2004-08-22T010:20'
boardpoint='DEN' offpoint='DCA'/>
<flight vendor='F9' number='34' departs='2004-08-22T12:35'
boardpoint='DEN' offpoint='DCA'/>
<flight vendor='AA' number='1034' departs='2004-08-22T14:45'
boardpoint='DEN' offpoint='DCA'/>
<flight vendor='UA' number='210' departs='2004-08-22T17:00'
boardpoint='DEN' offpoint='DCA'/>
</flights>")
(define flight-list (ssax:xml->sxml (open-input-string sample-doc) '()))
Now, you can use the following definitions
(define-syntax aif
(syntax-rules ()
((_ var test-expr true-expr false-expr)
(cond
[test-expr => (lambda (var) true-expr)]
[else
false-expr]))))
(define children cdr)
(define (first-child n) (cadr n))
(define (node-name n) (car n))
(define (attributes n)
(if (and (pair? n)
(eq? (node-name (first-child n)) '@))
(cdr (first-child n))
#f))
(define (find-attribute n attr-symb)
(let ((attrs (attributes n)))
(if n
(list:find (lambda (n) (and (pair? n) (eq? attr-symb (car n)))) attrs)
#f)))
(define (element-axis n)
(if (and (pair? n)
(eq? (node-name (first-child n)) '@))
(cddr n)
(cdr n)))
(define (append-child n newChild)
(append! (element-axis n) (list newChild)))
(define (add-attribute n attr)
(aif attr-list (attributes n)
(append! attr-list attr)
((set-cdr! n (cons (list '@ attr) (cdr n))))))
(define (set-attribute n attrSymb attrValue)
(aif attrib (find-attribute n attrSymb)
(set-car! (cdr attrib) attrValue)
#f))
(define (find-child n child)
(list:find (lambda (n) (equal? n child)) (children n)))
(define (replace-child n child new-child)
(set-cdr! n (map
(lambda (node)
(if (equal? node child)
new-child
node))
(children n))))
(define (inner-text n)
(fold (lambda (n a)
(cond ((string? n)
(string-append a n))
((and (pair? n)
(not (eq? '@ (car n))))
(string-append a (inner-text n)))
(else a)))
""
n))
--
HectorEGomezMorales - 05 May 2004
--
GordonWeakliem - 03 Aug 2004
--
DannyYoo - 08 Oct 2007
Adjusted the example to use
PLaneT's version of the
SSAX library. Also included a definition for
AIF.