Can't get XmlDocument.SelectNodes to retrieve any of my nodes?

36,889

Solution 1

You have to use xml namespace specifically to select them. consider

"//*[local-name()='Applications']/*[local-name()='Application']"    

in your case this code may also work well:

var doc = new XmlDocument();
doc.LoadXml(xml);
var nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("a", "http://schemas.microsoft.com/appx/2010/manifest");
var nodes = doc.SelectNodes("//a:Applications/a:Application",nsmgr);

Solution 2

You need to specify prefixes for namespaces in NamespaceManager and XPaths. Note that prefixes does not need to match anything except between your XPath and your namespace manager*.

var xml=new XmlDocument();
xml.Load(myfile);
var mgr=new XmlNamespaceManager(xml.NameTable);
mgr.AddNamespace("a", "http://schemas.microsoft.com/appx/2010/manifest");
mgr.AddNamespace("bar", "http://schemas.microsoft.com/developer/appx/2012/build");
var nodes=xml.SelectNodes("//a:Applications", mgr);

And as pointed out by other answers XPath that accepts any namespace is another option.

*) I.e. in your particular sample there are 2 namespaces "default" (note that default prefix is not the same as empty namespace) and "build". So when you define your namespace manager you need to specify a prefix for each of the namespace (if you need to target nodes in both), but prefixes can be arbitrary strings (valid for prefixes but not empty). I.e. use "a" for "default" namespace and "bar" for namespace that mapped to "build" in the XML.

Solution 3

Not in this particular case, but in general, if the namespace URN in the actual XML is not exactly the same as one used to add a namespace to a namespace manager (example: missing a trailing slash), and a prefix is specified in XPath, the query may return null.

If namespace URN in the XML is not reliable, syntax

"//*[local-name()='tag']" 

will work.

Solution 4

You might need to read this

Here's your code:

var xml = new XmlDocument();
xml.Load("myXMLFile1.xml");
var mgr = new XmlNamespaceManager(xml.NameTable);
mgr.AddNamespace("", "http://schemas.microsoft.com/appx/2010/manifest");
XmlNode root = xml.DocumentElement;
var nodes = root.SelectNodes("//*[local-name()='Applications']/*[local-name()='Application']");
Share:
36,889
Earlz
Author by

Earlz

Hello there! My name's Jordan Earls, but most people online know me as "earlz". I'm the lead developer and a co-founder of the Qtum project which brings the Ethereum Virtual Machine (ie, the thing that makes Solidity contracts function) to a UTXO based blockchain similar to Bitcoin. I've been programming since I was 13 and am completely self-taught. Low-level code like assembly and pointer arithmetic is the fun stuff for me. I also make music when I have time even though it's usually awful. Most of my personal projects are open source and BSD licensed. The majority of them are at bitbucket with the rest of them being listed on github Also, you can follow me on the twitters @earlzdotnet

Updated on July 05, 2022

Comments

  • Earlz
    Earlz almost 2 years

    I'm trying to parse an XML document. The document in question is an AppxManifest file.

    An example document looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:build="http://schemas.microsoft.com/developer/appx/2012/build" IgnorableNamespaces="build">
      <Identity Name="uytury" Publisher="hygj" Version="1.0.0.12" ProcessorArchitecture="neutral" />
      <Properties>
        <DisplayName>jhjj</DisplayName>
        <PublisherDisplayName>bhhjb</PublisherDisplayName>
        <Logo>Assets\StoreLogo.png</Logo>
      </Properties>
      <Prerequisites>
        <OSMinVersion>6.2.1</OSMinVersion>
        <OSMaxVersionTested>6.2.1</OSMaxVersionTested>
      </Prerequisites>
      <Resources>
        <Resource Language="EN" />
      </Resources>
      <Applications>
        <Application Id="App" Executable="gfg.exe" EntryPoint="gfg.App">
          <VisualElements DisplayName="fdsf" Logo="Assets\Logo.png" SmallLogo="Assets\SmallLogo.png" Description="gfdsg" ForegroundText="light" BackgroundColor="#2672EC">
            <DefaultTile ShowName="allLogos" WideLogo="Assets\WideLogo.png" ShortName="gfdsg" />
            <SplashScreen Image="Assets\SplashScreen.png" BackgroundColor="#2672EC" />
            <InitialRotationPreference>
              <Rotation Preference="portrait" />
              <Rotation Preference="landscape" />
              <Rotation Preference="portraitFlipped" />
              <Rotation Preference="landscapeFlipped" />
            </InitialRotationPreference>
          </VisualElements>
          <Extensions>
            <Extension Category="windows.search" />
            <Extension Category="windows.shareTarget">
              <ShareTarget>
                <DataFormat>Text</DataFormat>
              </ShareTarget>
            </Extension>
          </Extensions>
        </Application>
      </Applications>
      <build:Metadata>
        <build:Item Name="TargetFrameworkMoniker" Value=".NETCore,Version=v4.5" />
        <build:Item Name="VisualStudio" Version="11.0" />
        <build:Item Name="OperatingSystem" Version="6.2.9200.16384 (win8_rtm.120725-1247)" />
        <build:Item Name="Microsoft.Build.AppxPackage.dll" Version="11.0.50727.1" />
        <build:Item Name="Microsoft.Windows.UI.Xaml.Build.Tasks.dll" Version="11.0.50727.1" />
      </build:Metadata>
    </Package>
    

    I try to parse it like so:

    var xml=new XmlDocument();
    xml.Load(myfile);
    var mgr=new XmlNamespaceManager(xml.NameTable);
    mgr.AddNamespace("", "http://schemas.microsoft.com/appx/2010/manifest");
    var nodes=xml.SelectNodes("Applications");
    

    However, after I execute this, nodes will never contain anything. The xml document is loaded and such though. using SelectNodes("//*") returns every node as expected. What is my problem here?

    I've also tried many variations on that XPath query such as

    • /Package/Applications/Application
    • Applications/Application
    • Applications/*

    Nothing appears to retrieve the single node though. Ideally, I'd like for nodes to contain all of the Application nodes

  • Earlz
    Earlz over 11 years
    For the first one I get "expression must evaluate to a node-set"
  • Earlz
    Earlz over 11 years
    Ah, that works now. By chance is there a cleaner way of doing this? I've never dealt with namespaces with XPath so I'm use to something simple and clean like /Applications/Application just working
  • aiodintsov
    aiodintsov over 11 years
    I had to use it couple months ago and was not able to find a cleaner syntax, but the XPath 2.0 "//*:name" which is not supported by .net framework system libs. you can find a 3rd party XPath 2.0 and XSTL 2.0 products though.
  • Earlz
    Earlz over 11 years
    I did read that :) I just didn't realize it'd change my XPath query so radically compared to an XML file without a namespace
  • aiodintsov
    aiodintsov over 11 years
    Oh, you CAN specify namespaces you need via this overload msdn.microsoft.com/en-us/library/4bektfx9.aspx however in my case I did to deal with generic requests those could have different namespaces/namespace versions declared so resolving namespace was not an option for me
  • Earlz
    Earlz over 11 years
    Heh, that's odd. With that code I get "Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function" which doesn't make any sense to me as I have a namespace manager?
  • MiMo
    MiMo over 11 years
    I think either //a:Applications or a:Package/a:Applications, because that node is not directly under the root.
  • MiMo
    MiMo over 11 years
    The namespace manager should be passed to SelectNodes: SelectNodes("//a:Applications", mgr);
  • Alexei Levenkov
    Alexei Levenkov over 11 years
    @Earlz, sample fixed (I hope) - forget to pass namespace manger. And inlined MiMo suggestion too.
  • Earlz
    Earlz over 11 years
    Although you went through a lot of revisions, I think you provided the most complete answer. Other people mentioned naming the namespace, but you also included the overload to make it actually work, and for my XPath query to be nice and clean :) Thanks!