在Perl中从XML文件获取元素

在Perl中从XML文件获取元素,xml,perl,url,xml-simple,Xml,Perl,Url,Xml Simple,我在服务器上有一个文件,我想用Perl解析它。我已经用XML:Simple和XML:LibXML尝试过了,但在这两种情况下我都无法获得XML元素 这是我的.xml文件: <csixml version="1.0"> <head> <details> <name-link>linkName</name-link> <table>links</table>

我在服务器上有一个文件,我想用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>
当我尝试使用XML:LibXML时,它会给我一个错误:

Start tag expected, '<' not found

Start-tag-expected,“XML::Simple是不可靠的,不应该使用(),但是,尽管如此,它是一个相对简单的修复程序,可以让程序按预期工作

您的数据结构走错了方向。您需要仔细查看数据:转储程序输出。您的
$data
变量相当于顶级
标记。其他的都是散列。因此,要获得所需的数据结构,您需要:

print $data->{data}{r}{v1}
print $data->{data}{r}{v2}
我还看到您正在使用“间接对象表示法”(
newxml::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'};

Simple是脆弱的,不应该使用(),但是,尽管如此,它是一个相对简单的修复程序,可以让您的程序按预期工作

您的数据结构走错了方向。您需要仔细查看数据:转储程序输出。您的
$data
变量相当于顶级
标记。其他的都是散列。因此,要获得所需的数据结构,您需要:

print $data->{data}{r}{v1}
print $data->{data}{r}{v2}
我还看到您正在使用“间接对象表示法”(
newxml::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'};

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>
或:

将找到您想要的值

但也可以做其他有用的事情,如:

foreach my $node ( $doc -> findnodes ( '//record/*[string()="34.1"]' ) ) {
   print $node -> nodeName;
}

所以我认为这里的核心问题是,您的XML加载不正确。它在上面的示例中确实有效(
IO=>\*数据
从特殊的内联
数据
文件句柄加载,但对于您的示例来说效果很好)

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>
或:

将找到您想要的值

但也可以做其他有用的事情,如:

foreach my $node ( $doc -> findnodes ( '//record/*[string()="34.1"]' ) ) {
   print $node -> nodeName;
}

所以我认为这里的核心问题是,您的XML加载不正确。它在上面的示例中确实有效(
IO=>\*数据
从特殊的内联
数据
文件句柄加载,但对于您的示例来说效果很好)

我尝试了所有这些解决方案,但最终我找到了:

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
,并使用
$\u r->{time}
获取XML元素,效果非常好。我希望这将有助于某人,谢谢大家

我尝试了所有这些解决方案,但最终我找到了:

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
,并使用
$\u r->{time}
获取XML元素,效果非常好。我希望这将有助于某人,谢谢大家

get($url)
返回什么?你测试过吗?是的,当你打印转储程序($data)时
它以正确的结构打印xml文件。您真的需要这样做。
获取($url)
返回什么?你测试过吗?是的,当你打印转储程序($data)时它以正确的结构打印xml文件。真的。首先感谢您的提示@Dave Cross,我刚刚使用了您的代码,它给了我一个错误:
不是C:\Users\dbz\test.pl第127行的哈希引用
@dbz:我想这清楚地说明了使用xml::Simple的问题。我会在一秒钟内将我的完整代码添加到我的答案中,但当我运行它时,我会得到一个完全是散列引用的数据结构。显然,你得到了别的东西。如果有帮助的话,我正在Ubuntu 17.04上使用XML::Simple version 2.22。首先感谢您的光临@Dave Cross,我刚刚使用了您的代码,它给了我一个错误:
不是C:\Users\dbz\test.pl第127行的哈希引用
@dbz:我想这清楚地说明了使用XML::Simple的问题。我会在一秒钟内将我的完整代码添加到我的答案中,但当我运行它时,我会得到一个完全是散列引用的数据结构。显然,你得到了别的东西。如果有帮助的话,我正在Ubuntu 17.04上使用XML::Simple 2.22版。