2017-05-30 102 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