如何重构此XSL以利用apply-templates?

时间:2011-03-02 21:07:07

标签: xslt

考虑这个question about beautiful XSL但更具体,我应该如何重构这个XSL以利用应用模板和/或密钥。

我倾向于“过度使用”for-each元素来控制源的上下文,我可以想象apply-templates可以提供帮助。尽管有很多Google,但我仍然不明白如何在多个模板中控制上下文。

在下面的示例中,如何通过重构来减少重复的XPath段?

    <xsl:template match="/">
    <xsl:element name="Body">
        <xsl:element name="Person">
            <xsl:if     test="/source/dbSrc/srv/v[@name='name']/text()='false'">
                <xsl:element name="PhoneNumber" />
                <xsl:element name="Zip">
                    <xsl:value-of
                        select="/source/req[1]/personal-info/address-info/zip-code" />

                </xsl:element>
            </xsl:if>
            <xsl:if test="/source/dbSrc/srv/v[@name='name']/text()='true'">
                <xsl:element name="PhoneNumber" />
                <xsl:element name="Zip">
                    <xsl:value-of select="/source/req[3]/personal-info/address-info/zip-code" />
                </xsl:element>
            </xsl:if>
        </xsl:element>

</xsl:template>

2 个答案:

答案 0 :(得分:1)

重构给定代码的一种初始方法如下:

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

 <xsl:template match="/">
  <Body>
   <Person>
    <PhoneNumber/>
     <Zip>
       <xsl:apply-templates select=
        "/*/dbSrc/srv/v[@name='name']"/>
     </Zip>
   </Person>
  </Body>
 </xsl:template>

 <xsl:template match="v[@name='name' and .='true']">
   <xsl:value-of select=
   "/*/req[3]/personal-info/address-info/zip-code"/>
 </xsl:template>

 <xsl:template match="v[@name='name' and .='false']">
   <xsl:value-of select=
   "/*/req[1]/personal-info/address-info/zip-code"/>
 </xsl:template>
</xsl:stylesheet>

请注意:重构的代码不包含任何条件xslt说明。

进一步的重构可以让我们摆脱最后的模板,因为在这种情况下实际上并不需要额外的模板 - 代码只创建一个元素并依赖于一个条件:

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

 <xsl:variable name="vCond" select=
  "/*/dbSrc/srv/v[@name='name']/text()='true'"/>

 <xsl:variable name="vInd" select=
  "3*$vCond + 1*not($vCond)"/>

 <xsl:template match="/">
  <Body>
   <Person>
    <PhoneNumber/>
     <Zip>
      <xsl:value-of select=
      "/*/req[position()=$vInd]
                /personal-info/address-info/zip-code"/>
     </Zip>
   </Person>
  </Body>
 </xsl:template>
</xsl:stylesheet>

注意:我们假设/*/dbSrc/srv/v[@name='name']/text()只有两个可能的值:'true''false'

答案 1 :(得分:1)

在XSLT 2.0中,我将其写为:

<xsl:template match="/">
    <Body>
        <Person>
          <PhoneNumber/>
          <Zip>
            <xsl:variable name="index" as="xs:integer"
               select="if (/source/dbSrc/srv/v[@name='name']='true') then 3 else 1"/>
            <xsl:value-of select="/source/req[$index]/personal-info/address-info/zip-code"/>
          </Zip>
        </Person>
    </Body>
</xsl:template>

使用1.0时,xsl:变量会变得有点复杂,但不会相同。

注意使用文字结果元素和变量来减小代码的大小;也避免使用“/ text()”,这几乎总是不好的做法。

这里使用模板规则的里程很少,因为您使用的输入数据非常少,而且您似乎确切地知道在哪里找到它。如果您想要更准确地了解您在源中查找的位置,那么模板规则就会自成一体:它们有助于使代码更容易适应变化和输入的变化。但是,如果没有看到来源并了解更多背景知识,我们无法告诉您需要哪种灵活性。索引“1”和“3”的硬编码对我来说似乎是一个危险的信号,但只有你能判断出来。