dom4j(一个非常好的 Java XML 库)有一个非常有用的调用。在其 XPathNavigator 对象的等效项上,您可以请求一个唯一的 Xpath 语句,该语句将为您提供该特定节点。如何使用 .NET XML 库获得此信息?
问问题
1774 次
1 回答
0
我刚刚解决了这个问题。
注意:这要求在 XPathNavigator 对象下有一个 XmlDocument。您可以有一个不包装 XmlDocument 的 XPathNavigator 对象,对于这些情况,这将不起作用。
要调用它,请传入 XPathNavigator 对象当前所在的 XmlNode。它包含在 XPathNavigator.UnderlyingObject 中。
此外,如果您明确设置任何命名空间,则需要传入一个字典,其中键是命名空间 uri,值是前缀。底层 XmlDocument 将使用原始 XML 中定义的命名空间,而不是加载后分配给它的命名空间(不知道为什么)。如果您没有明确设置任何命名空间,则可以传递 null。
完整代码位于XML Get Unique XPath的 zip 中
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;
}
}
}
于 2010-12-03T23:34:35.130 回答