Perl XML:: LibXML: findnode vs. findvalue vs. find - какая разница?

Я использую XML::LibXML, и мне просто нужно получить количество узлов, указанное выражением XPath.

Используя одну из первых двух строк кода ниже, вы получите то, что я ищу. Я могу использовать функцию count XPath с findvalue или find, но не findnodes (да, я знаю, потому что он возвращает список).

my $node_cnt = $dom->findvalue("count($xpath_str)");  # WORKS!
my $node_cnt = $dom->find("count($xpath_str)");       # WORKS!
my @node_cnt = $dom->findnodes("count($xpath_str)");  # count doesn't work!

Это приводит меня к общему вопросу: Какая разница между тремя типами find? В документации говорится:

$string = $node->findvalue($xpath)
$result = $node->find($xpath)
@nodes  = $node->findnodes($xpath_expression)
  • Есть ли разница между аргументом $xpath_expression и просто $xpath в документации?

  • Для двух возвращающих скаляр, какая разница?

Я пытаюсь понять значение использования одного типа поиска над другим - спасибо!

Ответ 1

Разница - это тип значения, возвращаемого методами.

  • findnodes используется для возврата списка узлов. Если метод вызывается в контексте списка, он возвращает список объектов соответствующего типа, например XML::LibXML::Element, XML::LibXML::Text и т.д. Если он вызывается в скалярном контексте, он возвращает один объект XML::LibXML::NodeList, который содержит та же информация.

    Он не может использоваться для возврата произвольного выражения, например $dom->findnodes('42') ничего не вернет. Вы можете получать только узлы списка документов из этого метода.

  • findvalue используется для возврата одного текстового или числового значения, т.е. не XML node. Если вы передадите выражение XPath, которое оценивает список node, тогда он преобразует этот список в текст, объединяя все текстовые узлы в любом из узлов в списке.

  • find может вернуть что угодно. Он вернет список node как объект XML::LibXML::NodeList, числовое значение как объект XML::LibXML::Number, строковый литерал как объект XML::LibXML::Literal и т.д. В отличие от findnodes, он всегда возвращает одно скалярное значение, даже если вызывается в контексте списка.

    Я никогда не выбирал использовать find. Похоже, он предназначен для всех, когда вы не знаете, какого результата ожидать.

Например, вы, вероятно, захотите написать my $nrecs = $dom->findvalue('count(/root/record)'), чтобы получить количество записей в корневом элементе. $nrecs будет простым числовым значением perl.

С другой стороны, чтобы получить список этих записей, вы должны использовать my @records = $dom->findnodes('/root/record'). Теперь @records содержит несколько объектов XML::LibXML::Element.

В ваших примерах

my $node_cnt = $dom->findvalue("count($xpath_str)");  # WORKS!

это устанавливает $node_cnt в простое число Perl, а это

my $node_cnt = $dom->find("count($xpath_str)");  # WORKS!

устанавливает $node_cnt в объект XML::LibXML::Number, который, как правило, строит (когда вы печатаете его) так же, как и предыдущий оператор. Докажите это сами, напечатав print ref $node_cnt в обоих случаях.

Тогда

my @node_cnt = $dom->findnodes("count($xpath_str)");  # count doesn't work!

терпит неудачу, потому что XPath count оценивается числом, а не XML node (он не является частью дерева XML). Нет способа преобразования чисел в узлы, поэтому результат представляет собой пустой список. Если бы это было наоборот, и мы вызывали findvalue в выражении, которое оценивалось в списке node, тогда есть неопределенно разумный способ преобразования в текстовое значение, а findvalue делает все возможное и возвращает конкатенация всех текстовых узлов, содержащихся в узлах в списке.