s c h e m a t i c s : c o o k b o o k

/ Cookbook.XMLRecipeSimpleChanges

This Web

TOC (with recipes)

Other Webs



Schematics Home
Sourceforge Page
Original Cookbook

Scheme Links

Scheme FAQ
Scheme Cross Reference
Scheme48 SCM
MIT Scheme scsh
JScheme Kawa
Chicken Guile
Bigloo Tiny
Gambit LispMe

Lambda the Ultimate

Making Simple Changes to Elements or Text


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 (lib "myenv.ss" "ssax")
           (lib "ssax.ss" "ssax")
           (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'/>
(define flight-list (SSAX:XML->SXML (open-input-string sample-doc) '()))

Now, you can use the following definitions

(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))

; find an attribute with the name attr-symb in n's attribute list
(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)

; return the element children of n
(define (element-axis n)
  (if (and (pair? n)
           (eq? (node-name (first-child n)) '@))
      (cddr n)
      (cdr n)))

; appends a child to the end of the list of child elements of n.
(define (append-child n newChild)
  (append! (element-axis n) (list newChild)))

; adds an attribute in list form ('attrName "attrValue") to node n
(define (add-attribute n attr)
  (aif attr-list (attributes n)
       (append! attr-list attr)
       ((set-cdr! n (cons (list '@ attr) (cdr n))))))

; sets the attribute with name eq? attrSymb to attrValue
(define (set-attribute n attrSymb attrValue)
  (aif attrib (find-attribute n attrSymb)
       (set-car! (cdr attrib) attrValue)

; finds the (immediate) equal? child in n
(define (find-child n child)
  (list:find (lambda (n) (equal? n child)) (children n)))

; replaces the equal? child of n with new-child
(define (replace-child n child new-child)
  (set-cdr! n (map 
               (lambda (node)
                 (if (equal? node child)
               (children n))))

; works like the InnerText property on the MSXML DOM, all text
; children (not traversing the attribute axis)
(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)))

-- HectorEGomezMorales - 05 May 2004

-- GordonWeakliem - 03 Aug 2004

TopicType: Recipe
ParentTopic: XmlRecipes
TopicOrder: 040

Copyright © 2004 by the contributing authors. All material on the Schematics Cookbook web site is the property of the contributing authors.
The copyright for certain compilations of material taken from this website is held by the SchematicsEditorsGroup - see ContributorAgreement & LGPL.
Other than such compilations, this material can be redistributed and/or modified under the terms of the GNU Lesser General Public License (LGPL), version 2.1, as published by the Free Software Foundation.
Ideas, requests, problems regarding Schematics Cookbook? Send feedback.
/ You are Main.guest