2012-04-04 70 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> 
相關問題