XSLT 2:如何在 XML-tag 的第一次出现和最后一次出现之间包装所有内容?
XSLT 2: How to wrap all content between first and last occurrence of a XML-tag?
我正在使用 XSLT 2.0 转换 XML。
重要的是,输入 XML 中的所有文本节点都包含在结果 XML 中,并且顺序与它们在输入中出现的顺序相同。对于元素节点,在大多数情况下我只想更改标签的名称,或者添加一些层次结构将某些节点包装在新节点中。
对于这个问题,我想知道如何将某个标签从(含)第一个 child 出现到(含)最后一个 "unit" 出现的所有内容视为一个 "unit" child 出现相同的标签,包括 文本和中间的其他标签。同时,我希望也能把这个选择之前的所有children当作一个单独的"unit",把这个选择之后的所有children当作另一个单独的"unit".
我已经包含了一个我想要的虚拟示例。假设 "current node" 是 <c>
,例如如果我们在 <xsl:template match="//c">
中。
我想把从第一个 <e>
节点到最后一个 <e>
节点(含)的所有内容(在 <c>
下)包装起来,包括 <f>
节点,在一个节点 <es>
。此外,我(仍然只在上下文 <c>
中)喜欢在 as-is 之前保留所有内容,但将所有内容包装在节点 <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。解决方案越简洁越好。
这是您可以查看的一种方式:
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()[. << ../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
child。
我自己解决这个问题的方法是在我看到@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 <= position() and position() <= $last-e]"/>
</es>
<after-es>
<xsl:apply-templates select="e[last()]/following-sibling::node()"/>
</after-es>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
我正在使用 XSLT 2.0 转换 XML。 重要的是,输入 XML 中的所有文本节点都包含在结果 XML 中,并且顺序与它们在输入中出现的顺序相同。对于元素节点,在大多数情况下我只想更改标签的名称,或者添加一些层次结构将某些节点包装在新节点中。
对于这个问题,我想知道如何将某个标签从(含)第一个 child 出现到(含)最后一个 "unit" 出现的所有内容视为一个 "unit" child 出现相同的标签,包括 文本和中间的其他标签。同时,我希望也能把这个选择之前的所有children当作一个单独的"unit",把这个选择之后的所有children当作另一个单独的"unit".
我已经包含了一个我想要的虚拟示例。假设 "current node" 是 <c>
,例如如果我们在 <xsl:template match="//c">
中。
我想把从第一个 <e>
节点到最后一个 <e>
节点(含)的所有内容(在 <c>
下)包装起来,包括 <f>
节点,在一个节点 <es>
。此外,我(仍然只在上下文 <c>
中)喜欢在 as-is 之前保留所有内容,但将所有内容包装在节点 <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。解决方案越简洁越好。
这是您可以查看的一种方式:
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()[. << ../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
child。
我自己解决这个问题的方法是在我看到@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 <= position() and position() <= $last-e]"/>
</es>
<after-es>
<xsl:apply-templates select="e[last()]/following-sibling::node()"/>
</after-es>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>