在 Perl 中从 XML 获取 XSI 类型
Get XSI Type from XML in Perl
在根文件夹的不同子文件夹中有一堆 XML 文件。其中一些有以下内容。
XML-1
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Channels>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-1</CableType>
<Name>C-SPAN</Name>
</Genre>
<displayName>C-SPAN Network</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Sports">
<CableType>XY-2</CableType>
<Name>Fox</Name>
</Genre>
<displayName>Fox Sports</displayName>
</Channels>
XML-2
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Channels>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-1</CableType>
<Name>ABC</Name>
</Genre>
<displayName>ABC News</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Movies">
<CableType>XY-2</CableType>
<Name>HBO</Name>
</Genre>
<displayName>HBO Movies</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-3</CableType>
<Name>CBS</Name>
</Genre>
<displayName>CBS News</displayName>
</Channels>
XML-3
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Channels>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-1</CableType>
<Name>PBS</Name>
</Genre>
<displayName>PBS News</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Sports">
<CableType>XY-@</CableType>
<Name>ESPN</Name>
</Genre>
<displayName>ESPN Network</displayName>
</Channels>
目标是遍历所有子文件夹并解析 XML 并查找 xsi:type 值。大多数 XML 预计只会有一个 xsi:type=News 。但是在这种情况下,XML-2 中有 2 个 xsi:type=News。
下面是一个 perl 脚本,到目前为止我可以想出它来遍历所有子文件夹并找到 XML 文件并将其添加到数组列表中。现在需要一些帮助来查找包含多个 xsi:type=News.
的 XML 个文件
my $dir = "C:\perl_scripts";
use File::Find;
find(
{
wanted => \&findfiles,
},
$dir
);
sub findfiles
{
}
my @file_list;
find ( sub {
return unless -f; #Must be a file
return unless /\.xml$/; #Must end with `.xml` suffix
push @file_list, $File::Find::name;
}, $dir );
foreach my $title (@file_list) {
say $title;
}
如何获取xsi:type=News > 1 的总数然后打印到控制台?
对于以上3 XMLs,它应该打印XML-2.
更新:
这是最终代码,
use feature qw(say);
use strict;
use warnings;
use XML::LibXML;
my $dir = "C:\perl_scripts";
use File::Find;
find(
{
wanted => \&findfiles,
},
$dir
);
sub findfiles
{
}
my @file_list;
find ( sub {
return unless -f; #Must be a file
return unless /\.xml$/; #Must end with `.xml` suffix
push @file_list, $File::Find::name;
}, $dir );
foreach my $title (@file_list){
my $doc = XML::LibXML->load_xml(location => $title);
my %xsi_type;
for my $node ($doc->findnodes('//Genre')) {
$xsi_type{ $node->getAttribute('xsi:type') }++;
}
if ($xsi_type{News} > 1) {
print 'Found file with more than one xsi:type="News" ==> ';
say $title;
}
}
这是一个示例,说明如何使用 XML::LibXML
确定一个文件是否有多个带有 xsi:type="News"
的标签:
use feature qw(say);
use strict;
use warnings;
use XML::LibXML;
my $xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Channels>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-1</CableType>
<Name>ABC</Name>
</Genre>
<displayName>ABC News</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Movies">
<CableType>XY-2</CableType>
<Name>HBO</Name>
</Genre>
<displayName>HBO Movies</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-3</CableType>
<Name>CBS</Name>
</Genre>
<displayName>CBS News</displayName>
</Channels>';
my $doc = XML::LibXML->load_xml(string => $xml);
my %xsi_type;
for my $node ($doc->findnodes('//Genre')) {
$xsi_type{ $node->getAttribute('xsi:type') }++;
}
if ($xsi_type{News} > 1) {
say 'Found file with more than one xsi:type="News"';
}
您可以要求 News
类型的第二个 Genre
。[1]
此外,File::Find::Rule 比 File::Find 干净得多。
use strict;
use warnings;
use File::Find::Rule qw( );
use XML::LibXML qw( );
use XML::LibXML::XPathContext qw( );
my $root_dir_qfn = $ARGV[0] // ".";
my $xpc = XML::LibXML::XPathContext->new;
$xpc->registerNs( xsi => "http://www.w3.org/2001/XMLSchema-instance" );
for my $qfn (
File::Find::Rule
->file
->name( "*.xml" )
->in( $dir_qfn )
) {
my $doc = XML::LibXML->load_xml( string => $xml );
if ( () = $xpc->findnodes( '/Channels/Genre[@xsi:type="News"][2]', $doc ) ) {
warn( "$qfn: Found multiple News channels\n" );
}
}
之前的答案建议遍历所有 Genres
元素,但没有必要这样做。
这个答案还展示了如何正确使用命名空间。依赖特定前缀是不可接受的,因为任何前缀都可以使用,只要它具有与之关联的正确命名空间。例如,<Genre xmlns:foo="http://www.w3.org/2001/XMLSchema-instance" foo:type="News">
是合法的,我的程序将正确识别此元素(与之前的答案不同)。
在根文件夹的不同子文件夹中有一堆 XML 文件。其中一些有以下内容。
XML-1
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Channels>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-1</CableType>
<Name>C-SPAN</Name>
</Genre>
<displayName>C-SPAN Network</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Sports">
<CableType>XY-2</CableType>
<Name>Fox</Name>
</Genre>
<displayName>Fox Sports</displayName>
</Channels>
XML-2
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Channels>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-1</CableType>
<Name>ABC</Name>
</Genre>
<displayName>ABC News</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Movies">
<CableType>XY-2</CableType>
<Name>HBO</Name>
</Genre>
<displayName>HBO Movies</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-3</CableType>
<Name>CBS</Name>
</Genre>
<displayName>CBS News</displayName>
</Channels>
XML-3
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Channels>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-1</CableType>
<Name>PBS</Name>
</Genre>
<displayName>PBS News</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Sports">
<CableType>XY-@</CableType>
<Name>ESPN</Name>
</Genre>
<displayName>ESPN Network</displayName>
</Channels>
目标是遍历所有子文件夹并解析 XML 并查找 xsi:type 值。大多数 XML 预计只会有一个 xsi:type=News 。但是在这种情况下,XML-2 中有 2 个 xsi:type=News。
下面是一个 perl 脚本,到目前为止我可以想出它来遍历所有子文件夹并找到 XML 文件并将其添加到数组列表中。现在需要一些帮助来查找包含多个 xsi:type=News.
的 XML 个文件my $dir = "C:\perl_scripts";
use File::Find;
find(
{
wanted => \&findfiles,
},
$dir
);
sub findfiles
{
}
my @file_list;
find ( sub {
return unless -f; #Must be a file
return unless /\.xml$/; #Must end with `.xml` suffix
push @file_list, $File::Find::name;
}, $dir );
foreach my $title (@file_list) {
say $title;
}
如何获取xsi:type=News > 1 的总数然后打印到控制台?
对于以上3 XMLs,它应该打印XML-2.
更新:
这是最终代码,
use feature qw(say);
use strict;
use warnings;
use XML::LibXML;
my $dir = "C:\perl_scripts";
use File::Find;
find(
{
wanted => \&findfiles,
},
$dir
);
sub findfiles
{
}
my @file_list;
find ( sub {
return unless -f; #Must be a file
return unless /\.xml$/; #Must end with `.xml` suffix
push @file_list, $File::Find::name;
}, $dir );
foreach my $title (@file_list){
my $doc = XML::LibXML->load_xml(location => $title);
my %xsi_type;
for my $node ($doc->findnodes('//Genre')) {
$xsi_type{ $node->getAttribute('xsi:type') }++;
}
if ($xsi_type{News} > 1) {
print 'Found file with more than one xsi:type="News" ==> ';
say $title;
}
}
这是一个示例,说明如何使用 XML::LibXML
确定一个文件是否有多个带有 xsi:type="News"
的标签:
use feature qw(say);
use strict;
use warnings;
use XML::LibXML;
my $xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Channels>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-1</CableType>
<Name>ABC</Name>
</Genre>
<displayName>ABC News</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Movies">
<CableType>XY-2</CableType>
<Name>HBO</Name>
</Genre>
<displayName>HBO Movies</displayName>
<Genre xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="News">
<CableType>XY-3</CableType>
<Name>CBS</Name>
</Genre>
<displayName>CBS News</displayName>
</Channels>';
my $doc = XML::LibXML->load_xml(string => $xml);
my %xsi_type;
for my $node ($doc->findnodes('//Genre')) {
$xsi_type{ $node->getAttribute('xsi:type') }++;
}
if ($xsi_type{News} > 1) {
say 'Found file with more than one xsi:type="News"';
}
您可以要求 News
类型的第二个 Genre
。[1]
此外,File::Find::Rule 比 File::Find 干净得多。
use strict;
use warnings;
use File::Find::Rule qw( );
use XML::LibXML qw( );
use XML::LibXML::XPathContext qw( );
my $root_dir_qfn = $ARGV[0] // ".";
my $xpc = XML::LibXML::XPathContext->new;
$xpc->registerNs( xsi => "http://www.w3.org/2001/XMLSchema-instance" );
for my $qfn (
File::Find::Rule
->file
->name( "*.xml" )
->in( $dir_qfn )
) {
my $doc = XML::LibXML->load_xml( string => $xml );
if ( () = $xpc->findnodes( '/Channels/Genre[@xsi:type="News"][2]', $doc ) ) {
warn( "$qfn: Found multiple News channels\n" );
}
}
之前的答案建议遍历所有
Genres
元素,但没有必要这样做。这个答案还展示了如何正确使用命名空间。依赖特定前缀是不可接受的,因为任何前缀都可以使用,只要它具有与之关联的正确命名空间。例如,
<Genre xmlns:foo="http://www.w3.org/2001/XMLSchema-instance" foo:type="News">
是合法的,我的程序将正确识别此元素(与之前的答案不同)。