[英]How to find nodes case-insensitive using XML::LibXML
我需要在XML文件中找到不区分大小写的节点。 以下代码有效,但仅当所有元素都不为小写时:
my $dom = XML::LibXML->new->parse_fh(*DATA);
my $xpc = XML::LibXML->XPathContext->new( $dom->documentElement );
my @invoices = $xpc->findnodes( "/ALLINVOICES/INVOICES/INVOICE" );
__DATA__
<ALLINVOICES>
<INVOICES>
<INVOICE number="12345">
<CUSTOMER>Mr Fubar</CUSTOMER>
</INVOICE>
</INVOICES>
</ALLINVOICES>
如何修复它,使其也接受<allinvoices><invoices><invoice>
?
将元素名称规范化为小写的字符串预处理阶段可能会帮助您:
my $xmlstring = '';
{
local $/;
$xmlstring = <DATA>;
}
#
# Turns all element names into lowercase.
# Works as well with uppercase ( replace lc with uc )
#
# !!! The usual caveats wrt processing semistructured data with regexen apply (ie. don't try more complex transformations purely by changing the regex pattern )
#
$xmlstring =~ s#(<[/]?[^/>[:space:]]+)#lc($1)#eg; # all element names
my $dom = XML::LibXML->new->parse_string( $xmlstring);
# ...
注意
提出的解决方案错误地处理了注释和cdata部分(如@ikegami所指出)。 为了根据安全规格 ,元素名称的第一个字符必须属于以下字符类:
[:_a-zA-Z\x{c0}-\x{d6}\x{d8}-\x{f6}\x{f8}-\x{ff}\x{0370}-\x{037d}\x{037f}-\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}\N{U+10000}-\n{U+EFFFF}]
这个怪物会插入[/]?
之间[/]?
和上面代码部分的regex模式中的[^/>[:space:]]*
(遵守更改的重复修饰符)。
XML和XPath始终区分大小写,因此您需要编写将字符串转换为大写或小写的代码以进行比较。 我认为LibXML::XPathContext
允许您注册其他函数,以便您可以在Perl中编写一个函数,从XPath调用该函数,并带有要比较的节点和名称,并根据需要返回true或false:
$xpc->registerFunction('tn', sub { my ($node,$name) = @_; if (lc($node->item(0)->localName) eq $name) { return XML::LibXML::Boolean->True; } else { return XML::LibXML::Boolean->False;} });
my @invoices = $xpath->findnodes('/*[tn(., "allinvoices")]/*[tn(., "invoices")]/*[tn(., "invoice")]');
但是,这仅比编写注释(很多)长XPath表达式时在注释中已经建议的在XPath中使用translate
稍短。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.