给定一个深度嵌套的 XML 树,我想找到某个元素。那时我想将 X 包装在一个与更高元素处于同一级别的新元素中。然后我想从“某些”元素之后的点继续处理原始树的其余部分。
例如,给定这个输入:
<root>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</branch>
</root>
我想在 <ul> (easy peasy) 中找到第 2 项。我想在第 2 项之前插入一个新的分支级元素。然后我想继续第 2 项(因此继续祖先节点)。也就是说,我想要这个输出:
<root>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 1</li>
</ul>
</div>
</branch>
<branch>
<div>
<p>New branch here</p>
</div>
</branch>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</branch>
</root>
为了制定一个通用的解决方案,我在开始时遇到了一个难题。我认为它将涉及多种模式或键以及祖先节点的处理以查找节点名称和属性。任何帮助表示赞赏。
好吧,这就是我到目前为止所拥有的。它部分有效(例如,我复制了一些节点但没有属性;我也复制了太多节点,但我认为这是一个开始)。我的想法是我需要一个递归函数。我从我关心的最远的祖先(分支)开始,对于最终后代是某个元素(li“item 2”)的每个子节点,我复制每个节点及其属性,将其附加到前一个节点(即newNewTree 的用途)。然后我递归直到到达某个元素(li 的第 2 项),此时我返回 NewNewTree 变量,我的意图是让它成为正确的祖先元素的树(没有文本)。为了完成这项工作,我认为我需要做的是通过复制节点及其属性的标识模板覆盖来处理函数中的每个节点。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="http://www.example.com"
exclude-result-prefixes="xs my"
version="2.0">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:sequence select="$origNodesNoText" />
</xsl:template>
<xsl:variable name="ancestorElemName" select="'div'" />
<xsl:variable name="origNodesNoText">
<xsl:apply-templates select="/" mode="origNodesNoText"/>
</xsl:variable>
<xsl:template match="text()" mode="origNodesNoText"/>
<xsl:template match="li[.='Item 2']" mode="origNodesNoText">
<xsl:variable name="newTree">
<xsl:element name="{local-name(ancestor::*[local-name()=$ancestorElemName][1])}">
<xsl:for-each select="ancestor::*[local-name()=$ancestorElemName][1]/attribute::*">
<xsl:attribute name="{local-name()}" select="."/>
</xsl:for-each>
</xsl:element>
</xsl:variable>
<xsl:sequence select="my:split(ancestor::*[local-name()=$ancestorElemName][1],.,$newTree)" />
</xsl:template>
<xsl:function name="my:split">
<xsl:param name="ancestorElemNode" />
<xsl:param name="callingNode" />
<xsl:param name="newTree" />
<xsl:message>Calling my split</xsl:message>
<xsl:choose>
<xsl:when test="$ancestorElemNode ne $callingNode">
<xsl:message>Found a node to copy its name and attributes</xsl:message>
<xsl:variable name="newNewTree">
<xsl:for-each select="$newTree/node()">
<xsl:copy /> <!-- doesn't copy attribute nodes so not what we want -->
</xsl:for-each>
<xsl:element name="{local-name($ancestorElemNode)}">
<xsl:for-each select="$ancestorElemNode/attribute::*">
<xsl:attribute name="{local-name()}" select="."/>
</xsl:for-each>
</xsl:element>
</xsl:variable>
<xsl:sequence select="my:split($ancestorElemNode/child::*[descendant::*[. eq $callingNode]][1],$callingNode,$newNewTree)" />
</xsl:when>
<xsl:otherwise>
<xsl:message>Found the end point</xsl:message>
<xsl:sequence select="$newTree" />
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</xsl:stylesheet>