Using Xpath With Default Namespace in C#

前端 未结 13 1484
别那么骄傲
别那么骄傲 2020-11-22 03:02

I\'ve got an XML document with a default namespace. I\'m using a XPathNavigator to select a set of nodes using Xpath as follows:

XmlElement myXML = ...;           


        
相关标签:
13条回答
  • 2020-11-22 03:56

    In case the namespaces differ for outerelement and innerelement

    XmlNamespaceManager manager = new XmlNamespaceManager(myXmlDocument.NameTable);
                                manager.AddNamespace("o", "namespaceforOuterElement");
                                manager.AddNamespace("i", "namespaceforInnerElement");
    string xpath = @"/o:outerelement/i:innerelement"
    // For single node value selection
    XPathExpression xPathExpression = navigator.Compile(xpath );
    string reportID = myXmlDocument.SelectSingleNode(xPathExpression.Expression, manager).InnerText;
    
    // For multiple node selection
    XmlNodeList myNodeList= myXmlDocument.SelectNodes(xpath, manager);
    
    0 讨论(0)
  • 2020-11-22 03:59

    I encountered a similar problem with a blank default namespace. In this example XML, I have a mix of elements with namespace prefixes, and a single element (DataBlock) without:

    <src:SRCExample xmlns="urn:some:stuff:here" xmlns:src="www.test.com/src" xmlns:a="www.test.com/a" xmlns:b="www.test.com/b">
     <DataBlock>
      <a:DocID>
       <a:IdID>7</a:IdID>
      </a:DocID>
      <b:Supplimental>
       <b:Data1>Value</b:Data1>
       <b:Data2/>
       <b:Extra1>
        <b:More1>Value</b:More1>
       </b:Extra1>
      </b:Supplimental>
     </DataBlock>
    </src:SRCExample>
    

    I attempted to use an XPath that worked in XPath Visualizer, but did not work in my code:

      XmlDocument doc = new XmlDocument();
      doc.Load( textBox1.Text );
      XPathNavigator nav = doc.DocumentElement.CreateNavigator();
      XmlNamespaceManager nsman = new XmlNamespaceManager( nav.NameTable );
      foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
        nsman.AddNamespace( nskvp.Key, nskvp.Value );
      }
    
      XPathNodeIterator nodes;
    
      XPathExpression failingexpr = XPathExpression.Compile( "/src:SRCExample/DataBlock/a:DocID/a:IdID" );
      failingexpr.SetContext( nsman );
      nodes = nav.Select( failingexpr );
      while ( nodes.MoveNext() ) {
        string testvalue = nodes.Current.Value;
      }
    

    I narrowed it down to the "DataBlock" element of the XPath, but couldn't make it work except by simply wildcarding the DataBlock element:

      XPathExpression workingexpr = XPathExpression.Compile( "/src:SRCExample/*/a:DocID/a:IdID" );
      failingexpr.SetContext( nsman );
      nodes = nav.Select( failingexpr );
      while ( nodes.MoveNext() ) {
        string testvalue = nodes.Current.Value;
      }
    

    After much headscratching and googling (which landed me here) I decided to tackle the default namespace directly in my XmlNamespaceManager loader by changing it to:

      foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
        nsman.AddNamespace( nskvp.Key, nskvp.Value );
        if ( nskvp.Key == "" ) {
          nsman.AddNamespace( "default", nskvp.Value );
        }
      }
    

    So now "default" and "" point to the same namespace. Once I did this, the XPath "/src:SRCExample/default:DataBlock/a:DocID/a:IdID" returned my results just like I wanted. Hopefully this helps to clarify the issue for others.

    0 讨论(0)
  • 2020-11-22 04:01

    When using XPath in .NET (via a navigator or SelectNodes/SelectSingleNode) on XML with namespaces you need to:

    • provide your own XmlNamespaceManager

    • and explicitly prefix all elements in XPath expression, which are in namespace.

    The latter is (paraphrased from MS source linked below): because XPath 1.0 ignores default namespace specifications (xmlns="some_namespace"). So when you use element name without prefix it assumes null namespace.

    That's why .NET implementation of XPath ignores namespace with prefix String.Empty in XmlNamespaceManager and allways uses null namespace.

    See XmlNamespaceManager and UndefinedXsltContext don't handle default namespace for more information.

    I find this "feature" very inconvenient because you cannot make old XPath namespace-aware by simply adding default namespace declaration, but that's how it works.

    0 讨论(0)
  • 2020-11-22 04:02

    For anyone looking for a quick hack solution, especially in those cases where you know the XML and don't need to worry about namespaces and all that, you can get around this annoying little "feature" by simply reading the file to a string and replacing the offensive attribute:

    XmlDocument doc = new XmlDocument();
    string fileData = File.ReadAllText(fileName);
    fileData = fileData.Replace(" xmlns=\"", " whocares=\"");
    using (StringReader sr = new StringReader(fileData))
    {
       doc.Load(sr);
    }
    
    XmlNodeList nodeList = doc.SelectNodes("project/property");
    

    I find this easier than all the other non-sense requiring a prefix for a default namespace when I'm dealing with a single file. Hope this helps.

    0 讨论(0)
  • 2020-11-22 04:02

    1] If you have a XML file without any prefix in the namespace:

    <bookstore xmlns="http://www.contoso.com/books">
    …
    </bookstore>
    

    you have this workaround:

    XmlTextReader reader = new XmlTextReader(@"C:\Temp\books.xml");
    // ignore the namespace as there is a single default namespace:
    reader.Namespaces = false;
    XPathDocument document = new XPathDocument(reader);
    XPathNavigator navigator = document.CreateNavigator();
    XPathNodeIterator nodes = navigator.Select("//book");
    

    2] If you have a XML file with a prefix in the namespace:

    <bookstore xmlns:ns="http://www.contoso.com/books">
    …
    </bookstore>
    

    Use this:

    XmlTextReader reader = new XmlTextReader(@"C:\Temp\books.xml");
    XPathDocument document = new XPathDocument(reader);
    XPathNavigator navigator = document.CreateNavigator();
    XPathNodeIterator nodes = navigator.Select("//book");
    

    Of course, you can use a namespace manage if needed:

    XmlTextReader reader = new XmlTextReader(@"C:\Temp\books.xml");
    XPathDocument document = new XPathDocument(reader);
    XPathNavigator navigator = document.CreateNavigator();
    XmlNamespaceManager nsmgr = new XmlNamespaceManager(reader.NameTable);
    nsmgr.AddNamespace("ns", "http://www.contoso.com/book");
    XPathNodeIterator nodes = navigator.Select("//book", nsmgr);
    

    I think that it's the easiest way to make the code working in the most cases.

    I hope this help to solve this Microsoft issue…

    0 讨论(0)
  • 2020-11-22 04:03

    You might want to try an XPath Visualizer tool to help you through.

    XPathVisualizer is free, easy to use.

    IMPORTANT: If you are using Windows 7/8 and don't see File, Edit and Help Menu items, please press ALT key.

    0 讨论(0)
提交回复
热议问题