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

/ Cookbook.XMLRecipeSimpleChanges

This Web


WebHome 
WebChanges 
TOC (with recipes)
NewRecipe 
WebTopicList 
WebStatistics 

Other Webs


Chicken
Cookbook
Erlang
Know
Main
Plugins
Sandbox
Scm
TWiki  

Schematics


Schematics Home
Sourceforge Page
SchemeWiki.org
Original Cookbook
RSS

Scheme Links


Schemers.org
Scheme FAQ
R5RS
SRFIs
Scheme Cross Reference
PLT Scheme SISC
Scheme48 SCM
MIT Scheme scsh
JScheme Kawa
Chicken Guile
Bigloo Tiny
Gambit LispMe
GaucheChez

Lambda the Ultimate
TWiki.org

Making Simple Changes to Elements or Text

Problem

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>

Solution

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

;; Anamorphic if: if the test succeeds, bind the value
;; to var and make it available to the true expression body.
(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))

; 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)
        #f)))

; 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)
       #f))

; 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)
                     new-child
                     node))
               (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)))
          ""
          n))

-- Note:

Maybe I'm just confused, but I think "sxpath" makes a lot of the above a lot simpler.

-- 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.

CookbookForm
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