Stand-alone XSL Transformations
Posted: (EET/GMT+2)
Stand-alone XSL Transformations
September 30, 2001
Delphi 6 Enterprise
If you are using XML in your applications, XSL Transformation (XSLT) allows you to convert your XML data to another format for presentation. The destination format could be HTML, XHTML, text or even binary data. Read on to learn how to use Delphi 6 Enterprise to apply XSLT to your XML data.
Delphi 6 Enterprise contains new WebSnap components, one of which is named TXSLPageProducer. Although this component has been originally intended to be used in WebSnap applications, it can also be used in stand-alone applications. Being able to do stand-alone XSL transformations is important because not all applications are WebSnap applications. For example, you could store database data in XML format and create on-the-fly HTML in your GUI application. Or, you might wish to add XSLT support to your old WebBroker applications without converting them to WebSnap applications first.
The code
The following code displays an example of a command-line XSLT tool called XSLTransform. It is similar to the Microsoft msxsl.exe utility. The XSLTransform program is good for learning XSL transformations as well, since you get immediate feedback (just as our IBXML database tool, by the way). Behind the scenes, it uses the Microsoft MSXML components available for free.
The source code looks like this:
Program XSLTransform;
{$APPTYPE CONSOLE}
Uses SysUtils,ComObj,Forms,Classes,XMLDoc,XSLProd;
Var
XMLFile,XSLFile : String;
OutputFile : String;
Procedure ShowHelp;
Begin
WriteLn('XSLTransform v1.0. Code by WhirlWater.com 2001, provided "as is".');
WriteLn('Takes an XML document and transforms it using an XSL document.');
WriteLn('Optionally outputs the results to a file for later inspection.');
WriteLn('Requires MSXML components available for free from Microsoft.');
WriteLn;
WriteLn('Usage: XSLTRANSFORM [xmlfile xslfile [output]]');
WriteLn('Where: xmlfile = Name of XML file to transform.');
WriteLn(' xslfile = Name of XSL transformation file.');
WriteLn(' output = Name of file where to store resulting transformation.');
WriteLn(' Default is standard output (STDOUT).');
WriteLn('Process exit code is above zero in case of failure.');
Halt(1);
End;
Procedure GetParams;
Begin
XMLFile := ParamStr(1);
XSLFile := ParamStr(2);
OutputFile := ParamStr(3);
If (Not FileExists(XMLFile)) Then Begin
WriteLn('XSLTransform: Cannot find XML file "',XMLFile,'", exiting.');
Halt(2);
End;
If (Not FileExists(XSLFile)) Then Begin
WriteLn('XSLTransform: Cannot find XSL file "',XSLFile,'", exiting.');
Halt(3);
End;
End;
Procedure TransformXML;
Var
XSLT : TXSLPageProducer;
XML : TXMLDocument;
Failed : Boolean;
Content : String;
OutStrm : TFileStream;
Begin
Failed := False;
Try
Application.Initialize; { to initialize COM with CoInitialize() }
XSLT := TXSLPageProducer.Create(Application);
XML := TXMLDocument.Create(Application);
Try
{ open XML document }
XML.FileName := XMLFile;
XML.Active := True;
{ open XSL document }
XSLT.XMLData := XML;
XSLT.FileName := XSLFile;
XSLT.Active := True;
{ transform }
Content := XSLT.Content;
If (OutputFile <> '') Then Begin
OutStrm := TFileStream.Create(OutputFile,fmCreate,fmShareDenyWrite);
Try
OutStrm.Write(Pointer(Content)^,Length(Content));
Finally
OutStrm.Free;
End;
WriteLn('XSLTransform: Wrote results to "',OutputFile,'".');
End
Else WriteLn(Content); { standard output }
Finally
XML.Free;
XSLT.Free;
End;
Except
On E : Exception do Begin
Failed := True;
WriteLn('XSLTransform: ',E.Message+'.');
End;
End;
If Failed Then Halt(4);
End;
Begin
If (Not (ParamCount In [2,3])) Then ShowHelp;
GetParams;
TransformXML;
End.
Of course, the most important procedure is the TransformXML procedure. It uses the given XML and XSL filenames, and creates a TXSLPageProducer and a TXMLDocument component on the fly. Note that the TXSLPageProducer has a property named XMLData, which must point to a TXMLDocument component (or similar). This TXMLDocument component then supplies the raw XML data that is going to be transferred by the TXSLPageProducer component. The FileName property of this component specifies the XSL filename, not the XML data file itself.
Also note that it is important to call Application.Initialize before trying to use these two components. This is because both components use COM, and COM support must be initialized with a call to CoInitialize or CoInitializeEx. Having the ComObj unit in your Uses clauses and calling Application.Initialize takes automatically care of this. If you don't initialize COM, you will get an error "Microsoft MSXML is not installed" when trying to use the component.
Similarly, both the TXSLPageProducer and TXMLDocument components must have an owner (the Application itself in this case). Otherwise you will get an error "Invalid pointer operation" when calling the Content method of the TXSLPageProducer component.
XSLT := TXSLPageProducer.Create(Application); XML := TXMLDocument.Create(Application);
Calling the Content method does the actual XSL transformation. After this, it is easy to output the resulting (HTML) data to the screen or save it as a file. Note that since the example application is a Win32 console application, the output will actually be standard output (stdout). So, it is easy to redirect the output to another program or a file if you like.
Testing the tool
Once you have downloaded the example code and compiled it, you are ready to test the application. The following screen shot shows an example of its use:

To test the application, you can use the employee.xml and employee.xsl files available on the ZIP file downloadable below.
C:\>xsltransform employee.xml employee.xsl employee.html
The employee.xml is a XML file that was generated with a TClientDataSet component and the SaveToFile method with the datapacket format of dfXML. The result of this transformation is an HTML file to list the employees from the following XML file:
<?xml version="1.0" standalone="yes"?>
<DATAPACKET Version="2.0">
<METADATA>
<FIELDS>
<FIELD attrname="EmpNo" fieldtype="i4"/>
<FIELD attrname="LastName" fieldtype="string" WIDTH="20"/>
<FIELD attrname="FirstName" fieldtype="string" WIDTH="15"/>
<FIELD attrname="PhoneExt" fieldtype="string" WIDTH="4"/>
<FIELD attrname="HireDate" fieldtype="dateTime"/>
<FIELD attrname="Salary" fieldtype="r8"/>
</FIELDS>
<PARAMS DEFAULT_ORDER="1" PRIMARY_KEY="1" LCID="1033"/>
</METADATA>
<ROWDATA>
<ROW EmpNo="2" LastName="Nelson" FirstName="Roberto" PhoneExt="250"
HireDate="19881228" Salary="40000"/>
<ROW EmpNo="4" LastName="Young" FirstName="Bruce" PhoneExt="233"
HireDate="19881228" Salary="55500"/>
<ROW EmpNo="5" LastName="Lambert" FirstName="Kim" PhoneExt="22"
HireDate="19890206" Salary="25000"/>
...
</ROWDATA>
</DATAPACKET>
Then, the XSL file is the following:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<HTML>
<HEAD>
<TITLE>Employees</TITLE>
</HEAD>
<BODY>
<H1>Employees</H1>
<TABLE BORDER="1">
<TR><TD><B>Last Name</B></TD><TD><B>First Name</B></TD></TR>
<xsl:for-each select="DATAPACKET/ROWDATA/ROW">
<xsl:sort select="@LastName"/>
<TR>
<TD><xsl:value-of select="@LastName"/></TD>
<TD><xsl:value-of select="@FirstName"/></TD>
</TR>
</xsl:for-each>
</TABLE>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>
As a result, the HTML file looks like this on a browser (note the automatic sort done by the XSLT):

Now, go ahead and download the example code and start generating XSL transformations in your own applications.
Download the example code
Download standalonexsltransformations.zip (237 kB) which contains the sample application XSLTransform developed in this article. Please note that the sample application will require Delphi 6 Enterprise as well as the free MSXML components available from Microsoft.
* * *
Need consulting help to develop your XML applications? Contact us today!