2012-04-04 2 views
2

У меня есть файл XML, который имеет следующий формат:Могу ли я вернуть анализируемый XML-путь с XML :: Simple?

<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 Я пытаюсь получить список testcases и их путь, чтобы в этом случае результат будет:

Conformance/Manageability/MIBs 
    name1 
    name2 

Могу ли я сделать это с помощью XML :: Простой, и если да, то как бы выглядел вызов?

Мой текущий сценарий:

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 :: Simple, когда вы столкнетесь с первой проблемой его использования. Пришло время перейти к чему-то, что не принимает для вас столько решений. –

ответ

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

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

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. Путь.

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> 
Смежные вопросы