Как я могу получить 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; 

        } 

    } 

}
Другие вопросы по тегам