XPathGenerator.cs
上传用户:hbhltzc
上传日期:2022-06-04
资源大小:1925k
文件大小:10k
源码类别:

xml/soap/webservice

开发平台:

Visual C++

  1. using System;
  2. using System.Collections;
  3. using System.Text;
  4. using System.Xml;
  5. namespace Microsoft.Xml {
  6.     /// <summary>
  7.     /// The XPathGenerator takes an XmlNode object, and an XmlNamespaceManager as follows:
  8.     /// <code>
  9.     /// <line><span style='color:teal'>XmlDocument</span> doc = <span style='color:blue'>new</span>&#160;
  10.     /// <span style='color:teal'>XmlDocument</span>();</line>
  11.     /// 
  12.     /// <line>doc.Load(<span style='color:maroon'>&quot;test.xml&quot;</span>);</line>
  13.     /// 
  14.     /// <line><span style='color:teal'>XmlNode</span> node = doc.DocumentElement.FirstChild.LastChild;</line>
  15.     /// 
  16.     /// <line>&#160;</line>
  17.     /// 
  18.     /// <line><span style='color:teal'>XmlNamespaceManager</span><span> nsmgr = <span
  19.     /// style='color:blue'>new</span>&#160;<span style='color:teal'>XmlNamespaceManager</span>(doc.NameTable);</span></line>
  20.     /// 
  21.     /// <line><span style='color:teal'>XPathGenerator</span>
  22.     ///  gen = <span style='color:blue'>new</span>&#160;<span style='color:teal'>XPathGenerator</span>();</line>
  23.     /// 
  24.     /// <line><span style='color:blue'>string</span><span >
  25.     /// xpath = gen.GetXPath(node, nsmgr);</span></line>
  26.     /// </code>
  27.     /// <line>
  28.     /// The resulting string can then be used in SelectNodes or SelectSingleNodes 
  29.     /// using the same XmlNamespaceManager and it will find the same node as follows: 
  30.     /// </line>    
  31.     /// <code>
  32.     /// <line><span style='color:teal'>XmlNode</span> found = doc.SelectSingleNode(xpath, nsmgr); </line>
  33.     /// <line><span style='color:teal'>Debug</span>.Assert(found == node);</line>
  34.     /// </code>
  35.     /// </summary>
  36.     public class XPathGenerator {
  37.         int nextPrefix;
  38.         bool useIndices;
  39.         /// <summary>
  40.         /// Construct new XPathGenerator.  
  41.         /// </summary>        
  42.         public XPathGenerator() {
  43.         }
  44.         /// <summary>
  45.         /// Construct new XPathGenerator with a flag to tell it to always specify 
  46.         /// child index positions in the resulting xpath expression.  By default
  47.         /// the XPathGenerator only generates child index positions if the child
  48.         /// is not uniquely named in the collection of children. For example, you
  49.         /// might get the xpath expression:
  50.         /// <code>
  51.         /// <line>/html/body/p[5]/span</line>
  52.         /// </code>
  53.         /// By passing true in this constructor you will get the following instead:
  54.         /// <code>
  55.         /// <line>/html[1]/body[1]/p[5]/span[1]</line>
  56.         /// </code>
  57.         /// </summary>
  58.         /// <param name="useIndices">Specify whether you want the XPathGenerator
  59.         /// to always include child index positions.  Default is false.
  60.         /// </param>
  61.         public XPathGenerator(bool useIndices) {
  62.             this.useIndices = useIndices;
  63.         }
  64.         /// <summary>
  65.         /// Return an XPath that will locate the given node within it's
  66.         /// document and populate an XmlNamespaceManager with the namespace
  67.         /// prefixes used in that XPath query.  You can then
  68.         /// use this XmlNamespaceManager in the call 
  69.         /// the SelectNodes or SelectSingleNode methods.
  70.         /// </summary>
  71.         /// <param name="node">The node to locate via XPath</param>
  72.         /// <param name="nsmgr">A namespace manager, it may be empty
  73.         /// or it may be pre-populated with the prefixes you want to use.  
  74.         /// Either way if a namespace prefix is needed that is not defined or 
  75.         /// if it conflicts with another definition
  76.         /// then a new prefix will be generated automatically.</param>
  77.         /// <returns>The XPath expression needed to locate the given node
  78.         /// or null if the node is not locatable by XPath because of it's
  79.         /// NodeType.</returns>
  80.         public string GetXPath(XmlNode node, XmlNamespaceManager nsmgr) {
  81.             if (node == null) {
  82.                 throw new System.ArgumentNullException("node");
  83.             }
  84.             if (nsmgr == null) {
  85.                 throw new System.ArgumentNullException("nsmgr");
  86.             }
  87.             nextPrefix = 0;
  88.             StringBuilder sb = new StringBuilder();
  89.             switch (node.NodeType) {
  90.                 // these node types are not accessible to XPath
  91.                 case XmlNodeType.Document:
  92.                 case XmlNodeType.DocumentType:
  93.                 case XmlNodeType.EntityReference:
  94.                 case XmlNodeType.DocumentFragment:
  95.                 case XmlNodeType.EndElement:
  96.                 case XmlNodeType.EndEntity:
  97.                 case XmlNodeType.Entity:
  98.                 case XmlNodeType.None:
  99.                 case XmlNodeType.Notation:
  100.                 case XmlNodeType.XmlDeclaration:
  101.                     return null;
  102.             }
  103.             
  104.             NodeToXPath(node, sb, nsmgr);
  105.             return sb.ToString();
  106.         }
  107.         void NodeToXPath(XmlNode node, StringBuilder sb, XmlNamespaceManager nsmgr) {
  108.             if (node != null) {
  109.                 XmlNode parent = node.ParentNode;
  110.                 if (parent == null) {
  111.                     // ParentNode doesn't work on Attributes!
  112.                     parent = node.SelectSingleNode("..");
  113.                 }
  114.                 NodeToXPath(parent, sb, nsmgr);
  115.                 string path = GetPathInParent(node, nsmgr);
  116.                 if (path != null) {
  117.                     sb.Append("/");
  118.                     sb.Append(path);
  119.                 }
  120.             }
  121.         }
  122.         string GetPathInParent(XmlNode node, XmlNamespaceManager nsmgr) {
  123.             XmlNodeType nt = node.NodeType;
  124.             if (nt == XmlNodeType.Attribute) {
  125.                 if (node.NamespaceURI == "http://www.w3.org/2000/xmlns/") {
  126.                     if (string.IsNullOrEmpty(node.Prefix) &&
  127.                         node.LocalName == "xmlns") {
  128.                         return "namespace::*[local-name()='']";// and .='" + node.Value + "']";
  129.                     } else {
  130.                         return "namespace::" + node.LocalName;
  131.                     }
  132.                 }
  133.                 // attributes are unique by definition, so no indices are
  134.                 // required.
  135.                 return string.Format("@{0}", GetQualifiedPath(node, nsmgr)); 
  136.             }
  137.                             
  138.             XmlNode parent = node.ParentNode;
  139.             if (parent != null) {
  140.                 int count = 0;
  141.                 int index = 0;
  142.                 bool wasText = false;
  143.                 XmlNode child = parent.FirstChild;
  144.                 while (child != null) {
  145.                     if (child == node) {
  146.                         index = count;
  147.                     }
  148.                     XmlNodeType ct = child.NodeType;
  149.                     if (IsTextNode(ct)) {
  150.                         if (IsTextNode(nt)) {      
  151.                             // Adjacent text nodes are merged in XPath model.
  152.                             if (!wasText) count++;                             
  153.                         }
  154.                         wasText = true;
  155.                     } else {
  156.                         wasText = false;
  157.                         if (ct == nt && child.Name == node.Name) {
  158.                             count++;
  159.                         }
  160.                     }
  161.                     child = child.NextSibling;
  162.                 }
  163.                 string selector = null;
  164.                 switch (node.NodeType) {
  165.                     case XmlNodeType.CDATA:
  166.                     case XmlNodeType.Text:
  167.                     case XmlNodeType.Whitespace:
  168.                     case XmlNodeType.SignificantWhitespace:
  169.                         selector = "text()";
  170.                         break;
  171.                     case XmlNodeType.Comment:
  172.                         selector = "comment()";
  173.                         break;
  174.                     case XmlNodeType.Element:
  175.                         selector = GetQualifiedPath(node, nsmgr);
  176.                         break;
  177.                     case XmlNodeType.ProcessingInstruction:
  178.                         selector = "processing-instruction('" + node.Name + "')";
  179.                         break;
  180.                 }
  181.                 if (!this.useIndices && count < 2) { // it's unique already, so no need for indices.
  182.                     return selector;
  183.                 }
  184.                 index++; // XPath indices are 1-based
  185.                 return string.Format("{0}[{1}]", selector, index.ToString());
  186.             } else {
  187.                 return null;
  188.             }
  189.         }
  190.         static bool IsTextNode(XmlNodeType nt) {
  191.             return nt == XmlNodeType.SignificantWhitespace ||
  192.                   nt == XmlNodeType.Whitespace ||
  193.                   nt == XmlNodeType.Text ||
  194.                   nt == XmlNodeType.CDATA ||
  195.                   nt == XmlNodeType.EntityReference;
  196.         }
  197.         string GetQualifiedPath(XmlNode node, XmlNamespaceManager nsmgr) {
  198.             string nsuri = node.NamespaceURI;
  199.             string prefix = node.Prefix;
  200.             string localName = node.LocalName;
  201.             if (!string.IsNullOrEmpty(nsuri)) {
  202.                 string p = nsmgr.LookupPrefix(nsuri);
  203.                 if (!string.IsNullOrEmpty(p)) {
  204.                     // Use previously defined prefix for this namespace.
  205.                     prefix = p;
  206.                 } else {
  207.                     string found = nsmgr.LookupNamespace(prefix);
  208.                     if (found == null && !string.IsNullOrEmpty(prefix)) {
  209.                         nsmgr.AddNamespace(prefix, nsuri);
  210.                     } else if (found != node.NamespaceURI) {
  211.                         // we have a prefix conflict, so need to invent a new 
  212.                         // prefix for this part of the query.
  213.                         int i = nextPrefix++;
  214.                         int number = (i / 26);
  215.                         char letter = Convert.ToChar('a' + i - (26 * number));
  216.                         if (number == 0) {
  217.                             prefix = letter.ToString();
  218.                         } else {
  219.                             prefix = string.Format("{0}{1}", letter, number);
  220.                         }
  221.                         nsmgr.AddNamespace(prefix, nsuri);
  222.                     }
  223.                 }
  224.                 return string.Format("{0}:{1}", prefix, localName);
  225.             }
  226.             return localName;
  227.         }
  228.     }
  229. }