Introduction to XSTL with Delphi

转载 2004年10月25日 10:01:00

Introduction to XSTL with Delphi
XSLT stands for eXtensible Stylesheet Language (XSL) Template, and is generally used to transform an XML document to another XML document (for example a HTML compliant XML document).
In this article, I will give an introduction to the basic capabilities of XSLT, as well as the reasons why XSLT can be beneficial to Delphi developers. We'll start of with some simple examples of using XSTL to transform XML documents to other XML documents. We then move on to the XSLT support in Delphi in the form of the TXSLPageProducer component (found on the WebSnap tab, but actually not related to WebSnap at all), which can be used to convert any XML data packet or XML document (using XSL Transformation rules), but is not always clear in its use.
I will also show where to get a few XSLT examples that are also a bit "hidden" in Delphi 6 and 7 Enterprise.

Finally, I will show that XSLT on the .NET Framework can be done using the XSL classes from the System.Xml.Xsl namespace, which contains the class XslTransform for this purpose.

What is XSLT?
XSLT stands for XSL Transformations, where XSL stands for eXtensible Stylesheet Language. It is primarily meant to transform XML documents to something else (another XML document, a HTML document or even a plain text document).
Using Delphi (and Delphi for .NET), we can load XML documents, read them, work with them, but it's harder for perform the operations on XML documents that we can do with a few lines of XSTL. Besides, we can use Delphi to build an XSLT processor, so still keep the "main" application in Delphi, while we're using the XSTL capabilities of Win32 or .NET.

How can you "run" XSLT?
There are several ways to execute or apply an XSLT on a XML document. The easiest way is to download the msxsl.exe command-line tool from Microsoft at http://msdn.microsoft.com/xml. msxsl.exe is a command-line XSLT processor, of which I use version 3. The instructions for running MSXSL are as follows:

  Usage: MSXSL source stylesheet [options] [param=value...] [xmlns:prefix=uri...]

  Options:
    -?            Show this message
    -o filename   Write output to named file
    -m startMode  Start the transform in this mode
    -xw           Strip non-significant whitespace from source and stylesheet
    -xe           Do not resolve external definitions during parse phase
    -v            Validate documents during parse phase
    -t            Show load and transformation timings
    -pi           Get stylesheet URL from xml-stylesheet PI in source document
    -u version    Use a specific version of MSXML: '2.6', '3.0', '4.0'
    -             Dash used as source argument loads XML from stdin
    -             Dash used as stylesheet argument loads XSL from stdin
This will work fine as a blackbox for our first examples. However, later in this article we'll also build our own XSLT processors with both Delphi 7 (Enterprise) and Delphi for .NET (using the classes from the System.XML.XSL namespace).

Some examples of XSLT
XSLT itself is an XML document with a special syntax that we have to read to understand what it's doing. They start with the following line (make no typing mistakes):

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
And obviously end with:
 </xsl:stylesheet>
If you apply an otherwise empty XSLT document to an XML document, then no transformation are performed which means that all XML tags will be removed and only the text between the tags remains.

A second XSLT example that ignores the XML document but simply produces some output is the following:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:template match='/'>
   <clinics>
    <clinic name="Delphi 8 for .NET" date="27 May 2004"/>
    <clinic name="Delphi 8 for .NET and ASP.NET" date="24 June 2004"/>
   </clinics>
  </xsl:template>
 </xsl:stylesheet>
This one uses an xsl:output statement that specifies that the output is of type XML (version 1.0, UTF-8) and that indentation should be done.
The xsl:template element matches the root of the XML document, where the transformation starts, and replaces the root with the literal output that I've specified. No other transformation are done.
Applying this XSLT transformation on any XML document (doesn't matter which one) produces the following XML output file:
 <?xml version="1.0" encoding="UTF-8"?>
 <clinics>
 <clinic name="Delphi 8 for .NET" date="27 May 2004" />
 <clinic name="Delphi 8 for .NET and ASP.NET" date="24 June 2004" />
 </clinics>
Now, suppose we want to change the attribute or a node from an XML document. We must then use another match expression, where a node is specified by the fully qualified name (starting from the root), and an attribute has an @ character before the name.

The first example is to copy an XML document to itself - including the XML tags (each node to itself), which is defined as follows (note that I've ommited the <xsl:output ...> statement to make sure the resulting XML document is the same format as the input XML document):

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="node() | @*">
   <xsl:copy>
    <xsl:apply-templates select="@* |node()"/>
   </xsl:copy>
  </xsl:template>
 </xsl:stylesheet>
The xsl:template element now matches the XPath expression node() which means each node in the XML document. The xsl:apply-templates is used to replace the @* with the XPath node() again which results in a literal copy of each input node to itself as output note.
An example that extends this functionality and turns each attribute into a child node (element) is defined as follows:
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

 <xsl:template match="node() | @*">
  <xsl:copy>
   <xsl:apply-templates select="node() | @*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="@*">
  <xsl:element name="{local-name(.)}">
   <xsl:value-of select="."/>
  </xsl:element>
 </xsl:template>

 </xsl:stylesheet>
The transformation is performed in two steps: first of all, the node() are replaced by themselves, and for attributes (@*) we continue to the next step (the second template match). In this second step, the attributes attributes (@*) are replaced by an element of the same name (local-name(.)) with as value, the value of the attribute itself.

Of course, we can also covert a document the other way around: turn all sub-elements of a specific node into attributes instead. As an example, I've once written an application that creates a disk.xml file (containing the directories and files on my local disk): an XML file with sub-elements date and size for <file> nodes. Instead of using the elements, I would like to change this big XML file and turn the child-elements into attributes. This can be done with the following XSTL:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

 <xsl:template match="node() | @*">
  <xsl:copy>
   <xsl:apply-templates select="node() | @*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="file">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
   <xsl:for-each select="*">
    <xsl:attribute name="{local-name(.)}">
     <xsl:value-of select="."/>
    </xsl:attribute>
   </xsl:for-each>
  </xsl:copy>
 </xsl:template>

 </xsl:stylesheet>
This consists of a number of steps. First of all, we start with the transformation of all nodes and attributes to themselves. Them the elements "file" are handled separately. We first copy the attributes of the file nodes themselves, and then for all sub-elements, turn them into attributes themselves. A problem could occur if this results into attributes with the same name (i.e. an element that also occured as attribute), but I leave it as excercise for the reader to decide how to rename the attributes in that case.

Default Templates
XSLT needs matching templates in order to perform the transformation. However, sometimes, no matching template can be found. For those casee, XSTL defines a number of default templates that will be used if no matching template can be found. The first default template is defined as follows:

 <xsl:template match="*|/">
   <xsl:apply-template/>
 </xsl:template>
This template matches all elements and the root, and continues the transformation process by re-applying the templates for the child elements (but not the attributes) of the current node. To ensure that all nodes are transformed.
Another default template is defined as follows:
 <xsl:template match="text()|@*">
   <xsl:value-of select="."/>
 </xsl:template>
This template matches the text nodes and all attributes from the elements and sends the values of these elements to the output. This is usually not what you would want, so you should try to transform text notes and attributes values yourself.

Our own XSLT Processor
There are several ways in which we can build our own XSLT Processor. I will start with Delphi 7 Enterprise, then move to Delphi Professional and show how we can perform the XSLT transformations by ourselves.
In Delphi 7 Enterprise, we can find a rather strange component called the TXslPageProducer - as part of WebSnap (although it's not really tightly integrated with WebSnap to be honest). This component works like a regular PageProducer in that we can call the "Content" property to fire the method (the GetContent function) that will perform the XSLT transformation. The only problem is that the properties that we can use to "feed" this transformation are not always clear...

XML and XSL
The XSLPageProducer component can be used to turn an XML document (or XML string) into another XML string, using an EXtensible Stylesheet Language (XSL) template. Note that the Delphi 6 on-line help still calls it the TXMLPageProducer component instead of the TXSLPageProducer component. The XSLPageProducer component has a number of properties that can easily be confused with each other (like the FileName, XML and XMLData properties, as I will make clear in a moment).
First of all, the only way to specify the input XML content is by using the XMLData property. You cannot use the XML property, since this is used to specify the XSL template (just like the FileName is used to point to an external XSL template file). I wonder why the XML property isn't called XSL, but I guess that's the same reason why the on-line help refers to it as the TXMLPageProducer instead the TXSLPageProducer component - it may have been renamed late in the development process of Delphi 6 (and Borland may have forgot the XML property - or would break existing code and examples by changing it).

XMLData property
Anyway, the XMLData property can point to any component that implements the IXMLDocument interface (like the TXMLDocument component) or the IGetXMLStream interface (like the TXMLBroker component). Since we're already using the data module with a TXMLBroker component, we can use that one. Just click on the XMLData property of the XSLPageProducer component and select XMLBroker1 to connect it to the XMLBroker component (which in turn connects to the XMLTransformProvider component).

FileName property
The FileName property points to an external XSL template file, which should contain XSL Transformations (XSLT) using XPath and XPointer (a bit more about that in a moment). It may be handy to use the FileName property, but you can also use the XML property to make sure the XSL template is embedded within the XSLPageProducer component (and hence the web server application itself). Unfortunately, when you click on the ellipsis to start the property editor for the FileName property, you get a File Open dialog that by default starts to look for XML files. You have to open the "Files of type" combobox in order to specify that you're looking for XSL files instead.

XML property
The XML property is yet another "strange one". You may think that this one can be used as alternative for the XMLData property, but that's not the case. The XML property is actually an alternative for the FileName property, and should in that function contain the XSL template, and not the XML data (so the "XML" property is a bit misleading). Warning: if you click on the XML property and enter some XSL Transformations, be aware that you'll clear the FileName property! It appears that the FileName and XML property are mutual exclusive. If you set a value to one, you clear the other. And this can be especially painful if you enter a new FileName property and accidentally clear the XML property (containing a potentially long list of XSL Transformations). In our example, I'll fill the XML property with the following set of XSL Transformations that can be generated by Delphi itself:

  <?xml version="1.0"?>
  <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
    <xsl:template match="/">
      <html>
      <body>
      <xsl:apply-templates/>
      </body>
      </html>
    </xsl:template>

    <xsl:template match="DATAPACKET">
      <table border="1">
      <xsl:apply-templates select="METADATA/FIELDS"/>
      <xsl:apply-templates select="ROWDATA/ROW"/>
      </table>
    </xsl:template>

    <xsl:template match="FIELDS">
      <tr>
      <xsl:apply-templates/>
      </tr>
    </xsl:template>

    <xsl:template match="FIELD">
      <th>
      <xsl:value-of select="@attrname"/>
      </th>
    </xsl:template>

    <xsl:template match="ROWDATA/ROW">
      <tr>
      <xsl:for-each select="@*">
        <td>
        <xsl:value-of/>
        </td>
      </xsl:for-each>
      </tr>
    </xsl:template>

  </xsl:stylesheet>
This XSL Transformation template above is especially designed to handle data packets that are coming from the XMLBroker component, and can originally be found in the XSLProducer WebSnap demo directory of Delphi 6 itself. Tip: if for some reason you can't locate this example, then you can use Delphi 6 to produce it for you as example template. Do File | New | Other, go to the WebSnap tab of the Object Repository and double-click on the WebSnap Page Module icon. In the dialog that follows, select XSLPageProducer as Producer Type. Now, make sure the XSL "New File" option is checked, and select the type of template from the combobox (standard, blank or data packet). For our example, select "data packet". Ignore all other options on the dialog, because we are only interested in the generated XSL template file. Click on OK to generate a new WebSnap Page Module. Click on the new Uni1.xsl tab, copy the contents and paste it inside the XML property of the XSLPageProducer. Alternately, you may save the contents in file datapacket.xsl and assign the FileName property to datapacket.xsl (note that the property editor starts to look for .xml files first, you need to make sure to put the .xsl file inside it). Make sure that you don't save the Unit1.pas or .dfm itself, since we now need to remove the useless WebSnap Page Module from the WebBroker project (do View | Project Manager, and remove Unit1 from the WebBroker project). And finally, in case you're interested, the standard XSL template produces the following three lines (ready for you to enter your own custom XSL):
  <?xml version="1.0"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/TR/WD-xsl">
  </xsl:stylesheet>
and the blank XSL template is indeed just an empty file.
The best thing, however, is that we can use the XSLPageProducer in a "normal" WebBroker environment, without the need for WebSnap Page Modules. The XSL Template language using XPath and XPointer can be used to specify the transformation from XML to HTML-compliant XML that can be shown in the browser.

XSLT Processor
In order to build our own XSLT Processor, we need an XMLDocument component (filled with an XML Document), as well as a place to store the XSLT templates, and a place to store the resulting transformation. Figure 1 shows a Delphi form at design-time with three memo fields: MemoXML, MemoXSLT and MemoXSL (for the result) as well as a XSMLDocument and XSLPageProducer component to perform the work.

examn50a.gif

The few steps of code that are needed to perform the XSLT transformation are as follows:

 try
   XMLDocument1.Active := False; 
   XSLPageProducer1.FileName := FileNameXSLT;
   XMLDocument1.FileName := FileNameXML;
   XSLPageProducer1.XMLData := XMLDocument1;
   XMLDocument1.Active := True;
   MemoXSL.Text := XSLPageProducer1.Content; 
   XMLDocument1.Active := False;
 except
   on E: Exception do
     MemoXSL.Text := E.Message
 end;

Alternative Approach
If you don't have Delphi 6 or 7 Enterprise, then you don't have WebSnap with the XSLPageProducer component. Fortunately, there's still a way to perform the same XSLT Transformation using the transformNode method of the IXMLDOMDocument interface (from the MSXML unit):

 procedure TForm1.XSLT1Click(Sender: TObject);

   function LoadXmlDoc(const FileName: WideString): IXMLDOMDocument;
   begin
     Result := CoDomDocument.Create();
     Result.async := False;
     Result.load(FileName);
 
   end;

 var
   doc, xsl: IXMLDOMDocument;
 begin
   try
    doc := LoadXmlDoc(XML);
    xsl := LoadXmlDoc(XSLT);
    MemoXSL.Text := doc.transformNode(xsl)
   except
     on E: Exception do
       MemoXSL.Text := E.Message
   end
 end;
As final example, remember the input.xml file (including attributes) that was transformed to output.xml (attributes changed into elements) by the Attr2Child.xslt transformation, which is shown in action in the figure below:

examn50b.gif

XSLT on .NET
We can also perform XSLT transformations (to work on XML documents) on the .NET Framework using the System.Xml.Xsl and System.Xml.Path namespaces from the System.Xml assembly.

 program XSLT;
 
 uses
   System.IO,
   System.Xml,
   System.Xml.Xsl,
   System.Xml.XPath;
 var
   XslTrans: XslTransform;
   XmlDoc: XmlDocument;
   XmlOut: XmlReader;
   XmlStream: FileStream;
 begin
   try
     XslTrans := XslTransform.Create;
     XslTrans.Load('attr2child.xslt');
     XmlDoc := XmlDocument.Create;
     XmlDoc.Load('input.xml');
    XmlStream := System.IO.FileStream.Create('output.xml', FileMode.Create);
     XslTrans.Transform(XmlDoc, nil, XmlStream);
     XmlStream.Flush;
   except
     on E: Exception do
       writeln(E.ClassName, ': ', E.Message)
   end;
   XmlStream.Free;
   XmlDoc.Free;
   XslTrans.Free;
 end.
This simple example shows that we can use the Transform method of the XslTransform class to transform a XmlDocument and produce a XmlReader (or directly write the output to an XML FileStream). This code should be enough basis to get you started to write more extensive XSTL processing applications for .NET.

Summary
In this paper, I have give an introduction to the basic capabilities of XSLT, although everyone should conclude for his or herself if XSLT can be beneficial. I don't use it very often, but when I do, I'm glad it's around (and support by Delphi).
I've also shown how we can use the msxsl command-line XSLT processor, and how we can build at least three different XSLT processors using Delphi 7 and the Delphi for .NET preview command-line compiler or Delphi 8 for .NET.

用XSTL转化xml

 package book.xml;import java.io.File;import java.util.Properties;import javax.xml.transform.OutputK...
  • wuhailin2005
  • wuhailin2005
  • 2009年01月08日 18:24
  • 1363

abstract 和 introduction的写法

abstract 1. 大背景, 很重要 2.目前问题 3. 考虑到什么因素,提出算法, 解决问题,1-2句 4.pros and cons 5. 实验证明 introdu...
  • seamanj
  • seamanj
  • 2017年01月16日 20:04
  • 652

【读书笔记】《推荐系统(recommender systems An introduction)》第一章 引言

很久没上来写blog,前两个月赶上校招季节,都忙校招去了。 这本书我早就买来了,不过我从前看过项亮的《推荐系统实践》,看这本书的目录结构和项亮那本差不多,就一直放着没看。最近在做一个推荐系统...
  • xceman1997
  • xceman1997
  • 2014年12月02日 22:57
  • 1379

Reinforcement Learning:An Introduction 读书笔记- Chapter 1

Reinforcement Learning: An Introduction第一章
  • PeytonPu
  • PeytonPu
  • 2017年11月05日 17:18
  • 76

【读书笔记】《推荐系统(recommender systems An introduction)》第三章 基于内容的推荐

基于内容的推荐的基本推荐思路是:用户喜欢幻想小说,这本书是幻想小说,则用户有可能喜欢这本小说 两方面要求:(1)知道用户的喜好;(2)知道物品的属性 基于内容的推荐相比协同过滤方法(个人观...
  • xceman1997
  • xceman1997
  • 2014年12月07日 21:51
  • 1152

An Introduction to GCC

准备工作 注意:本文可能会让你失望,如果你有下列疑问的话:为什么要在终端输命令啊? GCC 是什么东西,怎么在菜单中找不到? GCC 不能有像 VC 那样的窗口吗?…… 那么你真正想要了解的可能...
  • zhangliangaws
  • zhangliangaws
  • 2014年02月23日 21:37
  • 1158

《reinforcement learning:an introduction》第一章《The Reinforcement Learning Problem》总结

由于组里新同学进来,需要带着他入门RL,选择从silver的课程开始。 对于我自己,增加一个仔细阅读《reinforcement learning:an introduction》的要求。 因为之前读...
  • mmc2015
  • mmc2015
  • 2017年07月10日 17:27
  • 1081

A Brief Introduction of Deep Learning

导语 前面已经讲了一些机器学习的相关概念与理解方式,而现在只要提到机器学习,则不得不提深度学习。作为机器学习的一个重要分支,在硬件计算能力日益发达的今天,深度学习日益受到重视,变得广为人知。那么深度...
  • cherrylvlei
  • cherrylvlei
  • 2016年11月27日 20:13
  • 653

Introduction to Linear Algebra, Fifth Edition.pdf 英文原版 免费下载

下载地址: Introduction to Linear Algebra, Fifth Edition.pdf
  • jiongyi1
  • jiongyi1
  • 2018年01月20日 15:07
  • 649

MIT_Introduction to Algorithms课程资料

http://blog.csdn.net/tangl_99/article/details/771089 MIT的算法导论第一节课上,教授就说得了算法的performance的重要性,并非简...
  • winbobob
  • winbobob
  • 2014年06月22日 11:41
  • 1164
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Introduction to XSTL with Delphi
举报原因:
原因补充:

(最多只允许输入30个字)