Given a deeply nested XML tree, I want to find a certain element. At that point I want to wrap X in a new element that is at the same level as a higher element. I then want to continue on with the rest of the original tree from the point after the "certain" element.
For example, given this input:
<root>
<branch att="yo">
<div stuff="no">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</branch>
</root>
I want to find Item 2 in the <ul> (easy peasy). I want to insert a new branch-level element before item 2. And then I want to continue on with item 2 (so continuing the ancestor nodes). That is, I want this output:
<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>
I am having a hard problem getting started with this in order to make a generalized solution. I'm thinking it will involve either multiple modes or keys along with the processing of ancestor nodes to find node names and attributes. Any help is appreciated.
Alright this is what I have so far. It partially works (e.g., I copy some nodes but no attributes; I copy too many nodes as well but it is a start I think). My thinking here is that I need a recursive function. I start at the furthest ancestor I'm concerned with (branch) and for each child node whose ultimate descendant is the certain element (li "item 2"), I copy each node and its attributes, appending it to the previous node (that's what newNewTree is for). And I recurse until I reach the certain element (li's Item 2), at which point I return the NewNewTree variable, which my intent is for it to be a tree of the right ancestor elements (with no text). To make this work I think what I need to do is process each node from the function through an identity template override that copies the node and its attributes. But too tired to attempt that tonight.
<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>