我应该使用正则表达式来查找XML命名空间定义吗?

时间:2017-09-12 20:28:46

标签: regex xml perl namespaces xml-libxml

以下脚本有效。它解析XML并在命名空间“dei”下查找特定节点。

但是依靠正则表达式来命名空间定义的正确方法呢? (我真的不懂XML。所以我担心这样的正则表达式对于所有Edgar XML都不是万无一失的。  例如 - 这样的定义是否总是用双引号括起来,前面是xmlns:?)

感谢。

use strict;
use warnings;

use LWP::Simple;
use XML::LibXML;
use XML::LibXML::XPathContext;

my $url = 'https://www.sec.gov/Archives/edgar/data/1057051/000119312517099664/acef-20161231.xml';
my $xml = LWP::Simple::get($url);
my $dom = XML::LibXML->load_xml(string => $xml);

my @nsDefs = ($xml =~ /xmlns:dei="(.+?)"/g);
die "Namespace definition must be unique!\n" unless @nsDefs == 1;

my $xpc = XML::LibXML::XPathContext->new($dom);
$xpc->registerNs('dei', $nsDefs[0]);

my @matches = $xpc->findnodes('//dei:TradingSymbol');
print 'Number of matches = ', scalar(@matches), "\n";

输出:

Number of matches = 1

6 个答案:

答案 0 :(得分:1)

关于XML中命名空间的唯一重要事项是URI。您的代码假定名称空间前缀为dei,使用它来查找名称空间声明并确定URI为http://xbrl.sec.gov/dei/2014-01-31。这完全是倒退。您应该在脚本中进行硬编码的是URI - 它不会改变。名称空间前缀在理论上是可变的,并且不同的前缀可能用于其他文档中的相同URI。

答案 1 :(得分:1)

使用getNamespaces()

my @ns_dei = grep { $_->name eq 'xmlns:dei' } $dom->documentElement()->getNamespaces();

die "Namespace definition must be unique!\n" if @ns_dei != 1;

my $xpc = XML::LibXML::XPathContext->new($dom);
$xpc->registerNs( 'dei', $ns_dei[0]->value );

答案 2 :(得分:1)

dei不是名称空间;它是一个仅在该特定文档中有意义的前缀。您不能指望命名空间的前缀始终为dei

http://xbrl.sec.gov/dei/2014-01-31是命名空间。这是无法改变的事情,你应该以你的代码为基础。

在评论中,您提到必须处理多个规范。只需为您支持的每个规范创建一个XPath前缀。

use strict;
use warnings;

use LWP::Simple               qw( );
use XML::LibXML               qw( );
use XML::LibXML::XPathContext qw( );

my $url = 'https://www.sec.gov/Archives/edgar/data/1057051/000119312517099664/acef-20161231.xml';

my $xml = LWP::Simple::get($url);

my $doc = XML::LibXML->load_xml(string => $xml);

my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs( d1 => 'http://xbrl.sec.gov/dei/2012-01-31' );
$xpc->registerNs( d2 => 'http://xbrl.sec.gov/dei/2014-01-31' );

my @matches = $xpc->findnodes('//d1:TradingSymbol|//d2:TradingSymbol', $doc);
print "Number of matches = ", 0+@matches, "\n";

答案 3 :(得分:0)

永远不要使用正则表达式来处理XML:您的代码总是错误的。您的示例至少有五个错误:如果使用不同的前缀,它将无法匹配,如果使用单引号,它将无法匹配,如果“=”符号周围有空格,它将无法匹配,它将出错如果名称空间声明是重复的,并且如果源文档中存在“已注释掉的”XML,则会给出虚假匹配。

理论上不可能消除这些错误,因为正则表达式不足以正确解析XML。

始终使用真正的XML解析器和XPath。

答案 4 :(得分:0)

我理解您的问题是您读取的XML并不总是使用与命名空间相同的URI来附加到dei:前缀和您正在使用它的元素。

在这种情况下,你所坚持的XML设计不合理,并没有为此建立好的做法。这个XML使用的命名空间错误,你需要解决这个问题。有关信息,更改元素的命名空间是根据定义更改其名称,因此是您用来查找它的最基本信息。

最好的办法是忽略名称空间。你可以用

做到这一点
//*[local-name () = "TradingSymbol"]

如果您可以获得的不同命名空间的数量仅限于少数几个,您可以将它们全部列为dei:和dei2012:例如,并选择两者:

//dei:TradingSymbol | //dei2012:TradingSymbol

答案 5 :(得分:0)

感谢所有回答的人。在使用Perl从Internet上获取数据方面我很缺乏经验(在这个特殊情况下,SEC Edgar的文件)。所以我可能甚至都没有提出最聪明的问题。

业务问题(根据我的理解): 1)当公司使用XBRL归档其10K / Q时,SEC希望根据SEC公布的模式之一披露交易符号信息。 2)已知(并将增长)模式位置的完整列表:

-- http://taxonomies.xbrl.us/us-gaap/2009/non-gaap/dei-2009-01-31.xsd
-- https://xbrl.sec.gov/dei/2012/dei-2012-01-31.xsd
-- https://xbrl.sec.gov/dei/2013/dei-2013-01-31.xsd
-- https://xbrl.sec.gov/dei/2014/dei-2014-01-31.xsd

3)我想抓住这样的交易代码信息。

我现在明白了" dei" namespace-prefix没有实际意义。但似乎甚至名称空间名称本身,例如' http://xbrl.sec.gov/dei/2012-01-31'没有意义。只有架构位置才真正有意义。这是对的吗?

我的理解是XBRL实例文档引用了一个"映射"命名空间(例如http://xbrl.sec.gov/dei/2012-01-31)到架构位置。 (因此命名空间名称只需要是唯一的字符串。)

那么有没有办法修改ikegami的代码以使用架构位置而不是命名空间名称?

完整的XRBL归档示例: https://www.sec.gov/Archives/edgar/data/1057051/000119312517099664