2

我有一个具有以下格式的 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);

我已经尝试了几件事,但似乎无法得到我需要的东西。解析返回的数据结构以获得我需要的东西是否更容易?

4

4 回答 4

2

使用XML::Simple? 听听该模块的作者怎么说:

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

来源:RE:帮助访问由 XML::Simple 生成的未知数据集

帮自己一个忙,学习正确的方法,这在大多数情况下意味着XML::LibXML。那是 C 库,它也用于 PHP、Python 和 Ruby。在 UNIX 和 WINDOWS 上编译。便携的。快速地。标准 API。要走的路。

于 2012-04-04T21:37:04.783 回答
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节点,但它就是你所说的。

于 2012-04-04T22:16:48.283 回答
2

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

2注意:testcase如果没有前一个testcase兄弟,则a是第一个,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>
于 2012-04-05T07:55:17.897 回答
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>

输出:

/一致性/可管理性/MIB
        名称1
        名称2
/一致性/Yabadabadoo/哒哒哒
        名称1
        名称2
于 2012-04-04T21:27:03.477 回答