I am using Crystal, and am trying to retrieve the ID of a node in an XML document:
<foo ID="bar"></foo>
I am using the following code to get to the 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")
If I inspect the nodeset, I get the content I am expecting:
[#<XML::Attribute:0x1287690 name="ID" value="bar">]
And nodeset.class
returns XML::NodeSet
which has an instance method []
. So I believe I should be able to do this to get the value:
node = nodeset[0]
node.value
However, when I call nodeset[0]
I get the following error:
undefined method '[]' for Float64 (compile-time type is (String | Float64 | Bool | XML::NodeSet))
node = nodeset[0]
I do not understand why the []
method is seeing nodeset as a Float64 when both inspect
and class
are seeing it as an XML::Nodeset
.
What am I missing?
Is it a coincidence that String has a []
method, but Float64 does not?
When you execute evaluate
the return type is a union type of all the possible values. In this case XML::NodeSet
is the runtime type (notice the difference with the compile time type).
If you can assure that the return type is always a node set, then you can simply do:
nodeset = xpath_context.evaluate("//foo/@ID") as XML::NodeSet
But that will raise an exception if the result has a different type. Another option is do it conditionally:
if nodeset.is_a?(XML::NodeSet)
# use nodeset here without casting, the compiler will restrict the type
end
Or even with a case
statement:
case nodeset
when XML::NodeSet
# ...
end