Delete / Remove node from XPathNodeIterator, given an XPath
First of all, if someone has a different, perhaps shorter (or better), solution to the problem, it's welcome as well.
I'm trying to "simply" remove (almost) duplicate elements in XSLT. There's some (metadata) nodes i don't want to include when comparing, and i couldn't figure out how do do that in XSLT so thought i'd extend it with a function removing these nodes. Like so:
<xsl:for-each select="abx:removeNodes(d/df600|d/df610|d/df611|d/df630|d/df650|d/df651|d/df655, '*[@key="i1" or @key="i2" or key="db"]')">
<xsl:if test="not(node()=preceding-sibling::*)">
blah
</xsl:if>
</xsl:for-each>
And the extension, which doesn't work so well... (C#)
public XPathNodeIterator removeNodes(XPathNodeIterator p_NodeIterator, String removeXPath)
{
Logger Logger = new Logger("xslt");
Logger.Log("removeNodes(removeXPath={0}):", removeXPath);
foreach (XPathNavigator CurrentNode in p_NodeIterator)
{
Logger.Log("removeNodes(): CurrentNode.OuterXml={0}.", CurrentNode.OuterXml);
foreach (XPathNavigator CurrentSubNode in CurrentNode.Select(removeXPath))
{
Logger.Log("removeNodes(): CurrentSubNode.OuterXml={0}.", CurrentSubNode.OuterXml);
// How do i delete this node!?
//CurrentSubNode.DeleteSelf();
}
}
return p_NodeIterator;
}
My initial approach using 'CurrentSubNode.DeleteSelf();' doesn't work because it gets confused and loses its position in the XPathNavigator, causing it to only delete the first item it finds using "removeXPath". Something like a DeleteAndMoveNext() would be nice but there seems to be no such method...
Example data:
<df650>
<df6开发者_运维知识库50 key="i1"> </df650>
<df650 key="i2">0</df650>
<df650 key="a">foo</df650>
<df650 key="x">bar</df650>
<df650 key="db">someDB</df650>
<df650 key="id">b2</df650>
<df650 key="dsname">someDS</df650>
</df650>
..and then another identical node (if you ignore the meta fields; db,id,dsname).
<df650>
<df650 key="i1"> </df650>
<df650 key="i2">0</df650>
<df650 key="a">foo</df650>
<df650 key="x">bar</df650>
<df650 key="db">someOtherDB</df650>
<df650 key="id">b2</df650>
<df650 key="dsname">someOtherDS</df650>
</df650>
The result should be...
<df650>
<df650 key="i1"> </df650>
<df650 key="i2">0</df650>
<df650 key="a">foo</df650>
<df650 key="x">bar</df650>
</df650>
You can do that in XSLT alone easily, an extension function really not necessary. Consider this:
<!-- make a template that matches all nodes that cold be removed -->
<xsl:template match="d/df600|d/df610|d/df611|d/df630|d/df650|d/df651|d/df655">
<!-- check the your condition for node removal, whatever it may be -->
<xsl:if test="not(@key='i1' or @key='i2' or @key='db')">
<!-- ...if it is *not* met, copy the node -->
<xsl:copy-of select="." />
</xsl:if>
<!-- ...in all other cases, nothing happens, i.e. the node is removed -->
</xsl:template>
Thanks for the hint RymdPung, I was able to remove blank rows in a repeating section using your List suggestion.
I added a reference to the System.Collections.Generic namespace in my code.
Here is the method I created to scan through a set of nodes, identify the ones I want to remove, and then delete these in a separate loop.
private void deleteEmptyRows(string path)
{
XPathNodeIterator nodesToCheck = MainDataSource.CreateNavigator().Select(path, NamespaceManager);
List<XPathNavigator> nodesToDelete = new List<XPathNavigator>();
foreach (XPathNavigator currentItem in nodesToCheck)
if (currentItem.Value.Trim().Length == 0)
nodesToDelete.Add(currentItem);
foreach(XPathNavigator deleteMe in nodesToDelete)
deleteMe.DeleteSelf();
}
The problem can be solved like this (however, it doesn't solve MY actual problem...).
- Create a List of type XPathNavigator that will contain nodes you want to delete.
- Add the nodes to this list instead of using DeleteSelf().
- When done finding all nodes you want to delete, iterate through your List and delete the nodes. Since these nodes are Navigators, there is no issue with lost position.
I gave up on trying to paste the code in after 10 minutes...
精彩评论