2017-05-30 81 views
0

鉴于下面的XML文档:避免重复使用XSLT 1.0

<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_

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

+1

你想(或需要)列出所有可能的前缀吗? '_'分隔符的存在不够吗? –

+0

不幸的不是。我在文档中还有其他元素,例如'a_b'。但是所有需要转换的元素的名称(只有那些)以正则表达式'_ [0-9] +'结尾,如果这有帮助的话。 – Markus

回答

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] - 包括前缀包含一个额外的下划线和包含其他前缀的前缀。

+0

谢谢。但AFAICT这不适用于像'foo_bar_1'这样的元素? – Markus

+0

你自己的代码 - 你说的工作正常 - 不会为'foo_bar_1'工作。有可能*使它工作(有一些努力,因为在XSLT 1.0中没有正则表达式)。 –

+0

对不起,我不明白为什么我的代码不应该用于前缀'foo_bar_'。我刚刚尝试过,当我添加缺少的模板规则时,它似乎工作正常。 – Markus

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> 

但目前还不清楚是否真的能达到你的目的:

,从而避免写很多重复模板r ULES?

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

+0

比我的方法更好;至少我必须指定前缀只有两次而不是三次。 (当然,我应该从头开始使用'starts-with()')。但是实际上,我希望能够只列出一次前缀。 – Markus