在 Perl 中从 XML 文件获取元素
Getting elements from XML file in Perl
我在服务器上有一个文件,我想用 Perl 解析它。我已经用 XML:Simple
和 XML:LibXML
试过了,但在这两种情况下我都无法获得 xml 元素。
这是我的 .xml 文件:
<csixml version="1.0">
<head>
<details>
<name-link>linkName</name-link>
<table>links</table>
<model>XS1-556</model>
</details>
<fields>
<field name="name1" />
<field name="name2"/>
<field name="name3"/>
<field name="name4"/>
<field name="name5"/>
<field name="name6" />
<field name="name7"/>
<field name="name8"/>
<field name="name9"/>
<field name="name10"/>
<field name="name11"/>
<field name="name12x"/>
<field name="name13"/>
<field name="name14"/>
<field name="name15"/>
<field name="name16"/>
<field name="name17"/>
</fields>
</head>
<data>
<record time="2017/06/01 00:00:00" no="742">
<v1>14.85</v1>
<v2>34.1</v2>
<v3>600</v3>
<v4>0</v4>
<v5>0</v5>
<v6>0</v6>
<v7>0</v7>
<v8>11.22</v8>
<v9>0.41</v9>
<v10>215</v10>
<v11>7.043</v11>
<v12>1.325</v12>
<v13>2017-05-31T23:47:14</v13>
<v14>202.3</v14>
<v15>0</v15>
<v16>42.85</v16>
<v17>12.25</v17>
</record>
</data>
</csixml>
这是代码:
my $parser = new XML::Simple;
$data = $parser->XMLin( get( $url ));
#print Dumper($data);
print $data->{'r'}[0]{'v1'};
print $data->{'r'}[1]{'v2'};
当我用 XML:LibXML 尝试时,它给我一个错误:
Start tag expected, '<' not found
XML::Simple 不稳定,不应使用 (even the author agrees) 但是,话虽如此,这是让您的程序按预期工作的相对简单的修复方法。
您对数据结构的处理不正确。您需要仔细查看 Data:Dumper 输出。您的 $data
变量等同于顶级 <csixml>
标签。其他一切都是其中的哈希值。所以,要得到你想要的数据结构,你需要:
print $data->{data}{r}{v1}
print $data->{data}{r}{v2}
我还看到您正在使用 "indirect object notation" (new XML::Simple
) 创建您的解析器对象。这通常可以正常工作,但如果不正常,您将浪费数天时间来找出问题所在。相反,请使用标准语法 - XML::Simple->new
.
更新: 这是我使用的代码:
#!/usr/bin/perl
use strict;
use warnings;
use Path::Tiny;
use XML::Simple;
use Data::Dumper;
my $file = 'test.xml';
my $xml = path($file)->slurp;
my $parser = new XML::Simple;
my $data = $parser->XMLin($xml);
#print Dumper($data);
print $data->{data}{'r'}{'v1'};
print $data->{data}{'r'}{'v2'};
XML::LibXML 可能是在抱怨 XML 有一些问题。 XML 规范是严格的,除其他事项外还说 - 错误是致命的。但它有效:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::LibXML;
my $doc = XML::LibXML->load_xml ( IO => \*DATA );
foreach my $node ( $doc -> findnodes ( '//record/v2' ) ) {
print $node -> textContent;
}
__DATA__
<csixml version="1.0">
<head>
<details>
<name-link>linkName</name-link>
<table>links</table>
<model>XS1-556</model>
</details>
<fields>
<field name="name1" />
<field name="name2"/>
<field name="name3"/>
<field name="name4"/>
<field name="name5"/>
<field name="name6" />
<field name="name7"/>
<field name="name8"/>
<field name="name9"/>
<field name="name10"/>
<field name="name11"/>
<field name="name12x"/>
<field name="name13"/>
<field name="name14"/>
<field name="name15"/>
<field name="name16"/>
<field name="name17"/>
</fields>
</head>
<data>
<record time="2017/06/01 00:00:00" no="742">
<v1>14.85</v1>
<v2>34.1</v2>
<v3>600</v3>
<v4>0</v4>
<v5>0</v5>
<v6>0</v6>
<v7>0</v7>
<v8>11.22</v8>
<v9>0.41</v9>
<v10>215</v10>
<v11>7.043</v11>
<v12>1.325</v12>
<v13>2017-05-31T23:47:14</v13>
<v14>202.3</v14>
<v15>0</v15>
<v16>42.85</v16>
<v17>12.25</v17>
</record>
</data>
</csixml>
XML::LibXML 支持 xpath
这对于您尝试做的事情来说是无价的——您可以在文档中指定完整路径,或者用//
表示'anywhere in document'。
所以要么:
/csixml/data/record/v2
或者:
//record/v2
会找到你想要的值。
但也可以做其他有用的事情,例如:
foreach my $node ( $doc -> findnodes ( '//record/*[string()="34.1"]' ) ) {
print $node -> nodeName;
}
所以我认为这里的核心问题是您加载 XML 不正确。它在上面的示例中当然有效(IO => \*DATA
从特殊的内联 DATA
文件句柄加载,但它对您的示例工作正常)。
我已经尝试了所有的解决方案,但最终我找到了:
my ($_xml) = new XML::Simple (KeyAttr=>[]);
my $url = 'http://www.example.com';
my $agent = LWP::UserAgent->new;
my $request = HTTP::Request->new(GET => $url);
$request->content_type('application/xml');
my $response = $agent->request($request);
if ($response->is_success) {
print "HTTP response is good\n";
my ($_message) = $response->decoded_content;
my ($_data) = $_xml->XMLin($_message,ForceArray => 1);
foreach my $_e (@{$_data->{data}})
{
foreach my $_r (@{$_e->{r}})
{
print $_r->{time}.": ".$_r->{no}."\n";
}
}
} else {
die "Awooga! HTTP request failed with ". $response->status_line;
}
最后我使用了 XML:Simple
并得到了我的 xml 元素: $_r->{time}
并且效果很好。我希望这会帮助别人,谢谢大家!
我在服务器上有一个文件,我想用 Perl 解析它。我已经用 XML:Simple
和 XML:LibXML
试过了,但在这两种情况下我都无法获得 xml 元素。
这是我的 .xml 文件:
<csixml version="1.0">
<head>
<details>
<name-link>linkName</name-link>
<table>links</table>
<model>XS1-556</model>
</details>
<fields>
<field name="name1" />
<field name="name2"/>
<field name="name3"/>
<field name="name4"/>
<field name="name5"/>
<field name="name6" />
<field name="name7"/>
<field name="name8"/>
<field name="name9"/>
<field name="name10"/>
<field name="name11"/>
<field name="name12x"/>
<field name="name13"/>
<field name="name14"/>
<field name="name15"/>
<field name="name16"/>
<field name="name17"/>
</fields>
</head>
<data>
<record time="2017/06/01 00:00:00" no="742">
<v1>14.85</v1>
<v2>34.1</v2>
<v3>600</v3>
<v4>0</v4>
<v5>0</v5>
<v6>0</v6>
<v7>0</v7>
<v8>11.22</v8>
<v9>0.41</v9>
<v10>215</v10>
<v11>7.043</v11>
<v12>1.325</v12>
<v13>2017-05-31T23:47:14</v13>
<v14>202.3</v14>
<v15>0</v15>
<v16>42.85</v16>
<v17>12.25</v17>
</record>
</data>
</csixml>
这是代码:
my $parser = new XML::Simple;
$data = $parser->XMLin( get( $url ));
#print Dumper($data);
print $data->{'r'}[0]{'v1'};
print $data->{'r'}[1]{'v2'};
当我用 XML:LibXML 尝试时,它给我一个错误:
Start tag expected, '<' not found
XML::Simple 不稳定,不应使用 (even the author agrees) 但是,话虽如此,这是让您的程序按预期工作的相对简单的修复方法。
您对数据结构的处理不正确。您需要仔细查看 Data:Dumper 输出。您的 $data
变量等同于顶级 <csixml>
标签。其他一切都是其中的哈希值。所以,要得到你想要的数据结构,你需要:
print $data->{data}{r}{v1}
print $data->{data}{r}{v2}
我还看到您正在使用 "indirect object notation" (new XML::Simple
) 创建您的解析器对象。这通常可以正常工作,但如果不正常,您将浪费数天时间来找出问题所在。相反,请使用标准语法 - XML::Simple->new
.
更新: 这是我使用的代码:
#!/usr/bin/perl
use strict;
use warnings;
use Path::Tiny;
use XML::Simple;
use Data::Dumper;
my $file = 'test.xml';
my $xml = path($file)->slurp;
my $parser = new XML::Simple;
my $data = $parser->XMLin($xml);
#print Dumper($data);
print $data->{data}{'r'}{'v1'};
print $data->{data}{'r'}{'v2'};
XML::LibXML 可能是在抱怨 XML 有一些问题。 XML 规范是严格的,除其他事项外还说 - 错误是致命的。但它有效:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::LibXML;
my $doc = XML::LibXML->load_xml ( IO => \*DATA );
foreach my $node ( $doc -> findnodes ( '//record/v2' ) ) {
print $node -> textContent;
}
__DATA__
<csixml version="1.0">
<head>
<details>
<name-link>linkName</name-link>
<table>links</table>
<model>XS1-556</model>
</details>
<fields>
<field name="name1" />
<field name="name2"/>
<field name="name3"/>
<field name="name4"/>
<field name="name5"/>
<field name="name6" />
<field name="name7"/>
<field name="name8"/>
<field name="name9"/>
<field name="name10"/>
<field name="name11"/>
<field name="name12x"/>
<field name="name13"/>
<field name="name14"/>
<field name="name15"/>
<field name="name16"/>
<field name="name17"/>
</fields>
</head>
<data>
<record time="2017/06/01 00:00:00" no="742">
<v1>14.85</v1>
<v2>34.1</v2>
<v3>600</v3>
<v4>0</v4>
<v5>0</v5>
<v6>0</v6>
<v7>0</v7>
<v8>11.22</v8>
<v9>0.41</v9>
<v10>215</v10>
<v11>7.043</v11>
<v12>1.325</v12>
<v13>2017-05-31T23:47:14</v13>
<v14>202.3</v14>
<v15>0</v15>
<v16>42.85</v16>
<v17>12.25</v17>
</record>
</data>
</csixml>
XML::LibXML 支持 xpath
这对于您尝试做的事情来说是无价的——您可以在文档中指定完整路径,或者用//
表示'anywhere in document'。
所以要么:
/csixml/data/record/v2
或者:
//record/v2
会找到你想要的值。
但也可以做其他有用的事情,例如:
foreach my $node ( $doc -> findnodes ( '//record/*[string()="34.1"]' ) ) {
print $node -> nodeName;
}
所以我认为这里的核心问题是您加载 XML 不正确。它在上面的示例中当然有效(IO => \*DATA
从特殊的内联 DATA
文件句柄加载,但它对您的示例工作正常)。
我已经尝试了所有的解决方案,但最终我找到了:
my ($_xml) = new XML::Simple (KeyAttr=>[]);
my $url = 'http://www.example.com';
my $agent = LWP::UserAgent->new;
my $request = HTTP::Request->new(GET => $url);
$request->content_type('application/xml');
my $response = $agent->request($request);
if ($response->is_success) {
print "HTTP response is good\n";
my ($_message) = $response->decoded_content;
my ($_data) = $_xml->XMLin($_message,ForceArray => 1);
foreach my $_e (@{$_data->{data}})
{
foreach my $_r (@{$_e->{r}})
{
print $_r->{time}.": ".$_r->{no}."\n";
}
}
} else {
die "Awooga! HTTP request failed with ". $response->status_line;
}
最后我使用了 XML:Simple
并得到了我的 xml 元素: $_r->{time}
并且效果很好。我希望这会帮助别人,谢谢大家!