|
|
||||||
|
|
![]() |
|
![]() |
|
||
|
|
||||||
Generating Style Sheets Dynamically
By Michael Floyd
When I envision an XML development team, I picture several people with different skill sets. For example, an information architect, or what we may once have called a "knowledge engineer," possesses the background and aptitude for designing and implementing document structure. That's the DTD or schema developer. Distinctly different is the software engineer who uses APIs like SAX or the Document Object Model (DOM) to access and process documents programmatically. And then there's a transformation specialist. This is, in my mind, someone who uses the XSL Transformation language (XSLT) to convert, translate, and transform XML into other formats, often for the purpose of data exchange with other systems.
The problem arises because this last person, or more importantly his or her skill set, isn't very well understood. Most managers push these responsibilities onto their user-interface designers. After all, these are style sheets, right? Well, yes we call them style sheets, but they bear little resemblance to the Cascading Style Sheets (CSS) that many managers associate with them. In fact, XSLT is a verbose language with far more complexity than the relatively straightforward DOM. Though not a programming language, XSLT includes features that let you walk document trees, create, access, and modify individual nodes, perform iterative and conditional processing, and much more. Thus, XSLT can be equated to a declarative programming language that uses XML syntax.
It's this misconception that XSL is a formatting mechanism that causes XSL in general, and XSLT in particular, to be overlooked. Managers, often thinking XSL is for formatting, decide XSL shouldn't be used because their applications aren't displaying XMLthey're using it for data exchange. In the end, you either wind up with resistant designers who are overwhelmed by the syntax, or programmers who won't touch XSLT because "it's not a programming language."
You can solve this problem by dynamically generating XSLT style sheets using the DOM. One benefit of this approach is that generating style sheets reduces the number of static style sheets you must write, which often exceeds 100. Also, you can use dynamic style-sheet generation to convert older style sheets into the newer XSL standards. Even if you're not developing style sheets, the techniques presented here will be instructive for anyone creating complex XML documents using the DOM. By the way, this article assumes you're familiar with both XSLT and the DOM. In particular, I won't be covering XSLT coding. However, you can get those details from my previous columns in the Web Techniques online archives or at the W3C User Interface Domain (www.w3.org/Style/XSL).
How It Works
Because a style sheet is a well-formed (and in fact, valid) document, you can load one into the DOM just like any other XML document. We'll use this feature to load a basic style sheet into a DOM object, then use DOM methods to construct a style sheet instance.
In this case, we'll create a skeleton document that can be loaded from file (see Listing 1). This skeleton file contains the basics of any style sheetthe XML declaration and the stylesheet element. After that, we'll create a script that makes a brand new DOM object, and build our style sheet by creating new elements and adding them to the document tree. Once the style sheet is finished, you can save it to disk or immediately apply it to an XML document.
Setting It Up
As a concrete example, Listing 2 shows an XML document that represents an article, such as would appear in a magazine. To render the class of article documents, I've created the XSLT style sheet shown in Listing 3. The structure of Listing 3 is straightforwardit transforms an XML document into HTML. The basic structure of the style sheet is to create a root template using
<xsl:template>. Within this template, we want to process the root node and its immediate child nodes. So it uses<xsl:value-of>to grab the headline, deck, byline, and article body, placing them in our HTML transformation. Then, additional templates are included to process ancestors of the<aBody>element.In the transformation, I've included an HTML
<LINK>tag , which associates a CSS style sheet with our HTML output. (Take a look at the CSS style sheet.) Then, I wrap the headline, deck, byline, and so on, in<DIV>tags to render these transformed elements. The<DIV>tag'sCLASSattribute corresponds to a style that I've created in the CSS style sheet. So, when the XML document is processed, the style sheet transforms the XML elements into HTML, and style rules from CSS are used to apply formatting to the resulting HTML. If this is done from a server transformation, the browser sees only HTML, never realizing that an XML document is being served. This, in a nutshell, is how you can use CSS to render any XML document.Creating the DOM Object
Our goal is to create a program that automatically generates the style sheet in Listing 3. As mentioned above, our program uses the DOM to create our style sheet. In the past, I've discussed DOM programming in detail (see "Total DOMination," in the October 2000 issue). In that article, I showed that to begin working with the DOM, you must create a new DOM object, then load an XML document into that object. From that point, you can use DOM methods to walk the document tree, access nodes, query properties, modify nodes, create new ones, and so on. One point I made was that the parser you use determines how your DOM object is created and loaded. In this example, I'm using the MSXML parser, so this part of the code will necessarily be Microsoft specific. Find a MSXML Technology preview at msdn.microsoft.com/xml. The rest of the code should work with any DOM Level 1-compliant parser.
The code for creating a DOM object under the Microsoft parser depends on whether you're working on the client or server side. For example, to create a new DOM object in a client application, you would use
new ActiveX()and assign the result to a variable. This is the technique shown in our example in Listing 4. If you're creating a DOM object on the server-side, say from an Active Server Page, then you must instead useServer.CreateObject().With that background, Listing 4 is an HTML document that includes a JavaScript function called
Parse(). Notice that the<BODY>tag of the HTML includes anONLOAD="Parse()". This invokes theParse()function as soon as the HTML document is loaded into the browser. WithinParse(), the first step is to create a new DOM object as outlined above. However, you'll notice that I've included three different calls to create a DOM object, two of which have been commented out. As noted in the comments of Listing 4, each of these calls invokes a different version of the MSXML parser. As it's presented, the script is running the beta version (2.6) of the MSXML parser. Of course, this assumes you have version 2.6 installed on your machine (for details, see the MSXML Technology Preview at msdn.microsoft.com/xml). To run MSXML version 1, simply comment out the reference to the version 2 parser and uncomment the code that instantiates the version 1 parser.Either way, the result of this operation is assigned to the
xslDocumentvariable. So, the next step is to populatexslDocumentwith the skeleton XSLT file in Listing 1. This is done using theload()method. As with DOM object creation, the method for populating a DOM object is specific to the parser you're using, so this line will vary.Building the Template Rules
With the
xslDocumentpopulated, we can now use DOM methods to build the style sheet in Listing 3. Because the skeleton XSLT document contains only an empty<xsl:stylesheet>element, the first task is to create the root template. So, Listing 4 uses the DOM'screateElement()method to create a new element calledxsl:template. Creating a new element doesn't automatically insert the element in the document tree, so I callappendChild()to insertxsl:templateas the last child of<xsl:stylesheet>. Once the element is in the tree, the script usessetAttribute()to insert thematch="/index.html"attribute-value pair into the<xsl:template>element.At this point, we want to begin adding the HTML code that will be used in our transformation. Originally, I tried to insert HTML tags as text. Unfortunately, when the document is processed, markup characters are replaced with predefined entities. For example, the
<HTML>tag is rewritten as<HTML>. Then, I realized that I could simply insert HTML tags as if they were XML markup: The processor doesn't care what the markup isit believes everything is XML. Thus, the script simply usescreateElement()again to create theHTML,BODY, andLINKtags.The challenge this time comes when you try to insert these HTML elements into the document tree. For example,
<BODY>is a subelement of<HTML>, which in turn is a subelementor more appropriately, the contentof<xsl:template>. When inserting the<HTML>element, the script walks from the root node (documentElement) to the last child (lastChild) and appends the newly created element to this node's list of subnodes. Appending<BODY>as a subelement of<HTML>is a bit easier. Because we now have a variable (Elem) that contains a pointer to the<HTML>element, Listing 4 simply callsappendChild()fromElem.Next, the
LINKtag is generated andDIVtags are inserted in the document. As I mentioned above, theLINKtag brings in a CSS style sheet and the<DIV>tags make use of these styles to format the HTML that's generated.The last step, at least for this simple case, is to insert our XML content into the
<DIV>tag. In XSLT, this is done using<xsl:value-of>. So, Listing 4 again callscreateElement()to create the<xsl:value-of>element for each piece of data we wish to return, sets the select attribute to correspond with the XML data we want to present, and inserts the element into the document tree at the point where<DIV>element appears.Finally, the script writes the resulting document tree out to disk. This lets you view the results quickly and easily. To test this example, simply link the style sheet to your XML document using an
<?xsl-stylesheet>processing instruction, then launch the XML document in Internet Explorer to view the resulting transformation.Conclusion
One of the problems I've encountered in developing XML-based Web sites is the proliferation of XSLT style sheets. I use XSLT style sheets to transform XML documents to HTML, which is specifically formatted for a certain browser type. For example, if a Netscape Navigator 4 browser requests a document, I detect the browser and version, and attach the style sheet for that particular browser. Because I have style sheets for Navigator 3 and 4, Internet Explorer 3, 4, and 5, and a generic one for other browsers, this results in a half dozen XSLT documents. However, I have several different types of documents (that is, documents of differing structures), so the propagation of style sheets grows exponentially. This is further compounded by the fact that my site uses the Rocket XML framework to support four different user interfaces, or themes, for each Web page served. These themes are, of course, generated by different style sheets. Ultimately, I have to support well over 100 style sheets.
The problem rears its head when you need to change something, like adding a new element type for a particular class of XML documents. Because your XSLT style sheets rely on the structure of your documents, a simple change like this could affect two dozen or more style sheets. That's far too fragile for my taste. Generating style sheets dynamically solves this problem. That is, because a program can now generate my style sheets, all I have to do is modify a few lines of code and I'm done.
(Get the source code for this article here.)
Michael is the author of Building Web Sites with XML from Prentice Hall, and architect of the Rocket XML framework. He's also the publisher of LifestylesSantaCruz.com and carries the honorary title of Editor At Large at Web Techniques. He can be reached at mfloyd@lifestylesSantaCruz.com.
|
|