找到两个元素之间的相对XPath的最有效方法是什么?

时间:2011-08-16 17:55:00

标签: xml perl dom xpath

看过各种流行的XML / XPath模块,我还没有看到实现这一目标的直接方法。

基本上界面看起来像:

my $xpath = get_path($node1, $node2);

...将返回从$ node1到$ node2的相对路径。

我将自己的时间用于计算'效率' - 我将采用任何现有的解决方案来解决这个问题。如果做不到这一点,我想知道在任何“明显的”本土解决方案中可能遇到的一些陷阱。

在我的头脑中,我可以想象只是首先在$ node1的后代中搜索$ node2,然后失败,迭代$ node1的祖先做同样的事情。那会不会像我担心的那样充满资源密集型呢?

对于我的特定用例,我可以假设$ node1和$ node2的绝对路径都是已知的。鉴于此,我想认为可以在两条完整路径之间完成一些“XPath数学”,而不必在整个树上运行,但我不知道该过程会是什么样的。

总结一下:

1)现有的CPAN模块是否可以轻松实现我想做的事情?

2)如果没有,那么有什么方法可以解决这个问题?

2 个答案:

答案 0 :(得分:6)

找到两个节点的绝对路径。

ref:    root foo bar[2] baz[1] moo
target: root foo bar[2] baz[2] moo

删除常见的潜在客户群。

ref:    baz[1] moo
target: baz[2] moo

对于参考中的每个细分,在目标前加上..细分。

.. .. baz[2] moo

转换为XPath。

../../baz[2]/moo

代码:

use XML::LibXML qw( XML_ATTRIBUTE_NODE XML_ELEMENT_NODE );

sub get_path_segs {
   my ($node) = @_;
   my @path = split(/\//, $node->nodePath());
   shift(@path);
   return @path;
}

sub get_path {
   my ($ref, $targ) = @_;

   die if $ref->nodeType()  != XML_ELEMENT_NODE && $ref->nodeType()  != XML_ATTRIBUTE_NODE;
   die if $targ->nodeType() != XML_ELEMENT_NODE && $targ->nodeType() != XML_ATTRIBUTE_NODE;

   my @ref  = get_path_segs($ref);
   my @targ = get_path_segs($targ);

   while (@ref && @targ && $ref[0] eq $targ[0]) {
      shift(@ref);
      shift(@targ);
   }

   while (@ref) {
      pop(@ref);
      unshift(@targ, '..');
   }

   return @targ ? join('/', @targ) : '.';
}

它目前支持元素和属性节点。它可以扩展为支持其他节点类型,可能是平凡的。

答案 1 :(得分:2)

有两种可能的结果

  • 这两个节点有一个共同的祖先
  • 一个节点是另一个节点的后代

合乎逻辑的行动方针是

  • 遍历每个节点的父节点,看看是否找到了一个共同的ancstor。
  • 执行此操作时,请检查其中一个祖先是否与另一个节点相同

在任何一种情况下,生成的路径都是最短的路径。

从父节点链构造相对XPath表达式。找到一个有吸引力的表示甚至可能是整个问题中最难的部分。