I wrote some Perl module that builds an XML::Twig tree.
Unfortunately it seems that XML::Twig
's XPath implementation is incomplete, so I loaded XML::Twig::XPath
.
As the documentation for XML::Twig states that findnodes
is the same as get_xpath
(among several more), I was expecting that I could process a query like //info_string[text()]
that the original XML::Twig
did not like.
On the first run XML::Twig::XPath
complained that I'll have to use getnodes
instead of get_xpath
, so I changed that.
Unfortunately even after that change, I still get the same message:
error in xpath expression //info_string[text()] at text()
the expression is a valid XPath statement, and you are using XML::Twig::XPath, but
you are using either 'find_nodes' or 'get_xpath' where the method you likely wanted
to use is 'findnodes', which is the only one that uses the full XPath engine
at /usr/lib/perl5/vendor_perl/5.18.2/XML/Twig.pm line 3566.
I'm confused.
The statement in question looks like this:
return $self->_XML()->findnodes($XPath);
The _XML()
method returns the XML::Twig
tree, and findnodes
is expected to query that tree.
I had also tried to replace use XML::Twig
with use XML::Twig::XPath
, but that didn't change the message, too.
As the code to create the data structure and the XML from it is overly complex, I' just going to present the XML output ($self->_XML()->print
) and the data structure (via Data::Dumper
) here.
The XML to query looks like this:
<MonitoringOutput id="test-id" version="0.1">
<description>This is a test output</description>
<exit_code>0</exit_code>
<status_string>OK</status_string>
<info_string>(demo): Some interesting story</info_string>
<perf_string>L1=0.49 L2=4.7s;;;0;7 L3=0.6;@0.2:0.3</perf_string>
<perf_data count="3">
<sample label="L3">
<label>L3</label>
<value>0.6</value>
<thresholds>
<warn end="0.3" inverted="1" start="0.2"/>
</thresholds>
</sample>
<sample label="L1">
<label>L1</label>
<value>0.49</value>
</sample>
<sample label="L2">
<label>L2</label>
<value unit="s">4.7</value>
<unit>s</unit>
<range max="7" min="0"/>
</sample>
<warn_labels count="0"/>
<crit_labels count="0"/>
</perf_data>
</MonitoringOutput>
This is my attempt to make a reproducible minimal example:
#!/usr/bin/perl
use warnings;
use strict;
use XML::Twig;
use XML::Twig::XPath;
my $twig = XML::Twig->new();
$twig->parse('<MonitoringOutput id="test-id" version="0.1">
<description>This is a test output</description>
<exit_code>0</exit_code>
<status_string>OK</status_string>
<info_string>(demo): Some interesting story</info_string>
<perf_string>L1=0.49 L2=4.7s;;;0;7 L3=0.6;@0.2:0.3</perf_string>
<perf_data count="3">
<sample label="L3">
<label>L3</label>
<value>0.6</value>
<thresholds>
<warn end="0.3" inverted="1" start="0.2"/>
</thresholds>
</sample>
<sample label="L1">
<label>L1</label>
<value>0.49</value>
</sample>
<sample label="L2">
<label>L2</label>
<value unit="s">4.7</value>
<unit>s</unit>
<range max="7" min="0"/>
</sample>
<warn_labels count="0"/>
<crit_labels count="0"/>
</perf_data>
</MonitoringOutput>');
$twig->findnodes('//info_string[text()]');
When running it under the debugger (on a different machine with somewhat newer perl), I get this output:
error in xpath expression //info_string[text()] at text()
the expression is a valid XPath statement, and you are using XML::Twig::XPath, but
you are using either 'find_nodes' or 'get_xpath' where the method you likely wanted
to use is 'findnodes', which is the only one that uses the full XPath engine
at /usr/lib/perl5/vendor_perl/5.26.1/XML/Twig.pm line 3685.
at /usr/lib/perl5/vendor_perl/5.26.1/XML/Twig.pm line 7122.
XML::Twig::Elt::_croak_and_doublecheck_xpath("//info_string[text()]", "error in xpath expression //info_string[text()] at text()") called at /usr/lib/perl5/vendor_perl/5.26.1/XML/Twig.pm line 7089
XML::Twig::Elt::_install_xpath("//info_string[text()]") called at /usr/lib/perl5/vendor_perl/5.26.1/XML/Twig.pm line 7131
XML::Twig::Elt::get_xpath(XML::Twig::Elt=HASH(0x5591017d3da0), "//info_string[text()]") called at /usr/lib/perl5/vendor_perl/5.26.1/XML/Twig.pm line 3685
XML::Twig::get_xpath(XML::Twig=HASH(0x55910059bb00), "//info_string[text()]") called at /tmp/t.pl line 38
When building the XML tree with full XPath support, it must be built using XML::Twig::XPath->new
and elements added must be created using XML::Twig::XPath::Elt->new
to make findnodes
work.
As using XPath is kind of optional in my code, I decided to load either XML::Twig
or XML::Twig::XPath
dynamically, depending whether full XPath support is needed.
In addition my code has to use XML::Twig::XPath::Elt
instead of XML::Twig::Elt
for full XPath support.
So my code uses these tricks:
my $xml_twig = $use_xpath ? 'XML::Twig::XPath' : 'XML::Twig';
my $xml_twig_mod = $xml_twig . '.pm';
$xml_twig_mod =~ s,::,/,g;
require $xml_twig_mod;
my $xml = $xml_twig->new(...);
my $elt = "${xml_twig}::Elt"->new(...);