Generating the XSL
Now let's start looking at the process of
generating the XSL file.
Normal XML rules apply. The XML generated
by our ASP page must be well-formed, which means that case matters.
There are two approaches we could use in
order to meet our goal of creating the XSL:
We could build a string
We could create an instance of the XML
DOM, build a result tree and write out the XML as a string
Either approach is valid for our needs. The
first is simpler, and may be faster, but it is a limited solution. Should we
wish to revisit this application at some point in the future and extend its
functionality, our options would very restricted. Furthermore, we would be
limited to a linear building process, and it would be cumbersome to dynamically
modify any part of the result string that had already been built. It would be
difficult, for example, to add the capability of converting an attribute in the
source document to an element in the result. By taking the XML DOM approach,
all of the capabilities of the DOM are available to us, and we are not limited
to a sequential result. Plus, it's more of an XML-purist approach to the
problem.
So, we will need two instances of the XML
DOM. The first will contain the result tree we will be building, whose XML
source will ultimately become our return value, and
the second instance will host the XML translation document
that will drive the creation process.
As you're looking through the discussion of
how this ASP page works, you may find it useful to refer to the end result,
which is shown here as rendered in Internet Explorer 5, with the color coding
and indentation provided by IE's default internal style sheet.
If you download the source code for this
book and look at the interpreter.asp file
in your browser, this is what you should see.
As is always the case when we use an ASP to
generate XML, we need to set the response type:
<%
Response.ContentType =
"text/xml"
Response.Expires = 0
dim result
dim interpreter
dim oNodes
Next we will create our required two
instances of the XML DOM, and load in the interpreter.xml file (our mapping file):
Set result =
Server.CreateObject("Microsoft.XMLDOM")
Set interpreter =
Server.CreateObject("Microsoft.XMLDOM")
interpreter.async = false
'// load the definition
file
sSource =
Server.MapPath("interpreter.xml")
interpreter.load(sSource)
'// did the XML file load
OK?
If
interpreter.parseError.errorCode <> 0 Then
msg = "<msg><gen>Error loading
INTERPRETER data file.</gen>"
msg = msg & "<br>Description: " &
interpreter.parseError.reason &
"</br>"
msg = msg & "<br>Source text: " &
interpreter.parseError.srcText &
"</br></msg>"
Response.Write msg
End If
...
Note that as this code is running
server-side, we need to provide a physical path to the XML file that we are
loading, which is done by using Server.MapPath. We set the async
property of the interpreter file to false, as
we cannot process anything until this file is completely loaded. The async property of the result tree is irrelevant.
Next we extract all of the <xlat> nodes from our definition tree:
Set oNodes =
interpreter.documentElement.selectNodes("//xlat")
Now we are ready to start populating our
result tree. The first step is to add the node that identifies this XML
document as a style sheet:
'// add a node to the
result tree for the stylesheet declaration
Set oNewNode =
result.createNode("element","xsl:stylesheet", _
"http://www.w3.org/TR/WD-xsl")
result.appendChild(oNewNode)
Then we set the root element, the
stylesheet declaration, as follows:
'// the stylesheet
declaration is the outermost grouping tag. It is the root
'// element and parent to
all other elements
Set root =
result.documentElement
If you refer back to the IE5 view of the
XSL shown in the previous figure, you'll see that the next entry is <xsl:template match="/">, which we create as follows:
'// create the XSL node
Set oTemp =
result.createNode("element","xsl:template", _
"http://www.w3.org/TR/WD-xsl")
oTemp.setAttribute
"match", "/"
root. appendChild(oTemp)
Rather than hard-coding the names of the
root document and the item of the translation result (<shipments> and <shipment> in
our example), we extract those tag names from the definition file. This is
based on the assumption that the root element of the definition file will also
be the root element of the translation result:
'// name of the container
tag (eg: "shipments")
nodeName =
interpreter.documentElement.firstChild.nodeName
Set oContainer =
result.createNode("element", _
interpreter.documentElement.nodeName,"")
oTemp.appendChild(oContainer)
Note that we have created the oContainer variable, which represents the root element (shipments) of the translated result.
Now we need to add a line to our XSL result
tree that when processed will tell the XSL to iterate through all instances of
the children (shipment) of that
root. The following code will create an element containing <xsl:for-each select="//shipment"> and append it to the result tree:
Set oLoop = result.createNode("element","xsl:for-each",
_
"http://www.w3.org/TR/WD-xsl")
oLoop.setAttribute
"select", "//" & nodeName
oContainer.appendChild(oLoop)
Next we create an entry for the contained
item (that is <shipment>)
and append it to the result tree:
Set oLoopParent =
result.createNode("element",nodeName, "")
oLoop.appendChild(oLoopParent)
Now we get to the real work. In this
section of the code, we iterate through each of the <xlat> mapping elements. For each one, we create an element using the name attribute of the <out> element, and an <xsl:value-of> from the select attribute
from the <in> element.
These items are added to the result tree.
An example of one of these would be:
<shippedby><xsl:value-of select="carrier"/></shippedby>
For Each oNode in oNodes
Set oNewNode = result.createNode("element", _
oNode.selectSingleNode("out").getAttribute("name"),
"")
Set oSelect = result.createNode("element",_
"xsl:value-of","http://www.w3.org/TR/WD-xsl")
oSelect.setAttribute "select",oNode.selectSingleNode("in").getAttribute("name")
oNewNode.appendChild(oSelect)
oLoopParent.appendChild(oNewNode)
Next
Finally, we need to add the apply-templates node:
Set oNode = result.createNode("element","xsl:apply-templates",
"http://www.w3.org/TR/WD-xsl")
oTemp.appendChild(oNode)
We have now completely built our result
tree, and we return it to the calling application (which could be a browser, a
script, an XML file, etc):
Response.Write result.xml
%>
If you
are considering adapting this procedure for your own project, then the first
stage should be to create an XSL file that gets the data from the XML document.
This way you can get the display right before you start writing your
interpretation code.