Assuming you are only interested in xpath of xml elements, I applied brute force algorithm (i.e. moving XML structure) as extension methods on XmlElement . This is very similar to @Zenexer's answer, although I already started my own version when he posted it.
Also, intrigued by Alexei about performance advice, I created a kind of test case using a somewhat complex XML file here. Then I implemented two versions of the same algorithm; one that depends on PreviousSibling, and another that iterates through the nodes sequentially. The third version was based on the XPath position() function, but it did not work as expected and was dropped.
While you have to check it yourself, on my machine the results showed a significant performance advantage for the iterative version - 1.7s versus the 21st, clogged by the version of siblings.
Importart: these extension methods are declared inside the static class XmlElementExtension .
Previous Version>
public static string GetXPath_UsingPreviousSiblings(this XmlElement element) { string path = "/" + element.Name; XmlElement parentElement = element.ParentNode as XmlElement; if (parentElement != null) { // Gets the position within the parent element, based on previous siblings of the same name. // However, this position is irrelevant if the element is unique under its parent: XPathNavigator navigator = parentElement.CreateNavigator(); int count = Convert.ToInt32(navigator.Evaluate("count(" + element.Name + ")")); if (count > 1) // There more than 1 element with the same name { int position = 1; XmlElement previousSibling = element.PreviousSibling as XmlElement; while (previousSibling != null) { if (previousSibling.Name == element.Name) position++; previousSibling = previousSibling.PreviousSibling as XmlElement; } path = path + "[" + position + "]"; } // Climbing up to the parent elements: path = parentElement.GetXPath_UsingPreviousSiblings() + path; } return path; }
Iterative version
public static string GetXPath_SequentialIteration(this XmlElement element) { string path = "/" + element.Name; XmlElement parentElement = element.ParentNode as XmlElement; if (parentElement != null) { // Gets the position within the parent element. // However, this position is irrelevant if the element is unique under its parent: XmlNodeList siblings = parentElement.SelectNodes(element.Name); if (siblings != null && siblings.Count > 1) // There more than 1 element with the same name { int position = 1; foreach (XmlElement sibling in siblings) { if (sibling == element) break; position++; } path = path + "[" + position + "]"; } // Climbing up to the parent elements: path = parentElement.GetXPath_SequentialIteration() + path; } return path; }
Test case
private static void Measure(string functionName, int iterations, Action implementation) { Stopwatch watch = new Stopwatch(); watch.Start(); for (int i = 0; i < iterations; i++) { implementation(); } watch.Stop(); Console.WriteLine("{0}: {1}ms", functionName, watch.ElapsedMilliseconds); } private static void Main(string[] args) { XmlDocument doc = new XmlDocument(); doc.Load(@"location of some large and complex XML file"); string referenceXPath = "/vps/vendorProductSets/vendorProductSet/product[100]/prodName/locName"; Measure("UsingPreviousSiblings", 10000, () => { XmlElement target = doc.SelectSingleNode(referenceXPath) as XmlElement; Debug.Assert(referenceXPath == target.GetXPath_UsingPreviousSiblings()); }); Measure("SequentialIteration", 10000, () => { XmlElement target = doc.SelectSingleNode(referenceXPath) as XmlElement; Debug.Assert(referenceXPath == target.GetXPath_SequentialIteration()); }); }