在Perl中将对XML::XPath对象的引用传递给子对象并没有按预期工作

在Perl中将对XML::XPath对象的引用传递给子对象并没有按预期工作,xml,perl,xpath,pass-by-reference,cpan,Xml,Perl,Xpath,Pass By Reference,Cpan,所以我有一些perl代码,类似于: 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

所以我有一些perl代码,类似于:

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 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;
}
这可以很好地工作并打印出INFO1:value。我现在正在修改代码,并试图从xml的另一部分获取信息,因此我为它编写了另一个子部分。为了避免对同一个xml进行两次解析,我尝试只使用1个xpath对象来编写代码,请参见

所以现在我的代码是

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;
}
因此,基本上,我将xpath对象的ref传递到mySubA中,而不是在mySubA中创建它。问题是找不到任何值,尽管我很确定xpath表达式正在解决某些问题,因为循环迭代了大约10次。我是perl新手,所以这里可能遗漏了一些东西,但让我困惑的是第二个方法——mySubB——工作正常;我第一次怀疑将xpath对象传递到sub中会有问题,这有点让我大吃一惊,因为在中,我没有像我应该的那样去引用/引用xpath对象

我不知道它是否相关,但我正在使用的xml不包含任何名称空间或属性。

您确定行$xp=xml:XPath->newcontext=>$field;?我从未以这种方式使用过XML::XPath,即使它在文档中。也许可以试着去掉这行代码,用我的$info1=$xp->find'/info1',$field;,替换下面的代码;,它们应该做同样的事情

在任何情况下,如果您没有绑定到XML::XPath,那么您可能应该尝试使用XML::LibXML。它维护得更好,速度更快,功能更强大。。。你说吧。它也非常类似于XML::Xpath,因为它实现了DOM和Xpath,因此代码非常类似。

每次通过循环创建一个新的XML::Xpath对象时,您都会这样做

$xp = XML:XPath->new(context => $field);
此对象中没有任何可处理的XML。您还临时隐藏了$xp,在块结束之前,您的函数有一个参数,如果启用了use warnings,您可能会得到一个警告

您可以在find方法中设置XPath上下文,这样应该可以:

foreach my $field ( @fields ) { 
    my $info1 = $xp->find('/info1', $field);
    print "\nINFO1: $info1";
}

除非您要提取不止一个子元素,否则我将使用直接XPath表达式:

/elA/elB/elC/info1
如果不是这样,那么应该使用XPath表达式选择父elC,然后使用简单的DOM方法获取子elC


如果要从elC元素中使用一些复杂的选择,那么在XPath引擎中应该有一些方法来获取表达式和上下文节点。但是请注意,如果elC是上下文节点,那么您应该使用像info1这样的相对XPath,而不是像/info1这样的绝对XPath,哈哈-我已经在使用XML::LibXML进行重写了。我本来打算这么做的;我只是好奇地问。我仍然有XML::XPath版本的未更改副本,所以我会尝试一下您的建议并发回;与$xp=XML相反:XPath->newcontext=>$field;似乎不起作用。即使我回去尝试我的第一个例子:/@Dave:那是因为我告诉你们的是相对路径和绝对路径。请认真阅读我的全部答案……事实上,文档声明您可以这样做,请看问题中的做法有点不可靠,因为$xp会被每个循环覆盖,但它可以工作,除非它不能-见我对米罗德答案的第二点评论。我确实尝试了你建议的方法,但似乎不起作用。另外,$xp=XML:XPath->newcontext=>$field;在我提供的第一个示例中有效。此外,我确实有使用警告;在我剧本的顶端。我确实收到了警告,但据我所知,这似乎与你所说的无关。PrintUsage调用太早,无法在fia_qa_parse_output_xml.pl第17行检查原型。在我的实际脚本中,我定义了一个PrintUsage方法。@Alejandro,是的,我提取了多个元素。我的示例没有清楚地显示这一点,我将对此进行更新,但相关信息的xml结构是:@Alejandro-你说得对。使用相对路径确实有效,并修复了我的第二个示例。但是,有一件事我仍然很困惑。为什么我的第一个示例有效,而我的第二个示例无效?尽管您的解决方案确实修复了我的第二个示例,但它没有解释为什么在第一个示例中使用绝对路径/info1有效。@Dave:这可能是因为XML::XPath构造函数将节点参数作为新文档节点而不是上下文节点……重新阅读您的问题。。。。如果替换我的$info1=$xp->find'/info1',会发生什么;通过我的$info1=$xp->findvalue'/info1';用findvalue替换find?良好捕获;但这是我在程序中没有包含在示例中的内容:我的$info1=xp->find'/info1'->string\u值;我会用你的观点更新这个例子
特德出来了;但是我也会尝试使用findvalue——只是为了实验的缘故。xp->find'/info1'->string\u value;与xp->findvalue'/info1'的结果相同;所以基本上,我还是在同一个地方。