无法使用XmlDocument.SelectNodes检索任何节点?

19

我正在尝试解析一个XML文件。这个文件是一个AppxManifest文件。

一个示例文件看起来像这样:

<?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>

我尝试这样解析它:

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");

然而,执行完这个操作后,nodes 永远不会包含任何内容。虽然 XML 文档已经被加载了。使用 SelectNodes("//*") 可以如预期地返回每个节点。我出了什么问题?

我还尝试了很多 XPath 查询的变体,例如:

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

然而,似乎没有一个可以检索到单个节点。理想情况下,我希望 nodes 包含所有 Application 节点。


2
你的标题提到了XDocument,但是你的代码使用了XmlDocument。如果你真的在使用LINQ to XML,我建议使用Descendants... LINQ to XML是一个选项吗? - Jon Skeet
很遗憾,我们不能使用 Linq to XML,因为我们的目标是 .Net 2.0。但是我想说的是 XmlDocument。我总是把这两个搞混。 - Earlz
4个回答

48

你需要特别使用xml命名空间来选择它们。考虑

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

在您的情况下,这段代码可能也能很好地工作:

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);

对于第一个,我得到了“表达式必须评估为节点集”。 - Earlz
1
啊,现在可以了。顺便问一下,有没有更简洁的方法来做这个?我以前从未处理过XPath的命名空间,所以我习惯于像/Applications/Application这样简单明了的方式。 - Earlz
我几个月前不得不使用它,但是无法找到更清晰的语法,但XPath 2.0 "//*:name"不受.NET Framework系统库支持。不过你可以找到第三方XPath 2.0和XSTL 2.0产品。 - aiodintsov
哦,你可以通过这个重载http://msdn.microsoft.com/en-us/library/4bektfx9.aspx指定你需要的命名空间,但在我的情况下,我需要处理可能声明不同命名空间/命名空间版本的通用请求,因此解析命名空间对我来说不是一个选项。 - aiodintsov
尽管您经历了许多修订,但我认为您提供了最完整的答案。其他人提到了命名空间,但是您还包括了重载,使其实际工作,并且可以使我的XPath查询变得简洁 :) 谢谢! - Earlz

11

您需要在NamespaceManager和XPath中指定命名空间的前缀。请注意,前缀不需要与任何内容匹配,除非在XPath和命名空间管理器之间。

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);

正如其他答案指出的那样,接受任何命名空间的XPath是另一种选择。

*) 即在您的特定示例中,有2个名称空间"default"(请注意,默认前缀与空名称空间不同)和"build"。因此,在定义命名空间管理器时,您需要为每个命名空间指定一个前缀(如果您需要针对两个节点进行操作),但是前缀可以是任意字符串(对于前缀有效,但不能是空的)。即使用"a"作为"default"命名空间的前缀,并使用"bar"作为映射到XML中的"build"命名空间的前缀。


嘿,这很奇怪。使用那段代码时,我得到了“需要命名空间管理器或XsltContext。此查询具有前缀、变量或用户定义的函数”,但这对我来说毫无意义,因为我已经有一个命名空间管理器了? - Earlz
1
我认为应该是 //a:Applications 或者 a:Package/a:Applications,因为该节点不直接位于根目录下。 - MiMo
1
命名空间管理器应该传递给 SelectNodesSelectNodes("//a:Applications", mgr); - MiMo
@Earlz,示例已修复(我希望)——忘记传递命名空间管理器。同时也添加了MiMo建议中的内联方法。 - Alexei Levenkov

6

并不是在这个特定的情况下,而是一般情况下,如果实际XML中的命名空间URN与用于向命名空间管理器添加命名空间的URN不完全相同(例如:缺少尾随斜杠),并且在XPath中指定了前缀,则查询可能返回null

如果XML中的命名空间URN不可靠,则语法

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

会起作用。


4

你可能需要阅读这个:

这是你的代码:

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']");

我确实读过那个 :) 只是没有意识到它会与没有命名空间的 XML 文件相比如此根本地改变我的 XPath 查询。 - Earlz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接