2017-01-16 78 views
0

我正在使用XSLT 2.0轉換XML。 重要的是,輸入XML中的所有文本節點都包含在生成的XML中,並且與輸入中發生的順序相同。對於元素節點,在大多數情況下,我只想更改標籤的名稱,或者添加一些層次來包裝新節點中的某些節點。對於這個問題,我想知道如何將一個標籤的第一個孩子出現的所有內容作爲一個「單位」對待,直到(包括)同一標籤的最後一個孩子出現, 包括之間的文字和其他標籤。同時,我希望能夠把所有選擇之前的孩子作爲一個獨立的「單位」,所有的孩子都將這一選擇作爲另一個獨立的「單位」。XSLT 2:如何在XML標籤的第一次和最後一次出現之間包裝所有內容?

我已經包含了我想要的虛擬示例。假設「當前節點」是<c>,例如,如果我們在<xsl:template match="//c">。 我想在節點<es>中包含從第一個<e>節點到最後一個<e>節點(含)(包括<f>節點)的所有內容(在<c>之下)。此外,我還會(仍然只在<c>的範圍內)喜歡在此之前保留所有內容,但將所有內容都包含在節點<after-es>之後。我希望在<c>以外沒有任何副作用,例如將內容移入或移出<c>節點。

輸入XML:

<a> 
    alfred 
    <b>bob</b> 
    charlie 
    <c> 
    dover 
    <d>elon</d> 
    fabio 
    <e>grant</e> 
    hugh 
    <f>illinois</f> 
    jacob 
    <e>kathy</e> 
    lombard 
    <e> 
     moby 
     <g>narwhal</g> 
     obi-wan 
    </e> 
    pontiac 
    <h>quantas</h> 
    rhino 
    </c> 
    xenu 
    <z>yoga</z> 
    zombie 
</a> 

預期輸出XML:

<a> 
    alfred 
    <b>bob</b> 
    charlie 
    <c> 
    dover 
    <d>elon</d> 
    fabio 
    <es> 
     <e>grant</e> 
     hugh 
     <f>illinois</f> 
     jacob 
     <e>kathy</e> 
     lombard 
     <e> 
     moby 
     <g>narwhal</g> 
     obi-wan 
     </e> 
    </es> 
    <after-es> 
     pontiac 
     <h>quantas</h> 
     rhino 
    </after-es> 
    </c> 
    xenu 
    <z>yoga</z> 
    zombie 
</a> 

如何才能做到這一點?最好使用XSLT 2.0。整潔的解決方案越好。

+0

如果在某個'c'元素中只有一個'e'子元素,那麼您想要包裝哪些元素呢? –

+0

@MartinHonnen如果'C'節點只有一個孩子,這個孩子是一個'e',那麼我會希望能像'... ... ...'。 如果沒有內容,是否包含'after-es'節點並不重要,但最好不要。 –

+0

我問eg' 巴茲 ... foobar的 ..',所以只有一個'e'的孩子,但也有其他子節點,也有一些下面'e',你想只包裹'e'元素還是'e'元素後面的節點? –

回答

2

這裏你可以看看它的一種方法:

XSLT 2.0

<xsl:stylesheet version="2.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="c"> 
    <xsl:variable name="nodes-before" select="node()[. &lt;&lt; ../e[1]]"/> 
    <xsl:variable name="nodes-after" select="node()[. >> ../e[last()]]"/> 
    <xsl:copy> 
     <xsl:apply-templates select="$nodes-before"/> 
     <es> 
      <xsl:apply-templates select="node() except ($nodes-before|$nodes-after)"/> 
     </es> 
     <xsl:if test="$nodes-after"> 
      <after-es> 
       <xsl:apply-templates select="$nodes-after"/> 
      </after-es> 
     </xsl:if> 
    </xsl:copy> 
</xsl:template> 

</xsl:stylesheet> 

注意,有一個潛在的假設,這裏c至少有一個e孩子。

+0

美麗,只是我希望的解決方案!比我用'position()'和'count()'這個可怕的組合來解決問題的方法要好得多。 –

0

我自己解決這個問題,在我看到@ michael.hor257k的答案之前就開始了。

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

    <xsl:output omit-xml-declaration="yes" /> 

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

    <xsl:template match="c"> 
    <xsl:variable name="first-e" select="count(e[  1]/preceding-sibling::node()) + 1"/> 
    <xsl:variable name="last-e" select="count(e[last()]/preceding-sibling::node()) + 1"/> 

    <xsl:copy> 
     <xsl:apply-templates select="e[1]/preceding-sibling::node()"/> 
     <es> 
     <xsl:apply-templates select="node()[$first-e &lt;= position() and position() &lt;= $last-e]"/> 
     </es> 
     <after-es> 
     <xsl:apply-templates select="e[last()]/following-sibling::node()"/> 
     </after-es> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 
相關問題