2012-04-04 4 views
2

Ich habe eine XML-Datei, die das folgende Format hat:Kann ich einen geparsten XML-Pfad mit XML :: Simple zurückgeben?

<testsuite name="Conformance"> 
<testsuite name="Manageability"> 
    <testsuite name="MIBs"> 
    <testcase internalid="1" name="name1">...</testcase> 
    <testcase internalid="2" name="name2">...</testcase> 
    </testsuite> 
</testsuite> 
</testsuite> 

Mit Perl XML::Simple Ich versuche, eine Liste von Testfällen und ihren Weg zu bekommen, so in diesem Fall wäre das Ergebnis:

Conformance/Manageability/MIBs 
    name1 
    name2 

Kann ich das mit XML :: Simple tun und wenn ja, wie würde der Anruf aussehen?

Meine aktuelle Skript:

use strict; 
use warnings; 
use Data::Dumper; 
#use XML::Twig; 
use XML::Simple; 

my $file = 'test.xml'; 

my $ref = XMLin($file); 

print Dumper($ref); 

ich einige Dinge ausprobiert habe, aber kann nicht scheinen, zu bekommen, was ich brauche. Ist es einfacher, die zurückgegebene Datenstruktur zu analysieren, um das zu bekommen, was ich brauche?

+0

Warum Rat ist, dass Sie mit Hilfe von XML sollten aufhören :: Simple, wenn Sie in das erste Problem führen Sie es. Es ist Zeit, sich auf etwas einzulassen, das nicht so viele Entscheidungen für dich trifft. –

Antwort

2

Rekursion passt hier perfekt.

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> 

Der Wurzelknoten sollte wirklich kein testsuite Knoten sein, aber es ist das, was Sie sagen, Sie haben.

+0

Das macht den Trick ... Danke! – user1216398

0

XML::Simple verletzt die "machen Sie alles so einfach wie möglich, nicht einfacher" in fast allen, aber die einfachsten Fälle.

Es sieht aus wie ich missverstanden Ihre Anforderungen beim ersten Mal, also hier ist ein anderer Weg - aber ich erwarte es viel schlimmer als @ Ikegamis Lösung, weil es zuerst alle testcase Knoten findet, und dann zurück zu ihren Eltern .

#!/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> 

Ausgang:

/Conformance/Manageability/MIBs 
     name1 
     name2 
/Conformance/Yabadabadoo/Da da da 
     name1 
     name2
+0

Bei der Frage ging es auch um die Ausgabe "Conformance/Manageability/MIBs" – ikegami

2

XML::Simple verwenden? Hören Sie, was der Autor dieses Moduls zu sagen hat:

Allerdings würde ich empfehlen, nicht mit XML :: Simple (und ich weiß, sollte - ich es geschrieben hat). Ich persönlich benutze XML :: LibXML.

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

Tun Sie sich selbst einen Gefallen und lernen, die richtige Art und Weise, die die meiste Zeit XML::LibXML bedeutet. Das ist die C-Bibliothek, die auch in PHP, Python und Ruby verwendet wird. Kompiliert unter sehr UNIX und WINDOWS. Tragbar. Schnell. Standard-APIs Der Weg, den man gehen sollte.

2

Da Sie mit XML :: Twig versucht haben, hier ist eine Lösung dafür. Wenn es einen testcase findet, prüft es, ob es der erste in testsuite ist, wenn es den Pfad druckt, indem er die Vorfahren des Elements verwendet. Dann druckt es den Namen des Testfalls.

2 Anmerkungen: a testcase ist die erste, wenn sie nicht einen vorherigen testcase Geschwister haben und ancestors kehrt die Vorfahren der Elemente aus den inneren einem (das Elemente parent) zum äußeren (die Wurzel), so In diesem Fall müssen wir die Liste umkehren, um sie in der gewünschten Reihenfolge zu erhalten.

Voilà:

#!/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>