使用 Crystal 从 XML::Nodeset 中的第一个节点检索值

Retrieve a value from the first node in an XML::Nodeset using Crystal

我正在使用 Crystal,并且正在尝试检索 XML 文档中的节点 ID:

<foo ID="bar"></foo>

我正在使用以下代码获取 ID

require "xml"
file = File.read("path/to/doc.xml")
xml = XML.parse(file)
xpath_context = XML::XPathContext.new(xml)
nodeset = xpath_context.evaluate("//foo/@ID")

如果我检查节点集,我会得到我期望的内容:

[#<XML::Attribute:0x1287690 name="ID" value="bar">]

nodeset.classreturnsXML::NodeSet其中有an instance method []。所以我相信我应该能够这样做以获得价值:

node = nodeset[0]
node.value

但是,当我调用 nodeset[0] 时出现以下错误:

undefined method '[]' for Float64 (compile-time type is (String | Float64 | Bool | XML::NodeSet))

    node = nodeset[0]

我不明白为什么 [] 方法将节点集视为 Float64 而 inspectclass 都将其视为 XML::Nodeset

我错过了什么?

String有[]方法,而Float64没有,是巧合吗?

当您执行 evaluate 时,return 类型是所有可能值的联合类型。在这种情况下 XML::NodeSet 是运行时类型(注意与编译时类型的区别)。

如果你能保证 return 类型总是一个节点集,那么你可以简单地做:

nodeset = xpath_context.evaluate("//foo/@ID") as XML::NodeSet

但如果结果具有不同的类型,则会引发异常。 另一种选择是有条件地进行:

if nodeset.is_a?(XML::NodeSet)
    # use nodeset here without casting, the compiler will restrict the type
end

甚至使用 case 语句:

case nodeset
when XML::NodeSet
  # ...
end

为了完整起见,这是我在@asterite 和@waj 的帮助下最终得到的代码

file = File.read("path/to/doc.xml")
xml = XML.parse(file)
node = xml.xpath_node("//foo/@ID")
node.text

注意 node.value 也错了!