如何将XPath带到XmlNode我的XPathNavigator?

时间:2010-12-03 23:32:07

标签: .net xpath xpathnavigator

dom4j(一个非常好的Java XML库)有一个非常有用的调用。在XPathNavigator对象的等价物上,您可以请求一个唯一的Xpath语句,该语句将为您提供该特定节点。如何使用.NET XML库获得此功能?

1 个答案:

答案 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; 

        } 

    } 

}