我有一个XML文件,格式如下:
<testsuite name="Conformance">
<testsuite name="Manageability">
<testsuite name="MIBs">
<testcase internalid="1" name="name1">...</testcase>
<testcase internalid="2" name="name2">...</testcase>
</testsuite>
</testsuite>
</testsuite>
使用Perl的XML::Simple我试图获取一个测试用例列表及其路径,所以在这种情况下结果将是:
Conformance/Manageability/MIBs
name1
name2
我可以使用XML :: Simple执行此操作,如果是,那么调用会是什么样的?
我目前的剧本:
use strict;
use warnings;
use Data::Dumper;
#use XML::Twig;
use XML::Simple;
my $file = 'test.xml';
my $ref = XMLin($file);
print Dumper($ref);
我尝试了几件事,但似乎无法得到我需要的东西。是否更容易解析返回的数据结构以获得我需要的东西?
答案 0 :(得分:2)
使用XML::Simple
?聆听该模块的作者所说的内容:
但是我建议不要使用XML :: Simple(我应该知道 - 我 写了)。我个人使用XML :: LibXML。
来源:RE: Help with accessing an unknown set of data generated by XML::Simple
帮自己一个忙,并学习正确的方法,大部分时间都是XML::LibXML
。这是C库,也用于PHP,Python和Ruby。在非常UNIX和WINDOWS上编译。便携。快速。标准API。要走的路。
答案 1 :(得分:2)
递归非常适合这里。
use strict;
use warnings;
use XML::LibXML qw( );
sub visit_testsuite {
my ($testsuite_node, $parent_path) = @_;
my $name = $testsuite_node->getAttribute('name');
my $path = defined($parent_path) ? "$parent_path/$name" : $name;
my @testcase_nodes = $testsuite_node->findnodes('testcase');
if (@testcase_nodes) {
print("$path\n");
for my $testcase_node (@testcase_nodes) {
printf(" %s\n", $testcase_node->getAttribute('name'));
}
print("\n");
}
for my $testsuite_child ($testsuite_node->findnodes('testsuite')) {
visit_testsuite($testsuite_child, $path);
}
}
my $doc = XML::LibXML->load_xml( IO => \*DATA );
my $root = $doc->documentElement();
visit_testsuite($root);
__DATA__
<testsuite name="Conformance">
<testsuite name="Manageability">
<testsuite name="MIBs">
<testcase internalid="1" name="name1">...</testcase>
<testcase internalid="2" name="name2">...</testcase>
</testsuite>
</testsuite>
</testsuite>
根节点确实不应该是testsuite
节点,但这就是你所说的。
答案 2 :(得分:2)
因为您尝试使用XML :: Twig,所以这是一个解决方案。当它找到testcase
时,它检查它是否是testsuite
中的第一个,如果它是打印路径,则使用元素的祖先。然后它打印测试用例的名称。
2注意:testcase
是第一个,如果它没有先前的testcase
兄弟,ancestors
返回内部元素的祖先(元素父元素) )到外部(根),所以在这种情况下,我们需要反转列表以使它们按所需的顺序。
瞧:
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
XML::Twig->new( twig_handlers => { testcase => \&test_case })
->parse( \*DATA);
sub test_case
{ my( $t, $test_case)= @_;
if( ! $test_case->prev_sibling( 'testcase'))
{ # first test case, output the "path"
print join( '/', map { $_->att( 'name') } reverse $test_case->ancestors( 'testsuite')), "\n";
}
print " ", $test_case->att( 'name'),"\n";
}
__DATA__
<testsuite name="Conformance">
<testsuite name="Manageability">
<testsuite name="MIBs">
<testcase internalid="1" name="name1">...</testcase>
<testcase internalid="2" name="name2">...</testcase>
</testsuite>
</testsuite>
</testsuite>
答案 3 :(得分:0)
XML::Simple
在几乎所有情况下都违反“使一切尽可能简单,而不是简单”。
看起来我第一次误解了你的要求,所以这是另一种方式 - 但是,我希望它比@ ikegami的解决方案更糟糕,因为它首先找到所有testcase
节点,然后追溯给他们的父母。
#!/usr/bin/env perl
use strict; use warnings;
use XML::XPath;
use XML::XPath::XMLParser;
my $xp = XML::XPath->new(ioref => \*DATA);
my $nodeset = $xp->find('//testcase');
my %cases;
foreach my $node ($nodeset->get_nodelist) {
my $current = $node;
my @parents;
while (defined(my $parent = $current->getParentNode)) {
my $name = $parent->getAttribute('name');
last unless defined $name;
push @parents, $name;
$current = $parent;
}
my $path = join('/', '', reverse @parents);
push @{ $cases{ $path } }, $node->getAttribute('name');
}
for my $path (sort keys %cases) {
print "$path\n";
for my $case (sort @{ $cases{$path} }) {
print "\t$case\n";
}
}
__DATA__
<testsuite name="Conformance">
<testsuite name="Manageability">
<testsuite name="MIBs">
<testcase internalid="1" name="name1">...</testcase>
<testcase internalid="2" name="name2">...</testcase>
</testsuite>
</testsuite>
<testsuite name="Yabadabadoo">
<testsuite name="Da da da">
<testcase internalid="1" name="name1">...</testcase>
<testcase internalid="2" name="name2">...</testcase>
</testsuite>
</testsuite>
</testsuite>
输出:
/Conformance/Manageability/MIBs name1 name2 /Conformance/Yabadabadoo/Da da da name1 name2