Introduction to XSTL with Delphi

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.

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

 try
   XMLDocument1.Active := False; // just in case
   XSLPageProducer1.FileName := FileNameXSLT;
   XMLDocument1.FileName := FileNameXML;
   XSLPageProducer1.XMLData := XMLDocument1;
   XMLDocument1.Active := True;
   MemoXSL.Text := XSLPageProducer1.Content; // Transform!!
   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);
 //  OleCheck(Result.parseError.errorCode);
   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:

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;
 {$APPTYPE CONSOLE}
 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');
{    XmlOut := XslTrans.Transform(XmlDoc, nil);
     XmlOut.Free;
}    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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习是一种人工智能(AI)的子领域,致力于研究如何利用数据和算法让计算机系统具备学习能力,从而能够自动地完成特定任务或者改进自身性能。机器学习的核心思想是让计算机系统通过学习数据中的模式和规律来实现目标,而不需要显式地编程。 机器学习应用非常广泛,包括但不限于以下领域: 图像识别和计算机视觉: 机器学习在图像识别、目标检测、人脸识别、图像分割等方面有着广泛的应用。例如,通过深度学习技术,可以训练神经网络来识别图像中的对象、人脸或者场景,用于智能监控、自动驾驶、医学影像分析等领域。 自然语言处理: 机器学习在自然语言处理领域有着重要的应用,包括文本分类、情感分析、机器翻译、语音识别等。例如,通过深度学习模型,可以训练神经网络来理解和生成自然语言,用于智能客服、智能助手、机器翻译等场景。 推荐系统: 推荐系统利用机器学习算法分析用户的行为和偏好,为用户推荐个性化的产品或服务。例如,电商网站可以利用机器学习算法分析用户的购买历史和浏览行为,向用户推荐感兴趣的商品。 预测和预测分析: 机器学习可以用于预测未来事件的发生概率或者趋势。例如,金融领域可以利用机器学习算法进行股票价格预测、信用评分、欺诈检测等。 医疗诊断和生物信息学: 机器学习在医疗诊断、药物研发、基因组学等领域有着重要的应用。例如,可以利用机器学习算法分析医学影像数据进行疾病诊断,或者利用机器学习算法分析基因数据进行疾病风险预测。 智能交通和物联网: 机器学习可以应用于智能交通系统、智能城市管理和物联网等领域。例如,可以利用机器学习算法分析交通数据优化交通流量,或者利用机器学习算法分析传感器数据监测设备状态。 以上仅是机器学习应用的一部分,随着机器学习技术的不断发展和应用场景的不断拓展,机器学习在各个领域都有着重要的应用价值,并且正在改变我们的生活和工作方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值