2010-12-03 45 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; 

     } 

    } 

}