如果我XPathNavigator在一个节点上有一个定位,我如何从根目录获得一个表示该节点路径的XPath表达式?
例如,如果XML是:
<data>
<class name='dogs'>
<item name='doberman />
<item name='husky' />
</class>
<class name='cats'>
<item name='persian' />
<item name='tabby' />
</class> </data>
</data>
Run Code Online (Sandbox Code Playgroud)
......那么波斯猫的路径可以表示为 /data/class[2]/item[1]
我可以枚举相关节点的祖先SelectAncestors()(或者我可以迭代地爬上父亲关系SelectParent()),但这并不能获得位置信息.
我是否必须position()为每个祖先评估使用XPath ,还是有更好的方法来执行此操作?
假设你只对xml元素的xpath感兴趣,我实现了一个强力算法(即遍历XML结构)作为扩展方法XmlElement.这与@ Zenexer的回答非常相似,尽管我在发布他的时候已经开始使用我自己的版本了.
另外,对于Alexei关于性能的提示感兴趣,我使用一个复杂的XML文件创建了一种测试用例.然后我实现了相同算法的两个版本; 一个依赖于PreviousSibling,另一个依赖迭代节点.第三个版本依赖于XPath的position()功能,但它没有按预期工作并被丢弃.
虽然您应该检查自己,但在我的机器中,结果显示迭代版本具有显着的性能优势 - 兄弟姐妹版本对21s进行了1.7s.
Importart:这些扩展方法在a中声明static class XmlElementExtension.
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;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
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());
});
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
17631 次 |
| 最近记录: |