Как я могу получить XPath для XmlNode, в котором включен мой XPathNavigator?
dom4j (действительно хорошая библиотека Java XML) имеет очень полезный вызов. В эквиваленте их объекта XPathNavigator вы можете запросить уникальный оператор Xpath, который даст вам этот конкретный узел. Как я могу получить это с помощью библиотеки.NET XML?
1 ответ
Я только что проработал это до конца.
Примечание. Для этого требуется наличие XmlDocument, лежащего в основе объекта XPathNavigator. У вас может быть объект XPathNavigator, который не содержит XmlDocument, и в этих случаях это не будет работать.
Чтобы вызвать это, вы передаете XmlNode, в котором сейчас находится объект XPathNavigator. Это содержится в XPathNavigator.UnderlyingObject.
Кроме того, если вы явно задаете какие-либо пространства имен, вам нужно передать в словарь, где ключ - это пространство имен uri, а значение - префикс. Базовый XmlDocument будет использовать пространства имен, определенные в исходном XML, а не пространства имен, назначенные ему после загрузки (понятия не имею, почему). Вы можете передать значение null, если вы явно не задали никаких пространств имен.
Полный код в формате zip на 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;
}
}
}