2012-04-04 68 views
2

我有一个具有以下格式的XML文件:我可以使用XML :: Simple返回解析的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

为什么建议是,当你使用它遇到的第一个问题,你应该停止使用XML ::简单。现在是时候提出一些不会为你做出如此多决定的事情了。 – 2012-04-07 20:51:42

回答

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节点,但它是你说你了。

+0

这样做的窍门......谢谢! – user1216398 2012-04-05 12:55:34

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
+0

问题是关于输出“一致性/可管理性/ MIBs” – ikegami 2012-04-04 22:04:25

2

使用XML::Simple?聆听该模块的作者必须说:

但是,我建议不要使用XML :: Simple(我应该知道 - 我 写道它)。我个人使用XML :: LibXML。

来源:RE: Help with accessing an unknown set of data generated by XML::Simple

帮自己一个忙,学习方法得当,大部分的时间是指XML::LibXML。这是在PHP,Python和Ruby中使用的C库。在非常UNIX和WINDOWS上编译。便携。快速。标准API。走的路。

2

由于您使用XML :: Twig进行了尝试,因此这里有一个解决方案。当它找到testcase时,它会检查它是否是testsuite中的第一个,如果它是通过使用元素的祖先打印路径。然后打印测试用例的名称。

2注:a 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> 
相关问题