1
dom4j(一個非常好的Java XML庫)有一個非常有用的調用。相當於它們的XPathNavigator對象,您可以請求一個唯一的Xpath語句,該語句將爲您提供該特定節點。我怎樣才能得到這個使用.NET XML庫?如何獲取XPathNavigator所在的XmlNode的XPath?
dom4j(一個非常好的Java XML庫)有一個非常有用的調用。相當於它們的XPathNavigator對象,您可以請求一個唯一的Xpath語句,該語句將爲您提供該特定節點。我怎樣才能得到這個使用.NET XML庫?如何獲取XPathNavigator所在的XmlNode的XPath?
我剛剛完成了這項工作。
注意:這要求在XPathNavigator對象下有一個XmlDocument。您可以擁有一個不包裝XmlDocument的XPathNavigator對象,對於這種情況,這不起作用。
要調用此函數,您需要傳遞XPathNavigator對象所在的XmlNode。這包含在XPathNavigator.UnderlyingObject中。
此外,如果您明確設置了任何名稱空間,則需要將其傳遞到Dictionary中,其中鍵是命名空間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;
}
}
}