重用使用不同相对XPath调用XSL模板

时间:2010-06-18 15:46:25

标签: xml xslt

这是我设计的例子,说明了我想要完成的事情。我有一个输入XML文件,我希望将其压缩以便进一步处理。

输入文件:

<BICYCLES>
    <BICYCLE>
        <COLOR>BLUE</COLOR>
        <WHEELS>
            <WHEEL>
                <WHEEL_TYPE>FRONT</WHEEL_TYPE>
                <FLAT>NO</FLAT>
                <REFLECTORS>
                    <REFLECTOR>
                        <REFLECTOR_NUM>1</REFLECTOR_NUM>
                        <SHAPE>SQUARE</SHAPE>
                    </REFLECTOR>
                    <REFLECTOR>
                        <REFLECTOR_NUM>2</REFLECTOR_NUM>
                        <SHAPE>ROUND</SHAPE>
                    </REFLECTOR>
                </REFLECTORS>
            </WHEEL>
            <WHEEL>
                <WHEEL_TYPE>REAR</WHEEL_TYPE>
                <FLAT>NO</FLAT>
            </WHEEL>
        </WHEELS>
    </BICYCLE>
</BICYCLES>

输入是<BICYCLE>个节点的列表。每个<BICYCLE>都有一个<COLOR>,并且可选择<WHEELS>

<WHEELS><WHEEL>个节点的列表,每个节点都有一些属性,并且可选择<REFLECTORS>

<REFLECTORS><REFLECTOR>个节点的列表,每个节点都有一些属性。

目标是扁平化这个XML。这是我正在使用的XSL:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" xml:space="preserve"/> 

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

<xsl:template match="BICYCLE">
<xsl:choose>
    <xsl:when test="WHEELS">
        <xsl:apply-templates select="WHEELS"/>
    </xsl:when>
    <xsl:otherwise>
        <BICYCLE>
            <COLOR><xsl:value-of select="COLOR"/></COLOR>
            <WHEEL_TYPE/>
            <FLAT/>
            <REFLECTOR_NUM/>
            <SHAPE/>
        </BICYCLE>
    </xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="WHEELS">
<xsl:apply-templates select="WHEEL"/>
</xsl:template>

<xsl:template match="WHEEL">
    <xsl:choose>
        <xsl:when test="REFLECTORS">
            <xsl:apply-templates select="REFLECTORS"/>
        </xsl:when>
        <xsl:otherwise>
            <BICYCLE>
                <COLOR><xsl:value-of select="../../COLOR"/></COLOR>
                <WHEEL_TYPE><xsl:value-of select="WHEEL_TYPE"/></WHEEL_TYPE>
                <FLAT><xsl:value-of select="FLAT"/></FLAT>
                <REFLECTOR_NUM/>
                <SHAPE/>
            </BICYCLE>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="REFLECTORS">
    <xsl:apply-templates select="REFLECTOR"/>
</xsl:template>

<xsl:template match="REFLECTOR"> 
    <BICYCLE>
        <COLOR><xsl:value-of select="../../../../COLOR"/></COLOR>
        <WHEEL_TYPE><xsl:value-of select="../../WHEEL_TYPE"/></WHEEL_TYPE>
        <FLAT><xsl:value-of select="../../FLAT"/></FLAT>
        <REFLECTOR_NUM><xsl:value-of select="REFLECTOR_NUM"/></REFLECTOR_NUM>
        <SHAPE><xsl:value-of select="SHAPE"/></SHAPE>
    </BICYCLE>
</xsl:template>

</xsl:stylesheet>

输出结果为:

<BICYCLES xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <BICYCLE>
        <COLOR>BLUE</COLOR>
        <WHEEL_TYPE>FRONT</WHEEL_TYPE>
        <FLAT>NO</FLAT>
        <REFLECTOR_NUM>1</REFLECTOR_NUM>
        <SHAPE>SQUARE</SHAPE>
    </BICYCLE>
    <BICYCLE>
        <COLOR>BLUE</COLOR>
        <WHEEL_TYPE>FRONT</WHEEL_TYPE>
        <FLAT>NO</FLAT>
        <REFLECTOR_NUM>2</REFLECTOR_NUM>
        <SHAPE>ROUND</SHAPE>
    </BICYCLE>
    <BICYCLE>
        <COLOR>BLUE</COLOR>
        <WHEEL_TYPE>REAR</WHEEL_TYPE>
        <FLAT>NO</FLAT>
        <REFLECTOR_NUM/>
        <SHAPE/>
    </BICYCLE>
</BICYCLES>

我不喜欢这个是我以几种形式输出颜色属性:

<COLOR><xsl:value-of select="../../../../COLOR"/></COLOR>
<COLOR><xsl:value-of select="../../COLOR"/></COLOR>
<COLOR><xsl:value-of select="COLOR"/></COLOR>
<COLOR/>

似乎应该有一种方法来创建一个命名模板并从需要它的各个地方调用它,并将一些表示路径的参数传递回它引用的<BICYCLE>节点。

有没有办法清理它,比如使用自行车场的命名模板,轮盘和反射器场?

在现实世界的例子中,这是基于,“自行车”还有更多的属性,而不仅仅是颜色,我想让这个XSL很容易改变,包括或排除字段,而不必更改多个XSL地方。

5 个答案:

答案 0 :(得分:2)

您可以使用name属性命名模板。您可以使用<xsl:call-template>按名称调用模板,并且只要<xsl:apply-templates>有效,它就是有效的(IIRC)。

更新(来自评论):听起来你想要一个不同的axis,可能是ancestor。像ancestor::bicycle/color

这样的东西

答案 1 :(得分:2)

  

我不喜欢这个   我正在输出颜色属性   几种形式:

     

          它   似乎应该有办法   创建一个命名模板并调用它   来自各个地方   需要并传递一些参数   代表回到的路径    它引用的节点。

     

有没有办法清理它,比如说   带有自行车的命名模板   田地,轮田和   反射器场?

您可以实现比此更多的重用

原则是避免条件逻辑并让XSLT处理器选择要选择哪个模板进行处理。任何所需的值都应该作为参数传递到路径中以应用于应用的模板。

以下转换演示了这些原则

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

 <xsl:template match="BICYCLE">
  <xsl:apply-templates>
    <xsl:with-param name="pColor" select="COLOR"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="BICYCLE[not(WHEELS)]">
    <BICYCLE>
        <COLOR><xsl:value-of select="COLOR"/></COLOR>
        <WHEEL_TYPE/>
        <FLAT/>
        <REFLECTOR_NUM/>
        <COLOR/>
        <SHAPE/>
    </BICYCLE>
 </xsl:template>

 <xsl:template match="WHEELS">
   <xsl:param name="pColor"/>

   <xsl:apply-templates>
    <xsl:with-param name="pColor" select="$pColor"/>
   </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="WHEEL[REFLECTORS]">
   <xsl:param name="pColor"/>

   <xsl:apply-templates select="REFLECTORS">
    <xsl:with-param name="pColor" select="$pColor"/>
    <xsl:with-param name="pWheel_Type" select="WHEEL_TYPE"/>
    <xsl:with-param name="pFlat" select="FLAT"/>
   </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="WHEEL">
   <xsl:param name="pColor"/>

    <BICYCLE>
        <COLOR><xsl:value-of select="$pColor"/></COLOR>
        <WHEEL_TYPE><xsl:value-of select="WHEEL_TYPE"/></WHEEL_TYPE>
        <FLAT><xsl:value-of select="FLAT"/></FLAT>
        <REFLECTOR_NUM/>
        <COLOR/>
        <SHAPE/>
    </BICYCLE>
 </xsl:template>

 <xsl:template match="REFLECTORS">
  <xsl:param name="pColor"/>
  <xsl:param name="pWheel_Type"/>
  <xsl:param name="pFlat"/>

  <xsl:apply-templates select="REFLECTOR">
    <xsl:with-param name="pColor" select="$pColor"/>
    <xsl:with-param name="pWheel_Type" select="$pWheel_Type"/>
    <xsl:with-param name="pFlat" select="$pFlat"/>
  </xsl:apply-templates>
</xsl:template>

 <xsl:template match="REFLECTOR">
  <xsl:param name="pColor"/>
  <xsl:param name="pWheel_Type"/>
  <xsl:param name="pFlat"/>

    <BICYCLE>
        <COLOR><xsl:value-of select="$pColor"/></COLOR>
        <WHEEL_TYPE><xsl:value-of select="$pWheel_Type"/></WHEEL_TYPE>
        <FLAT><xsl:value-of select="$pFlat"/></FLAT>
        <REFLECTOR_NUM><xsl:value-of select="REFLECTOR_NUM"/></REFLECTOR_NUM>
        <COLOR><xsl:value-of select="COLOR"/></COLOR>
        <SHAPE><xsl:value-of select="SHAPE"/></SHAPE>
    </BICYCLE>
 </xsl:template>

 <xsl:template match="BICYCLE/COLOR"/>
</xsl:stylesheet>

应用于提供的XML文档时,生成所需结果

<BICYCLES>
 <BICYCLE>
  <COLOR>BLUE</COLOR>
  <WHEEL_TYPE>FRONT</WHEEL_TYPE>
  <FLAT>NO</FLAT>
  <REFLECTOR_NUM>1</REFLECTOR_NUM>
  <COLOR>RED</COLOR>
  <SHAPE>SQUARE</SHAPE>
 </BICYCLE>
 <BICYCLE>
  <COLOR>BLUE</COLOR>
  <WHEEL_TYPE>FRONT</WHEEL_TYPE>
  <FLAT>NO</FLAT>
  <REFLECTOR_NUM>2</REFLECTOR_NUM>
  <COLOR>WHITE</COLOR>
  <SHAPE>ROUND</SHAPE>
 </BICYCLE>
 <BICYCLE>
  <COLOR>BLUE</COLOR>
  <WHEEL_TYPE>REAR</WHEEL_TYPE>
  <FLAT>NO</FLAT>
  <REFLECTOR_NUM/>
  <COLOR/>
  <SHAPE/>
 </BICYCLE>
</BICYCLES>

答案 2 :(得分:0)

你的意思是:

<xsl:template name="wheelblock">
  <xsl:param name="color"></xsl:param>
  <!-- Do something here -->
</xsl:template>

<xsl:template match="WHEEL">
  <xsl:call-template name="wheelblock">
    <xsl:with-param name="color">whatever element/etc</xsl:with-param>
  </xsl:call-template>
</xsl:template>

答案 3 :(得分:0)

谢谢汉克盖伊!这就是我想要的。以下XSL与我的原始输出具有相同的输出:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" xml:space="preserve"/> 

<xsl:template name="allfields">
    <BICYCLE>
        <COLOR><xsl:value-of select="ancestor-or-self::BICYCLE/COLOR"/></COLOR>
        <WHEEL_TYPE><xsl:value-of select="ancestor-or-self::WHEEL/WHEEL_TYPE"/></WHEEL_TYPE>
        <FLAT><xsl:value-of select="ancestor-or-self::WHEEL/FLAT"/></FLAT>
        <REFLECTOR_NUM><xsl:value-of select="ancestor-or-self::REFLECTOR/REFLECTOR_NUM"/></REFLECTOR_NUM>
        <SHAPE><xsl:value-of select="ancestor-or-self::REFLECTOR/SHAPE"/></SHAPE>
    </BICYCLE>
</xsl:template>

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

<xsl:template match="BICYCLE">
<xsl:choose>
    <xsl:when test="WHEELS">
        <xsl:apply-templates/>
    </xsl:when>
    <xsl:otherwise>
        <xsl:call-template name="allfields"/>
    </xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="WHEEL">
            <xsl:apply-templates/>
    <xsl:choose>
        <xsl:when test="REFLECTORS">
            <xsl:apply-templates/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="allfields"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="REFLECTOR"> 
    <xsl:call-template name="allfields"/>
</xsl:template>

</xsl:stylesheet>

答案 4 :(得分:0)

两种方式。

保留使用ancestor-or-self轴:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<xsl:template match="/"> 
    <BICYCLES> 
        <xsl:for-each select="BICYCLE[not(WHELLS)]|//REFLECTOR|//WHEEL[not(REFLECTORS)]"> 
            <BICYCLE> 
                <COLOR><xsl:value-of select="ancestor-or-self::BICYCLE/COLOR"/></COLOR> 
                <WHEEL_TYPE><xsl:value-of select="ancestor-or-self::WHEEL/WHEEL_TYPE"/></WHEEL_TYPE> 
                <FLAT><xsl:value-of select="ancestor-or-self::WHEEL/FLAT"/></FLAT> 
                <REFLECTOR_NUM><xsl:value-of select="ancestor-or-self::REFLECTOR/REFLECTOR_NUM"/></REFLECTOR_NUM> 
                <COLOR><xsl:value-of select="ancestor-or-self::REFLECTOR/COLOR"/></COLOR> 
                <SHAPE><xsl:value-of select="ancestor-or-self::REFLECTOR/SHAPE"/></SHAPE> 
            </BICYCLE> 
        </xsl:for-each> 
    </BICYCLES> 
</xsl:template> 

</xsl:stylesheet> 

这只是误用规则处理模型的一个例子。

相比之下,使用Dimitre帖子,您可以避免使用强制逻辑并将模板应用于所需的分支。

在这种情况下,我认为后代和祖先轴对于长XML输入效率不高。因此,如果为了进一步处理空节点而不是现有节点是相同的(与数据库结果一样),这种一般模式应该有效:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output indent="yes" method="xml"/>  

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

    <!--
        When you don't want a leaf in your result, just:
        <xsl:template match="here the leaf name" /> 
    --> 

    <xsl:template match="*"> 
        <xsl:copy> 
            <xsl:apply-templates/> 
        </xsl:copy> 
    </xsl:template>  

    <xsl:template match="*[*/*]"> 
        <xsl:param name="prev" /> 
        <xsl:apply-templates select="*[*]">
            <xsl:with-param name="prev">
                <xsl:copy-of select="$prev"/>
                <xsl:apply-templates select="*[not(*)]"/>
            </xsl:with-param>
        </xsl:apply-templates> 
    </xsl:template>  

    <xsl:template match="*[* and not(*/*)]"> 
        <xsl:param name="prev" /> 
        <BICYCLE>
            <xsl:copy-of select="$prev"/>
            <xsl:apply-templates select="*"/>
        </BICYCLE>
    </xsl:template>  

</xsl:stylesheet> 

注意:你不必知道叶子是什么。

编辑:使用后代和祖先轴的更糟糕的例子,以及对我的提案的一些调整

Edit2 :添加一个新的可用叶子(我第一次错过了!)以便更糟糕地实现。增加了为第二次实现排除一些叶子的可能性。

编辑3 :只是重写以澄清我的观点。