使用XML :: Simple

时间:2015-07-07 19:07:38

标签: xml perl parsing xml-simple

我正在使用XML::Simple编写脚本的一半。我读过这不是那么简单",甚至它自己的文档都不鼓励在新代码中使用它,但我别无选择,因为这个脚本将成为现有代码的扩展。

我正在做的是这个

  1. 通过阅读网址获取XML
  2. 使用XML::Simple
  3. 解析它
  4. 从数据中读取所需元素
  5. 对这些必需元素运行不同的检查
  6. 我可以解析并对一些元素进行一些检查,但在读取数组中的元素时,我得到了undef

    这是我的代码:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use LWP::UserAgent;
    use LWP::Simple;
    use XML::Simple;
    use DBI;
    
    use Data::Dumper;
    
    my $str = "<Actual_URL>";
    
    my $ua = LWP::UserAgent->new;
    $ua->timeout( 180 );
    $ua->agent( "$0/0.1 " . $ua->agent );
    
    my $req = HTTP::Request->new( GET => $str );
    
    my $buffer;
    $req->content_type( 'text/xml' );
    $req->content( $buffer );
    
    my $response = $ua->request( $req );
    
    my $xml = $response->content();
    print "Value of \$xml is:\n";
    print $xml;
    
    my $filename = 'record.txt';
    open( my $fh, '>', $filename ) or die "Could not open file '$filename' $!";
    print $fh $xml;
    close $fh;
    
    my $number_of_lines = `wc -l record.txt | cut -d' ' -f1`;
    print "Number of lines in $filename are: $number_of_lines\n";
    if ( $number_of_lines >= 50 ) {
        print "TEST_1 SUCCESS\n";
    }
    
    my $mysql_dbh;
    my $test_id;
    
    my $xst;
    my %cmts_Pre_EQ_tags;
    
    if ( ( not defined $xml ) or ( $xml =~ m/read\stimeout/i ) ) {
        &printXMLErr( 'DRUM request timed out' );
    }
    else {
        my $xs = XML::Simple->new();
        $xst = eval { $xs->XMLin( $xml, KeyAttr => 1 ) };
        &printXMLErr( $@ ) if ( $@ );
        print "Value of \$xst inside is:\n";
        print Dumper( $xst );
    }
    
    $cmts_Pre_EQ_tags{'$cmts_Pre_EQ_groupDelayMag'} =
        $xst->{cmts}->{Pre_EQ}->{groupDelayMag}->{content};
    
    #More elements like this are checked here
    $cmts_Pre_EQ_tags{'$cmts_Pre_EQ_ICFR'} =
        $xst->{cmts}->{Pre_EQ}->{ICFR}->{content};
    
    my $decision1 = 1;
    print "\%cmts_Pre_EQ_tags:\n";
    foreach ( sort keys %cmts_Pre_EQ_tags ) {
        print "$_ : $cmts_Pre_EQ_tags{$_}\n";
        if ( $cmts_Pre_EQ_tags{$_} eq '' ) {
            print "$_ is empty!\n";
            $decision1 = 0;
        }
    }
    print "\n";
    
    if ( $decision1 == 0 ) {
        print "TEST_2_1 FAIL\n";
    }
    else {
        print "TEST_2_1 SUCCESS\n";
    }
    
    my $cpeIP4 = $xst->{cmts}->{cpeIP4}->{content};
    print "The cpe IP is: $cpeIP4\n";
    
    if ( $cpeIP4 ne '' ) {
        print "TEST_2_2 SUCCESS\n";
    }
    else {
        print "TEST_2_2 FAIL\n";
    }
    
    # Working fine until here, but following 2 print are showing undef
    print Dumper ( $xst->{cmts}{STBDSG}{dsg}[0]{dsgIfStdTunnelFilterTunnelId} );
    print Dumper ( $xst->{cmts}{STBDSG}{dsg}[0]{dsgIfStdTunnelFilterClientIdType} );
    print "After\n";
    

    最后三个印刷语句的输出是:

    $VAR1 = undef;
    $VAR1 = undef;
    After
    

    我无法提供整个XML或print Dumper($xst)的输出,因为它太大而且动态生成,但我会提供它的示例。

    导致问题的XML部分是

    <cmts>
      <STBDSG>
        <dsg>
          <dsgIfStdTunnelFilterTunnelId>1</dsgIfStdTunnelFilterTunnelId>
          <dsgIfStdTunnelFilterClientIdType>caSystemId</dsgIfStdTunnelFilterClientIdType>
        </dsg>
        <dsg>
          <dsgIfStdTunnelFilterTunnelId>2</dsgIfStdTunnelFilterTunnelId>
          <dsgIfStdTunnelFilterClientIdType>gaSystemId</dsgIfStdTunnelFilterClientIdType>
        </dsg>
      </STBDSG>
    </cmts>
    

    当解析这部分时,$xst中的相应输出是

    $VAR1 = {
        'cmts' => {
                'STBDSG' => {
                    'dsg' => [
                             {
                               'dsgIfStdTunnelFilterTunnelId' => '1',
                               'dsgIfStdTunnelFilterClientIdType' => 'caSystemId',
                             },
                             {
                               'dsgIfStdTunnelFilterTunnelId' => '2',
                               'dsgIfStdTunnelFilterClientIdType' => 'gaSystemId',
                             }
                             ]
                         },
        },
    };
    

    解析值的XML部分很好,就像这样

    <cmts>
        <name field_name="Name">cts01nsocmo</name>
        <object field_name="Nemos Object">888</object>
        <vendor field_name="Vendor">xyz</vendor>
    </cmts>
    

    转换为:

        $VAR1 = {
          'cmts' => {
            'name' => {
                        'content' => 'cts01nsocmo',
                        'field_name' => 'Name'
                      },
            'object' => {
                          'content' => '888',
                          'field_name' => 'Nemos Object'
                        },
            'vendor' => {
                          'content' => 'xyz',
                          'field_name' => 'Vendor'
                        }
             },
    };
    

    所以基本上当解析内容中没有数组时,值会在变量中正确获取。

    似乎这就是为什么这个

    print Dumper ( $xst->{cmts}{STBDSG}{dsg}[0]{dsgIfStdTunnelFilterTunnelId} );
    print Dumper ( $xst->{cmts}{STBDSG}{dsg}[0]{dsgIfStdTunnelFilterClientIdType} );
    

    获得undef与将{0}}或KeyAttr设置正确的值相关。我试图通过阅读ForceArray找到它,但我想知道我在这里找不到一些与众不同的东西。

2 个答案:

答案 0 :(得分:4)

值得考虑使用XML::Twig,无论项目的其余部分是做什么

特别是,XML::Twig::Elt个对象 - 模块的XML元素实现 - 有一个simplify方法,其文档说明了这个

  

返回一个与XML :: Simple相似的数据结构。选项与XMLin选项相同

因此,您可以使用XML::Twig来提高其准确性和便利性,如果需要传递任何看似simplify数据结构的数据,则应用XML::Simple方法

答案 1 :(得分:1)

正如您所发现的那样 - XML::Simple,并非如此。即使它的文档建议:

  

不鼓励在新代码中使用此模块。其他模块可用,提供更直接和一致的接口。

部分问题是 - XML没有像数组这样的东西。它可能有重复的标签。但同样 - &#39;阵列&#39;之间没有线性映射。和&#39; XML&#39;因此它总是让编程感到不舒服。

它对你做的是假设dsg元素是一个数组,并自动投射它们。

无论如何,我建议改为使用XML::Twig - 然后再打印&#39;打印&#39;陈述就像这样:

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig->new->parse( \*DATA );

foreach my $element ( $twig->get_xpath( "cmts/STBDSG/dsg", 0 ) ) {
    print $element ->first_child_text("dsgIfStdTunnelFilterTunnelId"), "\n";
    print $element ->first_child_text("dsgIfStdTunnelFilterClientIdType"),
        "\n";
}

无论如何,如果您被迫使用XML::Simple - 并将其扔掉并重新开始并不是一个选择。 (因为说真的,我考虑一下!)。

XML :: Simple使用&#39;匹配&#39;元素是尝试并假装他们的阵列。

如果没有匹配的元素,它会将它们视为散列。这可能是什么让你感动。问题是 - 在perl中,哈希不能有重复的键 - 所以你的例子dsg - 而不是重复它,它就是数组 - 它。

启用ForceArray所有内容放入数组中,但某些数组可能是单个元素。如果你想要一致性,这很有用。

KeyAttr可能对你没有帮助 - 这主要是为了拥有不同的子元素而你想要映射&#39;他们。它允许您将其中一个XML属性转换为&#39;键&#39;哈希中的字段。

E.g。

<element name="firstelement">content</element>
<element name="secondelement">morecontent</element>

如果您将KeyAttr指定为name,则会使用firstelementsecondelement的密钥进行哈希处理。

由于您的dsg没有这个,那么这不是您想要的。

迭代dsg

foreach my $element ( @{ $xst->{cmts}{STBDSG}{dsg} } ) {
    print $element ->{dsgIfStdTunnelFilterTunnelId},     "\n";
    print $element ->{dsgIfStdTunnelFilterClientIdType}, "\n";
}