dom4j(一个非常好的Java XML库)有一个非常有用的调用。在XPathNavigator对象的等价物上,您可以请求一个唯一的Xpath语句,该语句将为您提供该特定节点。如何使用.NET XML库获得此功能?
答案 0 :(得分:0)
我刚刚完成了这项工作。
注意:这要求XPathNavigator对象下面有一个XmlDocument。您可以拥有一个未包装XmlDocument的XPathNavigator对象,对于这些情况,这将无效。
要调用它,可以传入XPathNavigator对象当前所在的XmlNode。它包含在XPathNavigator.UnderlyingObject。
中此外,如果您显式设置任何名称空间,则需要传入一个字典,其中键是名称空间uri,值是前缀。底层的XmlDocument将使用原始XML中定义的命名空间,而不是加载后分配给它的命名空间(不明白为什么)。如果没有显式设置任何名称空间,则可以传递null。
完整代码位于XML Get Unique XPath
的压缩文件中using System.Collections.Generic;
using System.Text;
using System.Xml;
/// <summary>
/// For an XmlNode in an XmlDocument, can determine the XPath
///to return that specific node. Can also then determine the non-specific
/// XPath to a subsequent child node (the XPath that will return that node AND ALSO any peer nodes of the same name(s)).
/// </summary>
public class NodeLocator
{
private NodeLocator next;
private readonly XmlNode node;
private readonly Dictionary<string, string> namespaceMap;
private NodeLocator(Dictionary<string, string> namespaceMap, XmlNode node)
{
this.node = node;
this.namespaceMap = namespaceMap ?? new Dictionary<string, string>();
}
/// <summary>
/// Get the unique XPath for the passed in node.
/// </summary>
/// <param name="namespaceMap">If namespace prefixes are different from the raw XmlDocument, this dictionaru is key=uri, value=prefix.</param>
/// <param name="node">The node to get the unique XPath to.</param>
/// <returns>The unique XPath to node.</returns>
public static string GetUniqueLocation(Dictionary<string, string> namespaceMap, XmlNode node)
{
NodeLocator loc = new NodeLocator(namespaceMap, node);
while (node.ParentNode != null)
{
node = node.ParentNode;
if (node is XmlDocument)
break;
NodeLocator parentloc = new NodeLocator(namespaceMap, node);
parentloc.next = loc;
loc = parentloc;
}
return loc.Xpath(true);
}
/// <summary>
/// Get the unique XPath for the passed in node. It uses the unique XPath from the root to the parent and then non-unique XPath from the parent to the node.
/// </summary>
/// <param name="namespaceMap">If namespace prefixes are different from the raw XmlDocument, this dictionaru is key=uri, value=prefix.</param>
/// <param name="parent">The node to get the unique XPath to.</param>
/// <param name="node">The node to get the NON-unique XPath to.</param>
/// <returns>The unique XPath to node.</returns>
public static string GetLocation(Dictionary<string, string> namespaceMap, XmlNode parent, XmlNode node)
{
NodeLocator loc = new NodeLocator(namespaceMap, node);
while ((node.ParentNode != null) && (node.ParentNode != parent))
{
node = node.ParentNode;
if (node is XmlDocument)
break;
NodeLocator parentloc = new NodeLocator(namespaceMap, node);
parentloc.next = loc;
loc = parentloc;
}
return loc.Xpath(false);
}
private string Xpath(bool unique)
{
StringBuilder sb = new StringBuilder();
NodeLocator loc = this;
do
{
if (loc.node.Name.StartsWith("#"))
{
if (loc.node.Name == "#document")
sb.Append('/');
}
else
{
sb.Append('/');
if (loc.node is XmlAttribute)
sb.Append('@');
sb.Append(FullName(loc.node));
if (unique)
{
sb.Append('[');
sb.Append(loc.IndexInParent);
sb.Append(']');
}
}
loc = loc.next;
} while (loc != null);
// no leading / for non-unique
if ((!unique) && (sb.Length > 0))
sb.Remove(0, 1);
return sb.ToString();
}
private string FullName(XmlNode _node)
{
if (string.IsNullOrEmpty(_node.NamespaceURI) || (!namespaceMap.ContainsKey(_node.NamespaceURI)))
return _node.Name;
return namespaceMap[_node.NamespaceURI] + ':' + _node.LocalName;
}
private int IndexInParent
{
get
{
int indexInParent = 1;
XmlNode parent = node.ParentNode;
string nodeName = FullName(node);
if (parent != null)
{
foreach (XmlNode child in parent.ChildNodes)
{
if (child == node)
break;
if (FullName(child) == nodeName)
{
indexInParent++;
}
}
}
return indexInParent;
}
}
}