一个XSLT的变量、参数和模板调用的问题

1.     问题

昨天遇到了这样一个xml文档,如下:bookObject.xml

<?xml version="1.0" encoding="ASCII"?>

<?xml-stylesheet type="text/xsl" href="Book2PubXSLT.xsl"?>

<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI">

<Book title="WxbBook">

           <chapters nbPages="12" title="chapter1" author="author1"/>

           <chapters nbPages="15" title="chapter2" author="author2"/>

           <chapters nbPages="20" title="chapter3" author="author3"/>

</Book>

<Book title="nextBook">

           <chapters nbPages="10" title="chapter1" author="aaa"/>

           <chapters nbPages="20" title="chapter2" author="bbb"/>

           <chapters nbPages="30" title="chapter3" author="ccc"/>

</Book>

</xmi:XMI>

希望能够使用XSLT转换为这样的一个xml文档(目的是为了做一个很简单的模型转换实例,当然这点和本文主题几乎无关):publicationObject.xml

<?xml version="1.0" encoding="ASCII"?>

<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns="Publication">

<Publication title="nextBook" nbPages="60" authors="ccc and aaa and bbb"/>

<Publication title="WxbBook" nbPages="47" authors="author3 and author1 and author2"/>

</xmi:XMI>

分析这两个文档可以看出,希望的转换规则如下:

Book节点有一个属性title,它等于Publication的节点属性title。

Book节点包含chapters子节点,每个子节点都有三个属性。其中一个Book节点的所有chapters子节点的nbPages属性相加等于Publication的nbPages属性;

一个Book节点的所有chapters子节点的author相连,并在中间插入and则等于Publication的authors属性。

由于之前我并没有接触过XSLT的变量、参数和xsl:call-template等概念,所以还是颇费了一点时间来解决此问题。

2.     问题的解决

值得注意的有几点:

1.              XSLT中的变量一次赋值后是不能改变的,所以这里的变量几乎等于其它语言中的常量。

2.              template有两种,常见的是通过<xsl:template match="/">这样的方式定义的,使用match属性。调用的时候是使用<xsl:apply-templates select=""/>。另一种template类似与其它语言中的函数调用,使用<xsl:template name=”t_name”>来定义,调用的时候使用<xsl:call-template name=" t_name "/>来调用。

3.              在第二种template的定义中可以加入参数,类似于其它编程语言中的参数列表。在调用时也可以传入具体的值做为实参。

4.              由于XSLT不包含for、while等循环语句。当简单的for-each不能满足要求时。则需要使用递归template调用来完成其它语言中的循环。从functional programming的理论中我们知道这二者是等价的。

下面是进行转换的XSL文档:Book2PubXSLT.xsl

<?xml version="1.0" encoding="ASCII"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:xmi="http://www.omg.org/XMI">

<xsl:template name="left">

<xsl:text disable-output-escaping="yes">&lt;</xsl:text>

</xsl:template>

<xsl:template name="right">

<xsl:text disable-output-escaping="yes">&gt;</xsl:text>

</xsl:template>

 

<xsl:template match="/">

<xsl:call-template name="left"/>xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns="Publication"<xsl:call-template name="right"/>

           <xsl:apply-templates select="xmi:XMI"/>

           <xsl:call-template name="left"/>/xmi:XMI<xsl:call-template name="right"/>

</xsl:template>

 

<xsl:template match="xmi:XMI">

           <xsl:for-each select="Book">

           <xsl:call-template name="left"/>Publication title="<xsl:value-of select="@title"/>"

                           nbPages ="<xsl:value-of select="sum(chapters/@nbPages)"/>"

              authors ="

                    <xsl:call-template name="concatAuthor">

                             <xsl:with-param name="str" select="chapters/@author"/>

                             <xsl:with-param name="index" select="1"/>

                             <xsl:with-param name="nodenum" select="last()+1"/>

                    </xsl:call-template>"/<xsl:call-template name="right"/>

           </xsl:for-each>

</xsl:template>

 

<xsl:template name="concatAuthor">

           <xsl:param name="str"/>

           <xsl:param name="index"/>

           <xsl:param name="nodenum"/>

           <xsl:choose>

                    <xsl:when test="$index != $nodenum">

                             <xsl:call-template name="concatAuthor">

                                       <xsl:with-param name="str" select="concat($str,' and ',chapters[$index+1]/@author)"/>

                                       <xsl:with-param name="index" select="$index + 1"/>

                                       <xsl:with-param name="nodenum" select="$nodenum"/>

                             </xsl:call-template>

                    </xsl:when>

                    <xsl:otherwise>

                             <xsl:value-of select="$str"/>

                    </xsl:otherwise>

           </xsl:choose>

</xsl:template>

</xsl:stylesheet>

从上面可以看出,解决问题最主要的一点就是使用了一个递归模板concatAuthor,它带有三个参数str,index 和nodenum,分别表示连接的字符串、迭代子和chapters的节点数。通过这种方法,熟悉递归的人可以很快的写出几乎和javascript等价的种种数据操作。当然,由于变量不能赋值、参数只能以递归的方式来改动,还是很不方便的。

其优点也是明显的,不依赖其它脚本语言完成了数据操作。可以在所有xsl引擎上面运行。

3.     小结

深入讲解XSLT的中文文献不多,大多数是入门级的。当我半猜半试的解决了这个问题后。发现了在O’Reilly有一本《XSLT》的书,其中列出了这样的内容。

出处:http://www.blogjava.net/wxb_nudt/archive/2006/03/08/34377.html

posted on 2012-07-18 17:48  jack_Meng  阅读(2374)  评论(0编辑  收藏  举报

导航