XML中的XPath变量::Twig或Other



我正在使用XML::Twig::XPath处理ITS数据,并试图找出如何解析包含变量的XPath表达式。以下是我需要使用ITS规范的一个示例:

<its:rules version="2.0">
  <its:param name="LCID">0x0409</its:param>
  <its:translateRule selector="//msg[@lcid=$LCID]" translate="yes"/>
</its:rules>

我需要能够评估selector中包含的XPath表达式,其中变量的值是its:param元素的内容。我不知道该怎么做。XML::XPath的文档提到了变量(我认为应该是上下文的一部分),它甚至有一个类来表示它们,但文档没有说明如何在上下文中指定变量。如果可能的话,我甚至不确定如何从XML::Twig访问这样的功能。

有人知道怎么做吗?或者,你能举一个例子,说明如何将这种功能与另一个模块(如XML::LibXML)一起使用吗?

libxml2和XML::LibXML支持XPath 2.0路径及其变量。

use XML::LibXML               qw( );
use XML::LibXML::XPathContext qw( );
sub dict_lookup {
   my ($dict, $var_name, $ns) = @_;
   $var_name = "{$ns}$var_name" if defined($ns);
   my $val = $dict->{$var_name};
   if (!defined($val)) {
      warn("Unknown variable "$var_name"n");
      $val = '';
   }
   return $val;
}
my $xml = <<'__EOI__';
<r>
<e x="a">A</e>
<e x="b">B</e>
</r>
__EOI__
my %dict = ( x => 'b' );
my $parser = XML::LibXML->new();
my $doc = $parser->parse_string($xml);
my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerVarLookupFunc(&dict_lookup, %dict);
say $_->textContent() for $xpc->findnodes('//e[@x=$x]', $doc);

这里有一个完整的解决方案。

我避开了"what's a Qname"部分,从已经找到的参数名称构建了一个正则表达式。如果有很多参数,这可能会很慢,但它在W3C的例子中运行良好;构建regexp意味着转义\Q/\E之间的每个名称,以便忽略名称中的元字符,按长度对名称进行排序,使较短的名称不匹配,而不是较长的名称,然后通过"|"、将它们连接起来

限制:

  • 如果您使用以前未定义的参数,则没有错误处理
  • 选择器中的名称空间不被处理,这很容易添加。如果您有真实的数据,只需添加适当的map_xmlns声明
  • 整个文档都加载在内存中,如果要使用通用的XPath选择器,这是很难避免的

这是:

#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig::XPath;
my %param;
my $mparam;
my @selectors;
my $t= XML::Twig::XPath->new( 
  map_xmlns     => { 'http://www.w3.org/2005/11/its' => 'its' },
  twig_handlers => { 'its:param' => sub { $param{$_->att( 'name')}= $_->text; 
                                          $match_param= join '|', 
                                                         map { "Q$_E" }
                                                         sort { lenght($b) <=> length($a) } keys %param;
                                        },
                     'its:translateRule[@translate="yes"]' =>
                                   sub { my $selector= $_->att( 'selector');
                                         $selector=~ s{$($mparam)}{quote($param{$1})}eg;
                                         push @selectors, $selector;
                                       },
                   },
                            )
                       ->parse( *DATA);
foreach my $selector (@selectors)
  { my @matches= $t->findnodes( $selector);
    print "$selector: ";
    foreach my $match (@matches) { $match->print; print "n"; }
  }
sub quote
  { my( $param)= @_;
    return $param=~ m{"} ? qq{'$param'} : qq{"$param"}; 
  }

如果您使用的引擎只支持XPath 1.0路径,则可以将该值视为语法为:的模板

start : parts EOI
parts : part parts |
part  : string_literal | variable | other

下面从XPath模板生成XPath。

sub text_to_xpath_lit {
   my ($s) = @_;
   return qq{"$s"} if $s !~ /"/;
   return qq{'$s'} if $s !~ /'/;
   $s =~ s/"/", '"', "/g;
   return qq{concat("$s")};
}
my $NCNameStartChar_class = '_A-Za-zxC0-xD6xD8-xF6xF8-x{2FF}x{370}-x{37D}x{37F}-x{1FFF}x{200C}-x{200D}x{2070}-x{218F}x{2C00}-x{2FEF}x{3001}-x{D7FF}x{F900}-x{FDCF}x{FDF0}-x{FFFD}x{10000}-x{EFFFF}';
my $NCNameChar_class = $NCNameStartChar_class . '-.0-9xB7x{300}-x{36F}x{203F}-x{2040}';
my $NCName_pat = "[$NCNameStartChar_class][$NCNameChar_class]*+";

my $xpath = '';
for ($xpath_template) {
   while (1) {
      if (/G ( [^'"$]++ ) /xgc) {
         $xpath .= $1;
      }
      elsif (/G (?=['"]) /xgc) {
         /G ( ' [^\']*+ ' | " [^\"]*+ " ) /sxgc
            or die("Unmatched quoten");
         $xpath .= $1;
      }
      elsif (/G $ /xgc) {
         /G (?: ( $NCName_pat ) : )?+ ( $NCName_pat ) /xgc
            or die("Unexpected '$'n");
         my ($prefix, $var_name) = ($1, $2);
         my $ns = $ns_map{$prefix}
            or die("Undefined prefix '$prefix'n");
         $xpath .= text_to_xpath_lit(var_lookup($ns, $var_name));
      }
      elsif (/G z /xgc) {
         last;
      }
   }    
}

样品var_lookup:

sub var_lookup {
   my ($ns, $var_name) = @_;
   $var_name = "{$ns}$var_name" if defined($ns);
   my $val = $params{$var_name};
   if (!defined($val)) {
      warn("Unknown variable "$var_name"n");
      $val = '';
   }
   return $val;
}

未测试。

在XML::XPath中,您可以在XML::XPath::Parser对象上设置变量。它似乎不能通过XML::XPath对象直接访问;您必须使用未记录的$xp->{path_parser}才能访问它。下面是一个包含字符串变量和节点集变量的示例:

use XML::XPath;
use XML::XPath::Parser;
use XML::XPath::Literal;
my $xp = XML::XPath->new(xml => <<'ENDXML');
<?xml version="1.0"?>
<xml>
    <a>
        <stuff foo="bar">
            junk
        </stuff>
    </a>
</xml>
ENDXML
#set the variable to the literal string 'bar'
$xp->{path_parser}->set_var('foo_att', XML::XPath::Literal->new('bar'));
my $nodeset = $xp->find('//*[@foo=$foo_att]');
foreach my $node ($nodeset->get_nodelist) {
    print "1. FOUNDnn",
        XML::XPath::XMLParser::as_string($node),
        "nn";
}
#set the variable to the nodeset found from the previous query
$xp->{path_parser}->set_var('stuff_el', $nodeset);
$nodeset = $xp->find('/*[$stuff_el]');
foreach my $node ($nodeset->get_nodelist) {
    print "2. FOUNDnn",
        XML::XPath::XMLParser::as_string($node),
        "nn";
}

相关内容

  • 没有找到相关文章

最新更新