【问题标题】:C# XML Select dictionary node by key valueC# XML 按键值选择字典节点
【发布时间】:2016-09-25 11:36:19
【问题描述】:

我有一个包含以下内容的 XML 文件(它是一个 iTunes 文件):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Major Version</key><integer>1</integer>
        <key>Minor Version</key><integer>1</integer>
        <key>Application Version</key><string>12.3.3.17</string>

    ...

        <key>Music Folder</key><string>file://localhost/C:/Users/username/longpath/</string>
    </dict>
</plist>

如何获取最后一个字符串的值,其中&lt;key&gt; 是“音乐文件夹”?

这是一个非常大的文件,所以我不想遍历所有节点。

我尝试了以下一些方法:

XmlNode location = xmlDocument.SelectSingleNode("descendant::dict[key='Music Folder']");
location = xmlDocument.SelectSingleNode("//text()[.='Music Folder']");
location = xmlDocument.SelectSingleNode("string[last()]");
location = xmlDocument.SelectSingleNode("dict::string[last()]");
location = xmlDocument.SelectSingleNode("descendant::dict[string[last()]]");

我对 XPath 表达式不太熟悉,并且无法正确表达。

我也对 LINQ 解决方案持开放态度。

【问题讨论】:

  • 如果音乐文件夹总是最后一个,/plist/dict//key[last()]/text() 会起作用吗?

标签: c# xml linq itunes xmldocument


【解决方案1】:

文档的 XPath 表达式是在文档根的上下文中计算的,因此您需要指定 plist 节点,我假设您需要字符串值,因此您需要导航到该节点。

XmlNode location = xmlDocument.SelectSingleNode("plist/dict/key[.='Music Folder']/following-sibling::string[1]")

请注意,该值没有嵌套在 key 元素中,而是以下兄弟元素。鉴于这出现在最后,您可能会逃脱:

XmlNode location = xmlDocument.SelectSingleNode("plist/dict/string[last()]")

我会在 XPath 中明确指定 plist 节点,因为它将可能的匹配项保持在最低限度。需要注意的另一点是,您的 XPath 最终可能会使用底层技术迭代每个节点,这取决于您如何构建表达式,因为某些实现可能能够应用优化。

您很接近,但我看到您混淆了运算符并使用 ::[]/ 更合适。 :: 将轴与节点名称分开,因此 descendants::dict 获取名为 dict 的上下文节点 (xmlDocument) 的所有后代。 / 运算符实际上是 child:: 的简写。 [] 是您的过滤谓词,用于缩小结果集。

【讨论】:

  • 实际上,这两个选项都有效。我更喜欢第一个,因为它更清楚意图是什么。谢谢!
  • //string[preceding::key[.="Music Folder"]]/text() 也可以使用
  • @Jahmic 如果要选择字符串或整数节点,可以使用 following-sibling::* 代替字符串。我同意意图也更清晰,更强大
  • @pinkfloydx33 最好不要用 // 选择后代,尤其是苹果 plist ,因为可能有嵌套的 dict 节点,你最终会搜索整个文件。虽然 +1 的想法,plist/dict/string[preceding::key='Music Folder'] 读起来真的很好
【解决方案2】:

花了 17 毫秒来获取文档中最后一个节点的值。为了测试速度,我创建了一个包含 Music Folder 节点作为最后一个 key 节点的文档,并且在您的示例中列出的前 3 个节点大约 11000 次。

string ReadValue(XDocument xDoc, string key)
{
    var node = xDoc.Element("plist")
        .Element("dict")
        .Elements("key")
        .Single(e => e.Value == key)
        .NextNode as XElement;
    return node.Value;
}

【讨论】:

  • 这个也不错。我可以在遍历键的情况下使用它。
猜你喜欢
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-30
相关资源
最近更新 更多