【问题标题】:Can I return a parsed XML path with XML::Simple?我可以使用 XML::Simple 返回已解析的 XML 路径吗?
【发布时间】:2012-04-18 14:09:36
【问题描述】:

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

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

【问题讨论】:

  • 为什么建议您在遇到第一个问题时停止使用 XML::Simple。是时候开始做一些不会为你做出太多决定的事情了。

标签: perl xml-parsing


【解决方案1】:

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

【讨论】:

  • 问题也是关于输出“一致性/可管理性/MIB”
【解决方案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。要走的路。

【讨论】:

    【解决方案3】:

    递归非常适合这里。

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

    【讨论】:

      【解决方案4】:

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

      2 注意:如果testcase 没有先前的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>
      

      【讨论】:

        猜你喜欢
        • 2015-10-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-11
        • 2012-05-13
        • 1970-01-01
        相关资源
        最近更新 更多