避免使用XSLT 1.0在XSLT模板规则中重复

时间:2017-05-30 13:58:07

标签: xslt xslt-1.0

给出以下XML文档:

<document>
  <a>123</a>
  <foo_1>true</foo_1>
  <foo_2>false</foo_2>
  <foo_3>true</foo_3>
  <foo_4>true</foo_4>
  <foo_5>false</foo_5>
  <b/>
  <bar_1>false</bar_1>
  <bar_2>false</bar_2>
  <bar_3>true</bar_3>
  <bar_4>false</bar_4>
  <bar_5>true</bar_5>
  <c>some text</c>
</document>

我希望通过删除包含false的所有枚举元素并将包含true的所有枚举元素转换为<prefix_n>value</prefix_n>形式来转换此文档,其中value是数字在下划线之后。

对于上面给出的示例,结果应如下所示:

<document>
  <a>123</a>
  <foo_n>1</foo_n>
  <foo_n>3</foo_n>
  <foo_n>4</foo_n>
  <b/>
  <bar_n>3</bar_n>
  <bar_n>5</bar_n>
  <c>some text</c>
</document>

为此,我使用以下转换,工作正常。

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

  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>

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

  <xsl:template match="*[substring(name(), 1, 4) = 'foo_']">
    <xsl:if test=". = 1 or . = 'true'">
      <xsl:element name="foo_n">
        <xsl:value-of select="substring-after(name(), 'foo_')"/>
      </xsl:element>
    </xsl:if>
  </xsl:template>

  <xsl:template match="*[substring(name(), 1, 4) = 'bar_']">
    <xsl:if test=". = 1 or . = 'true'">
      <xsl:element name="bar_n">
        <xsl:value-of select="substring-after(name(), 'bar_')"/>
      </xsl:element>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

现在我面临的问题是我必须处理多种不同的前缀,而不仅仅是foo_bar_

有没有办法将模板规则转换为命名模板,我可以将前缀作为参数传递,从而避免编写大量重复的模板规则?

2 个答案:

答案 0 :(得分:2)

这是你可以看到它的一种方式:

XSLT 1.0

<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:strip-space elements="*"/>

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

<xsl:template match="*[starts-with(name(), 'foo_') or starts-with(name(), 'bar_')]">
    <xsl:if test=". = 1 or . = 'true'">
        <xsl:element name="{substring-before(name(), '_')}-n">
            <xsl:value-of select="substring-after(name(), '_')"/>
        </xsl:element>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

这是另一个:

XSLT 1.0

<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:strip-space elements="*"/>

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

<xsl:template match="*[contains(translate(name(), '123456789', '000000000'), '_0')]">
    <xsl:if test=". = 1 or . = 'true'">
        <xsl:variable name="prefix" select="substring-before(translate(name(), '123456789', '000000000'), '_0')" />
        <xsl:element name="{$prefix}-n">
            <xsl:value-of select="substring(name(), string-length($prefix) + 2)"/>
        </xsl:element>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

最后一个应该使用任何前缀后跟_[1-9] - 包括包含额外下划线的前缀,并包含包含其他前缀的前缀。

答案 1 :(得分:0)

  

有没有办法将模板规则转换为命名模板,我可以将前缀作为参数传递

嗯,当然可以:

<xsl:template name="if-true">
  <xsl:param name="prefix"/>
  <xsl:if test=". = 1 or . = 'true'">
    <xsl:element name="{concat($prefix, 'n')}">
      <xsl:value-of select="substring-after(name(), $prefix)"/>
    </xsl:element>
  </xsl:if>
</xsl:template>

<!-- example using the above: -->
<xsl:template match="*[starts-with(name(), 'foo_')]">
  <xsl:call-template name="if-true">
    <xsl:with-param name="prefix" select="'foo_'"/>
  </xsl:call-template>
</xsl:template>

但目前尚不清楚这是否真的能实现你的目标:

  

,从而避免编写大量重复的模板规则?

因为您仍然需要一堆第二种模板来匹配您想要转换的特定元素。 @ michael.hor257k提出的解决方案建议,通过使相同的模板匹配您想要转换的所有元素来避免重复,是一个更好的选择。