Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 796
11 revs, 10 users 40% Avatar asked Mar 02 '26 12:03

11 revs, 10 users 40%


1 Answers

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
like image 67
waj Avatar answered Mar 05 '26 13:03

waj



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!