2010-12-03 79 views

回答

0

我剛剛完成了這項工作。

注意:這要求在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; 

     } 

    } 

}