Next: , Previous: Building a DOM from XML, Up: XML/XPath/XSL packages


8.2 Building XML

There's little reason to build an XML document if its not going to be processed by something down the road. Most XML tools require XML documents have a document root. A root is a tag inside which all other tags exist, or put another way, a single parent node from which all other nodes descend. In my case, a co-worker was attempting to use Sablot's sabcmd to transform the XML from my server into HTML. So start your document with the root ready to go:

     replyDoc := XML Document new.
     replyDoc addNode: (XML Element tag: 'response').

Before doing anything more complex, we can play with our new XML document. Assuming you're going to want to send the XML text to someone or write it to a file, you may first want to capture it in a string. Even if you don't want to first capture it into a string our example is going to:

     replyStream := String new writeStream.
     replyDoc printOn: replyStream.

If we examine'd the contents of our replyStream (replyStream contents) we'd see:

     <response/>

Which is what an empty tag looks like.

Let's add some text to our XML document now. Let's say we want it to look like:

     <response>Hello, world!</response>

Building this actually requires two nodes be added to a new XML document. The first node (or element) is named response. The second node adds text to the first:

     replyDoc := XML Document new.
     replyDoc addNode: (XML Element tag: response). "our root node"
     replyDoc root addNode: (XML Text text: 'Hello, world!').

Another way of writing it, and the way I've adopted in my code is to create the whole node before adding it. This is not just to reduce the appearance of assignments, but it suggests a template for cascading #addNode: messages to an element, which, if you're building any kind of nontrivial XML, you'll be doing a lot of:

     replyDoc := XML Document new.
     replyDoc addNode: (
         (XML Element tag: response)
             addNode: (XML Text text: 'Hello, world!')
     ).

Unless you're absolutely sure you'll never accidentally add text nodes that have an ampersand (&) in them, you'll need to escape it to get past XML parsers. The way I got around this was to escape them whenever I added text nodes. To make it easier, I (again) created a method in my objects' abstract superclass:

     asXMLElement: tag value: aValue
         | n |
     
         n := XML Element tag: tag.
         aValue isNil ifFalse: [
     	n addNode: (XML Text
     	    text: (aValue displayString copyReplaceAll: '&' with: '&amp;'))].
         ^n

Calls to self asXMLElement: 'sometagname' value: anInstanceVariable are littered throughout my code.

Adding attributes to documents is, thankfully, easier than accessing them. If we wanted to add an attribute to our document above we can do so with a single statement:

     replyDoc root addAttribute: (XML Attribute name: 'isExample' value: 'yes').

Now, our XML looks like:

     <response isExample="yes">Hello, world!</response>