使用 XML::Simple 从已解析的 XML 数据中读取值时出现问题
Trouble in reading values from parsed XML data using XML::Simple
我正在使用 XML::Simple
编写脚本。我读到情况并非如此 "simple",甚至它自己的文档也不鼓励在新代码中使用它,但我别无选择,因为这个脚本将是对现有代码的扩展。
我做的是这个
- 通过读取 URL
获得 XML
- 使用
XML::Simple
解析
- 从数据中读取需要的元素
- 运行 对这些必需元素的不同检查
我可以解析并检查一些元素,但是在读取数组中的元素时,我得到 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( "[=12=]/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
与将正确的值设置为 KeyAttr
或 ForceArray
有关。我试图通过阅读 XML::Simple
找到它,但我想看看这里是否遗漏了一些不同的东西。
如您所见 - XML::Simple
,事实并非如此。甚至它的文档也表明:
The use of this module in new code is discouraged. Other modules are available which provide more straightforward and consistent interfaces.
部分问题是 - XML 没有数组之类的东西。它可能有重复的标签。但因此 - 'array' 和 'XML' 之间没有线性映射,所以它总是让编程不舒服。
它对你所做的是假设 dsg
元素是一个数组,并自动转换它们。
无论如何,我建议改用 XML::Twig
- 然后您的 'print' 语句如下所示:
#!/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 对 'matching' 元素的作用是尝试假装它们是数组。
如果没有匹配的元素,它会将它们视为散列。这可能就是让你脱颖而出的原因。问题是 - 在 perl 中,散列不能有重复的键 - 所以你的例子 dsg
- 它不是复制它,而是数组化它。
打开 ForceArray
将 所有内容 放入数组,但某些数组可能是单个元素。如果您想要一致性,那将很有用。
KeyAttr
可能对您没有帮助 - 这主要适用于具有不同的子元素并且您想要 'map' 它们。它允许您将 XML 属性之一转换为散列中的 'key' 字段。
例如
<element name="firstelement">content</element>
<element name="secondelement">morecontent</element>
如果您将 KeyAttr
指定为 name
,它将使用 firstelement
和 secondelement
的键生成散列。
因为你的dsg
没有这个,那不是你想要的。
迭代 dsg
:
foreach my $element ( @{ $xst->{cmts}{STBDSG}{dsg} } ) {
print $element ->{dsgIfStdTunnelFilterTunnelId}, "\n";
print $element ->{dsgIfStdTunnelFilterClientIdType}, "\n";
}
值得考虑使用 XML::Twig
,无论您的项目的其余部分做什么
特别是,XML::Twig::Elt
对象——模块对 XML 元素的实现——有一个 simplify
方法,其文档说这个
Return a data structure suspiciously similar to XML::Simple's. Options are identical to XMLin options
因此,您可以使用 XML::Twig
的精确性和便利性,如果您需要传递任何看起来像 XML::Simple
数据结构的数据,请应用 simplify
方法
我正在使用 XML::Simple
编写脚本。我读到情况并非如此 "simple",甚至它自己的文档也不鼓励在新代码中使用它,但我别无选择,因为这个脚本将是对现有代码的扩展。
我做的是这个
- 通过读取 URL 获得 XML
- 使用
XML::Simple
解析
- 从数据中读取需要的元素
- 运行 对这些必需元素的不同检查
我可以解析并检查一些元素,但是在读取数组中的元素时,我得到 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( "[=12=]/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
与将正确的值设置为 KeyAttr
或 ForceArray
有关。我试图通过阅读 XML::Simple
找到它,但我想看看这里是否遗漏了一些不同的东西。
如您所见 - XML::Simple
,事实并非如此。甚至它的文档也表明:
The use of this module in new code is discouraged. Other modules are available which provide more straightforward and consistent interfaces.
部分问题是 - XML 没有数组之类的东西。它可能有重复的标签。但因此 - 'array' 和 'XML' 之间没有线性映射,所以它总是让编程不舒服。
它对你所做的是假设 dsg
元素是一个数组,并自动转换它们。
无论如何,我建议改用 XML::Twig
- 然后您的 'print' 语句如下所示:
#!/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 对 'matching' 元素的作用是尝试假装它们是数组。
如果没有匹配的元素,它会将它们视为散列。这可能就是让你脱颖而出的原因。问题是 - 在 perl 中,散列不能有重复的键 - 所以你的例子 dsg
- 它不是复制它,而是数组化它。
打开 ForceArray
将 所有内容 放入数组,但某些数组可能是单个元素。如果您想要一致性,那将很有用。
KeyAttr
可能对您没有帮助 - 这主要适用于具有不同的子元素并且您想要 'map' 它们。它允许您将 XML 属性之一转换为散列中的 'key' 字段。
例如
<element name="firstelement">content</element>
<element name="secondelement">morecontent</element>
如果您将 KeyAttr
指定为 name
,它将使用 firstelement
和 secondelement
的键生成散列。
因为你的dsg
没有这个,那不是你想要的。
迭代 dsg
:
foreach my $element ( @{ $xst->{cmts}{STBDSG}{dsg} } ) {
print $element ->{dsgIfStdTunnelFilterTunnelId}, "\n";
print $element ->{dsgIfStdTunnelFilterClientIdType}, "\n";
}
值得考虑使用 XML::Twig
,无论您的项目的其余部分做什么
特别是,XML::Twig::Elt
对象——模块对 XML 元素的实现——有一个 simplify
方法,其文档说这个
Return a data structure suspiciously similar to XML::Simple's. Options are identical to XMLin options
因此,您可以使用 XML::Twig
的精确性和便利性,如果您需要传递任何看起来像 XML::Simple
数据结构的数据,请应用 simplify
方法