XSLT 2:如何在XML标记的第一次和最后一次出现之间包装所有内容?

时间:2017-01-16 11:46:15

标签: xml xslt xpath xslt-2.0 xpath-2.0

我正在使用XSLT 2.0转换XML。 重要的是,输入XML中的所有文本节点都包含在生成的XML中,并且与输入中出现的顺序相同。对于元素节点,在大多数情况下,我只想更改标记的名称,或者在新节点中包装某些节点时添加一些层次结构。

对于这个问题,我想知道如何将某个标签的第一个子事件(包括)中的所有内容视为一个“单元”,直到(包括)相同标签的最后一个子事件,< em>包括文本和其他标签。与此同时,我希望能够将此次选拔之前的所有儿童视为一个单独的“单位”,并将所有选择的儿童作为另一个单独的“单位”。

我已经包含了一个我想要的虚拟例子。假设“当前节点”是<c>,例如如果我们在<xsl:template match="//c">。 我想在节点{{}中将所有内容(在<c>下)从第一个<e>节点包装到最后一个<e>节点(包括),包括<f>节点1}}。我还希望(仍然只在上下文<es>中)将所有内容保留为原样,但将所有内容包装在节点<c>之后。我希望在<after-es>之外没有任何副作用,例如将内容移入或移出<c>节点。

输入XML:

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

如何做到这一点?最好使用XSLT 2.0。解决方案越简洁越好。

2 个答案:

答案 0 :(得分: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孩子。

答案 1 :(得分: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>