Передача ref в XML :: XPath объект в sub в Perl не работает должным образом - PullRequest
1 голос
/ 06 января 2011

Итак, у меня есть некоторый 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: значение .... Сейчас я изменяю код и пытаюсь получить информацию из другого раздела xml, поэтому я написал для него другую подпрограмму. Чтобы избежать парсинга одного и того же xml дважды, я пытаюсь написать свой код только с использованием 1 объекта xpath (см. XPath new в perl docs ).

Так что теперь мой код похож на ....

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 в mySubA вместо того, чтобы создавать его в mySubA. Проблема в том, что значения не найдены, хотя я вполне уверен, что выражение xpath что-то разрешает, потому что цикл повторяется около 10 раз. Я новичок в Perl, поэтому я могу что-то здесь упустить, но меня смущает то, что второй метод - mySubB - работает нормально; отчасти смахнул мое первое подозрение, что существует проблема с передачей объекта xpath в подпрограмму (как, например, я не разыменываю / ссылаюсь на объект xpath, как должно быть).

Я не знаю, уместно ли это или нет, но xml, с которым я работаю, не содержит пространств имен или атрибутов.

Ответы [ 3 ]

2 голосов
/ 06 января 2011

Вы уверены в линии $xp = XML:XPath->new(context => $field);? Я никогда не использовал XML :: XPath таким образом, хотя он есть в документации. Возможно, попытайтесь избавиться от этой строки и заменить следующее на my $info1 = $xp->find('/info1', $field);, что должно сделать то же самое.

В любом случае, если вы не привязаны к XML :: XPath, вам, вероятно, следует попробовать вместо этого использовать XML :: LibXML. Это лучше поддерживается, быстрее, мощнее ... вы называете это. Он также очень похож на XML :: Xpath в том, что он реализует DOM и XPath, поэтому код будет очень похож.

1 голос
/ 07 января 2011

Если вы не собираетесь извлекать более одного дочернего элемента, я бы использовал прямое выражение XPath:

/elA/elB/elC/info1

Если это не ваш случай, тогда вы должны использовать выражение XPath для выбора родителей elC, а после этого использовать простой метод DOM, чтобы получить детей .

Если вы собираетесь использовать какой-то комплексный выбор из элемента elC, то в вашем движке XPath должен быть какой-то метод, принимающий выражение и контекстный узел. Но учтите, что если elC является узлом контекста, то вам следует использовать относительный XPath , такой как info1 вместо абсолютный XPath , такой как /info1

0 голосов
/ 06 января 2011

Вы создаете новый объект XML::XPath каждый раз в цикле, когда вы делаете

$xp = XML:XPath->new(context => $field);

Этот объект не имеет никакого XML-кода для работы с ним.Вы также временно скрываете $xp, что вы получили аргумент для своей функции до конца блока - вы бы получили предупреждение об этом, если бы вы включили use warnings.

Вы можете установить контекст XPath в методе find, поэтому что-то вроде этого должно работать:

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