Предполагая, что вы интересуетесь только xpath элементов xml, я реализовал алгоритм грубой силы (т.е. обход структуры XML) в качестве методов расширения на XmlElement
.Это очень похоже на ответ @ Zenexer, хотя я уже начал свою собственную версию, когда он опубликовал его.
Кроме того, заинтригованный советами Алексея о производительности, я создал своего рода тестовый пример, используя несколько сложный XMLфайл лиринг здесь.Затем я реализовал две версии одного и того же алгоритма;один, который зависит от PreviousSibling, и другой, который последовательно выполняет итерации узлов.Третья версия опиралась на position()
функцию XPath, но она не работала должным образом и была отброшена.
Хотя вы должны проверить сами, на моей машине результаты показали значительное преимущество в производительности дляитеративная версия - 1,7 с против 21 с, набранных по версии братьев и сестер.
Importart: эти методы расширения объявлены внутри static class XmlElementExtension
.
PreviousSibling 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's 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;
}
Итерационная версия
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's 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;
}
Контрольный пример
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());
});
}