2017-01-16 42 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> 
相关问题