Click here to Skip to main content
Click here to Skip to main content

Professional ASP XML - Chapter 10: Data Driven XSL

, 27 Nov 2000
Rate this:
Please Sign up or sign in to vote.
A case study which develops a real word application using ASP and XML
<!-- Article image -->
Sample Image
Title Professional ASP XML
Authors Mark Baartse, Richard Blair, Luca Bolognese, Dinar Dalvi, Steven Hahn, Corey Haines, Alex Homer, Bill Kropog, Brian Loesgen, Stephen Mohr, John Slater, Kevin Williams, Mario Zucca
Publisher Wrox
Published June 2000
ISBN 1861004028
Price US 49.99
Pages 850

Introduction

As business to business commerce and inter-enterprise communication via XML continues to explode, developers will continually be faced with situations where they must convert XML documents from an incoming structure to the structure they use internally. It is this translation requirement that we will use as the premise for this case study.

For an example situation where you might want to carry out such translations, consider converting an invoice sent by a field office into a format that is used by central office. The central office computer would have an application, or a web page, that could load the incoming invoice, extract only the information required by central office, and present it in a way that is relevant to the staff at central office, that is, with its data labels changed. Such a utility would save much time and trouble sifting through lots of data just to pick out the snippets of useful information.

In this case study we will show how to use ASP to create an XSL file dynamically, and we will data-drive the process using an XML file to define the contents of the XSL file. It is not the intent of this case-study to provide the ultimate translation tool. In fact, this is a simple translation tool that will only map elements to other elements, working within a fixed container/item structure. The mapping process that takes place could be achieved by a simple XSL transformation, which is effectively what we are doing, with the exception that the XSL is being generated on demand.

Meet the Puzzle Pieces

Our example consists of the following pieces:

File name

Description

incoming.xml

In-bound XML that needs to be translated

default.asp

Client-side test page that shows the translation at work

interpreter.asp

The ASP page that generates the XSL

interpreter.xml

The definition file for the translation. This is the data that defines the mapping between structures

The pieces are related to each other as shown below:

The Data

This case-study uses three XML files:

  • The incoming data (input, aka: "incoming.xml")

  • The translation definition file (process, aka: "interpreter.xml")

  • The translated data (output, seen from the test page)

Let's look at the structure of each of these:

Input XML

<?xml version='1.0'?>
<shipments>
   <shipment>
      <waybill>123WXZ99</waybill> 

      <carrier>Fedex</carrier> 
      <shipDate>20000110</shipDate>
      <boxes>3</boxes>
   </shipment>
   <shipment>
      <waybill>79843A</waybill> 
      <carrier>UPS</carrier> 
      <shipDate>20000110</shipDate>
      <boxes>2</boxes>
   </shipment>
   <shipment>
      <waybill>XXX12A</waybill> 
      <carrier>Fedex</carrier> 
      <shipDate>20000110</shipDate>
      <boxes>8</boxes>
   </shipment>
</shipments>

Output XML

<?xml version='1.0'?>
<shipments>
   <shipment>
      <waybill>123WXZ99</waybill> 
      <shippedby>Fedex</shippedby> 
      <shipped>20000110</shipped>
      <numberOfBoxes>3</numberOfBoxes>
   </shipment>
   <shipment>
      <waybill>79843A</waybill> 
      <shippedby>UPS</shippedby> 
      <shipped>20000110</shipped>
      <numberOfBoxes>2</numberOfBoxes>
   </shipment>
   <shipment>
      <waybill>XXX12A</waybill> 
      <shippedby>Fedex</shippedby> 
      <shipped>20000110</shipped>
      <numberOfBoxes>8</numberOfBoxes>
   </shipment>
</shipments>

The Translation Process

As you can see, the input and desired output files have a similar structure, but there are three differences:

  • "carrier" becomes "shippedby"

  • "shipDate" becomes "shipped"

  • "boxes" becomes "numberOfBoxes"

The required element mapping is defined in the following XML file:


<?xml version='1.0'?>
<shipments>
   <shipment>
      <xlat>
         <in name="waybill"/>
         <out name="waybill"/>
      </xlat>
      <xlat>
         <in name="carrier"/>
         <out name="shippedby"/>
      </xlat>
      <xlat>
         <in name="shipDate"/>
         <out name="shipped"/>
      </xlat>
      <xlat>
         <in name="boxes"/>
         <out name="numberOfBoxes"/>
      </xlat>
   </shipment>
</shipments>

As you can see, each <xlat> element contains an input element name and an output element name. This is the mapping we will use when we build our XSL. Note that the first of the <xlat> elements does something rather pointless – replacing "waybill" with "waybill". This has been included just to keep the code as simple as possible. You could put in some code to tell the interpreter not to process the <waybill> tags and only change the others.

We could easily create similar translation documents for any number of input documents. This would allow us to translate all input documents with the same ASP page.

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:templatematch="/">, 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,"")<P></P>
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-eachselect="//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.

The Test Page

Now let's see this all at work by looking at the test page, which is included in the source download as default.asp. We will use the JavaScript alert() function to display the XML because if we show it in the browser the tags will not be visible.

When it loads, the test page will invoke the init() function. It will create two instances of the XML DOM, one for the XML source document, and the second for the dynamic XSL file. The last thing it does is apply the XSL to the source XML, and saves the resulting string in the "processed" variable. This is our translated XML. The full code is shown here:

<html>
<head>
    <link REL="stylesheet" TYPE="text/css" HREF="list.css">
</head>
<body>
<hr color=red>

<button onclick='alert(source.xml);'>1. Show incoming.xml</button>
<button onclick='alert(dynstyle.xml);'>2. Show dynstyle.xsl</button>
<button onclick='alert(processed);' id=button1 name=button1> 3. <BR>         Show Translation</button><br>

<hr color=red>

<script FOR="window" EVENT="onload">
  init();
</script> 

<script>
var source;
var sourceName = "incoming.xml";
var dynstyle;
var dynstyleName = "interpreter.asp";
var processed = "";

function init(){
    // Do init stuff. Called by the parent frame.
    source = new ActiveXObject('Microsoft.XMLDOM');
    source.async = false;
    source.load(sourceName);     

    // did the XML file load OK?
    if (source.parseError.errorCode != 0){
        msg = 'Error loading SOURCE file.' 
        msg += '\nDescription: ' + source.parseError.reason   
        msg += '\nSource text: ' + source.parseError.srcText
    }
    root = source.documentElement;
    dynstyle = new ActiveXObject('Microsoft.XMLDOM');
    dynstyle.async = false;
    dynstyle.load(dynstyleName)
    // did the XML file load OK?
    if (dynstyle.parseError.errorCode != 0){
        msg = 'Error loading DYNSTYLE file.' 
        msg += '\nDescription: ' + dynstyle.parseError.reason   
        msg += '\nSource text: ' + dynstyle.parseError.srcText
    }
    processed = source.transformNode(dynstyle)
}
</script>
</body>
</html>

I have provided three buttons on the test page that show the three entities that make up this example:

  • Button 1 shows the original un-translated source document (incoming.xml)

  • Button 2 shows the dynamically-generated XSL returned as a result from interpreter.asp

  • Button 3 shows the translated version of the source document, after the style has been applied to it.

How it Works

The following screen shot shows the un-translated source document:


This screen shot shows our dynamically generated XSL:

This final screen shot shows the result of the translation:

Summary

In this chapter, we have seen a way to use ASP to generate an XSL file dynamically. We did this by using an ASP page to create an instance of the XML DOM and employing DOM methods to build the result tree. The information needed to build the result tree came from the contents of an XML file. A test web page then used the dynamic XSL to translate from one XML structure to another.

So now we have finished our discussion on styling XML for our web browsers. We will use XSL frequently as we progress through this book. In the next chapter we shall move on to storing and retrieving XML data from databases using ActiveX Data Objects (ADO).

Copyright © 2000 Wrox Press Ltd

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Wrox

United States United States
No Biography provided
Group type: Organisation

1 members


Comments and Discussions

 
GeneralXml Quik Buider Pinmembershak30-Jun-02 20:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 28 Nov 2000
Article Copyright 2000 by Wrox
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid