【问题标题】:XPathSelectElement always returns nullXPathSelectElement 始终返回 null
【发布时间】:2011-04-28 13:22:59
【问题描述】:

为什么这个 Xpath 不能使用 XDocument.XPathSelectElement 工作?

Xpath:

//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]

XML

<Plugin xmlns="http://www.MyNamespace.ca/MyPath">
  <UI>
    <PluginPageCategory>
      <Page>
        <Group>
          <CommandRef>
            <Images>
            </Images>
          </CommandRef>
          <CommandRef>
            <Images>
            </Images>
          </CommandRef>
        </Group>
      </Page>
    </PluginPageCategory>
  </UI>
</Plugin>

C#代码:

myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator());

【问题讨论】:

  • xpath查询中没有命名空间信息,可能是这个原因。尝试缩小范围,删除 XML 上的命名空间,看看是否能得到结果?
  • 这可能是由于命名空间 - 检查从 XML 中删除它是否修复它,如果是,则需要设置一个 NamespaceManager。
  • @Cumbayah:是的,它有效,谢谢。但是现在我该如何正确处理命名空间呢?

标签: c# xml xpath


【解决方案1】:

当使用命名空间时,它们也必须在 XPath 查询中使用。您的 XPath 查询仅适用于没有命名空间的元素(可以通过从 XML 中删除命名空间来验证)。

以下示例展示了如何创建和传递命名空间管理器:

var xml = ... XML from your post ...;

var xmlReader = XmlReader.Create( new StringReader(xml) ); // Or whatever your source is, of course.
var myXDocument = XDocument.Load( xmlReader );
var namespaceManager = new XmlNamespaceManager( xmlReader.NameTable ); // We now have a namespace manager that knows of the namespaces used in your document.
namespaceManager.AddNamespace( "prefix", "http://www.MyNamespace.ca/MyPath" ); // We add an explicit prefix mapping for our query.

var result = myXDocument.XPathSelectElement(
    "//prefix:Plugin/prefix:UI[1]/prefix:PluginPageCategory[1]/prefix:Page[1]/prefix:Group[1]/prefix:CommandRef[2]",
    namespaceManager
); // We use that prefix against the elements in the query.

Console.WriteLine(result); // <CommandRef ...> element is printed.

希望这会有所帮助。

【讨论】:

  • 如果您必须显式添加前缀/ns 组合,那么从 XmlReader 加载 NameTable 有什么意义?我假设您这样做是因为您不知道文档用于所需命名空间的前缀。但是,这是否也意味着没有必要用NameTable 初始化XmlNamespaceManager
  • 我只想从字符串中删除命名空间。如果你知道它是独一无二的。
【解决方案2】:

这可能应该是对@Cumbayah 帖子的评论,但我似乎无法在任何事情上留下 cmets。

您最好使用类似的东西而不是使用 XmlReader 来获取名称表。

var xml = ... XML from your post ...;
var myXDocument = XDocument.Parse(xml);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("prefix", "http://www.MyNamespace.ca/MyPath");

var result = ...;

【讨论】:

  • 只想指出默认命名空间应该作为前缀的问题。 IE。 namespaceManager.AddNamespace("","MyNamespace.ca/MyPath") 将使 DefaultNamespace 属性按预期正确,但 XPathSelectElement 不会使用它。误导!!!
【解决方案3】:

在您的情况下,最简单的方法是使用 XPath axesnode test 作为节点名称和位置来选择元素。 您的 XPath 选择:

myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator());

可以很容易地翻译成:

myXDocument.XPathSelectElement("/child::node()[local-name()='Plugin']/child::node()[local-name()='UI'][position()=1]/child::node()[local-name()='PluginPageCategory'][position()=1]/child::node()[local-name()='Page'][position()=1]/child::node()[local-name()='Group'][position()=1]/child::node()[local-name()='CommandRef'][position()=2]");

无需创建并传递 XmlNamespaceManager 作为参数。

【讨论】:

  • 太棒了,这很好用。命名空间应该简单但非常棘手,所以这个解决方案非常有用。
【解决方案4】:

有一种方法可以在不更改 xpath 的情况下做到这一点。我找到的解决方案是在将 XML 解析为 XDocument 时删除命名空间。

这是一个例子:

var regex = @"(xmlns:?[^=]*=[""][^""]*[""])";
var myXDocument = XDocument.Parse(Regex.Replace("MyXmlContent", regex, "", RegexOptions.IgnoreCase | RegexOptions.Multiline))

现在命名空间消失了,操作起来更容易了。

【讨论】:

  • “现在命名空间已经消失了”您可能没有格式良好的 XML 文档...
  • 嗯......它对我来说非常有效。请注意,当您无法更改 xpath 时,它会很有用。
  • 它并非在所有情况下都能完美运行,特别是在两个没有命名空间的元素相同的情况下。
猜你喜欢
  • 1970-01-01
  • 2016-05-31
  • 2015-08-15
  • 2012-03-18
  • 2016-11-04
  • 2016-07-28
  • 2010-11-08
  • 2017-11-26
相关资源
最近更新 更多