So I have some perl code that goes something like:
use strict;
use XML::XPath;
# $xml is an xml string read from file
my $returnVal = mySubA($xml);
#... some more code ...
# sub definition
sub mySubA {
my ($xml) = @_;
my $xp = XML::XPath->new(xml => $xml);
my @fields = $xp->find('/elA/elB/elC')->get_nodelist;
# foreach elC, get desired开发者_JAVA百科 info...
foreach my $field (@fields) {
$xp = XML:XPath->new(context => $field);
my $info1 = $xp->find('/info1')->string_value;
print "\nINFO1: $info1";
my $info2 = $xp->find('/info2')->string_value;
}
# do some stuff....
return $returnVal;
}
This works fine and prints out INFO1: value... . I'm now changing the code, and am trying to get information from another section of the xml, and so I've written a different sub for it. To avoid parsing the same xml twice, I'm trying to write my code only using 1 xpath object (see XPath new in perl docs).
So now my code is something like....
use strict;
use XML::XPath;
# $xml is an xml string read from file
my $xp = XML::XPath->new(xml => $xml);
my $returnValA = mySubA($xp);
my $returnValB = mySubB($xp);
#... some more code ...
# sub definition
sub mySubA {
my ($xp) = @_;
my @fields = $xp->find('/elA/elB/elC')->get_nodelist;
# foreach elC, get desired info...
foreach my $field (@fields) {
$xp = XML:XPath->new(context => $field);
my $info1 = $xp->find('/info1')->string_value;
print "\nINFO1: $info1";
my $info2 = $xp->find('/info2')->string_value;
}
# do some stuff...
return $returnVal;
}
sub mySubB {
my ($xp) = @_;
# do some stuff....
return $returnVal;
}
So, basically, I'm passing the ref to the xpath object into mySubA instead of creating it within mySubA. The problem is that no values are being found, even though I'm pretty sure the xpath expression is resolving something because the loop iterates about 10 times. I'm new to perl, so I might be missing something here, but the thing that is confusing for me is that the second method - mySubB - is working fine; kind of blowing away my first suspicion that there is a problem with passing the xpath object into the sub (as in, I'm not dereferencing/referencing the xpath object as I should be).
I don't know if its relevant or not, but the xml I'm working with doesn't contain any namespaces or attributes.
Are you sure of the line $xp = XML:XPath->new(context => $field);
? I have never used XML::XPath this way, even though it's in the docs. Maybe try getting rid of that line and replacing the following by my $info1 = $xp->find('/info1', $field);
, which should do the same thing.
In any case, if you are not tied to XML::XPath, you should probably try using XML::LibXML instead. It is better maintained, faster, more powerful... you name it. It is also very similar to XML::Xpath in that it implements the DOM and XPath, so the code would be very similar.
Unless you are going to extract more than just one child element, I would use a direct XPath expression:
/elA/elB/elC/info1
If this is not your case, then you should use the XPath expression to select the parents elC
and after that use simple DOM method to get the children.
If you are going to use some complex selecting from the elC
element, then there should be for you in your XPath engine some method taking an expression and a context node. But do note that if elC
is the context node, then you should use a relative XPath like info1
instead of an absolute XPath like /info1
You're creating a new XML::XPath
object each time through your loop, when you do
$xp = XML:XPath->new(context => $field);
This object doesn't have any XML in it to work on. You're also temporarily hiding the $xp
that you got an an argument to your function until the end of the block -- you would have gotten a warning about this if you had use warnings
turned on.
You can set an XPath context in the find
method, so something like this should work:
foreach my $field ( @fields ) {
my $info1 = $xp->find('/info1', $field);
print "\nINFO1: $info1";
}
精彩评论