【问题标题】:XML::Simple & accessing complex structuresXML::Simple & 访问复杂结构
【发布时间】:2012-05-12 04:06:36
【问题描述】:

我在使用 Perl/XML::Simple 访问 XML 响应中的一些嵌套数据时遇到问题。打印的 XML 回复的摘录如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:SelectCmDeviceResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://schemas.cisco.com/ast/soap/">
<SelectCmDeviceResult xsi:type="ns1:SelectCmDeviceResult">
<TotalDevicesFound xsi:type="xsd:unsignedInt">3</TotalDevicesFound>
<CmNodes soapenc:arrayType="ns1:CmNode[3]" xsi:type="soapenc:Array" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<item xsi:type="ns1:CmNode">
<ReturnCode xsi:type="ns1:RisReturnCode">NotFound</ReturnCode>
<Name xsi:type="xsd:string">10.186.78.4</Name>
<NoChange xsi:type="xsd:boolean">false</NoChange>
<CmDevices soapenc:arrayType="ns1:CmDevice[0]" xsi:type="soapenc:Array"/>
</item>
<item xsi:type="ns1:CmNode">
<ReturnCode xsi:type="ns1:RisReturnCode">Ok</ReturnCode>
<Name xsi:type="xsd:string">10.186.78.68</Name>
<NoChange xsi:type="xsd:boolean">false</NoChange>
<CmDevices soapenc:arrayType="ns1:CmDevice[2]" xsi:type="soapenc:Array">
<item xsi:type="ns1:CmDevice">
<Name xsi:type="xsd:string">SEPD0574CF73FC0</Name>
<IpAddress xsi:type="xsd:string">10.186.79.41</IpAddress>
<DirNumber xsi:type="xsd:string">51251001-Registered,51251004-Registered,51251002-Registered</DirNumber>
<Class xsi:type="ns1:DeviceClass">Phone</Class>
<Model xsi:type="xsd:unsignedInt">404</Model>
<Product xsi:type="xsd:unsignedInt">303</Product>
<BoxProduct xsi:type="xsd:unsignedInt">0</BoxProduct>

下面是代码,它应该解析响应并返回返回设备的 IpAddress 值:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use XML::Simple;
use LWP;
use SOAP::Lite;

my $cucmip = "10.1.10.1";
my $axl_port = "8443";
my $user = "admin";
my $password = "password";
my $axltoolkit = "http://schemas.cisco.com/ast/soap/action/#RisPort#SelectCmDevice";

sub getDevIp {
    my $message = "<?xml POST message>

    my $url="https://$cucmip:$axl_port/realtimeservice/services/RisPort?wsdl";
    my $ua = LWP::UserAgent->new;
    my $header = new HTTP::Headers (
    'Content-Type' => 'application/xml; charset=utf-8',
    'SOAPAction' => 'http://schemas.cisco.com/ast/soap/action/#RisPort#SelectCmDevice',
    );
    my $req = HTTP::Request->new(POST => $url, $header, $message);
    $req->authorization_basic($user,$password);
    my $response = $ua->request($req);
    my $xs = new XML::Simple(KeyAttr=>[]);
    my $data = $xs->XMLin($response->content);
    print $data->{'soapenv:Body'}->{'ns1:SelectCmDeviceResponse'}->{'SelectCmDeviceResult'}->{'CmNodes'}->{'item'}->[0]->{'CmDevices'}->{'item'}->[0]->{'IpAddress'}->{'content'};
}

getDevIp();

【问题讨论】:

  • 你真的希望任何人水平来回滚动来理解这一点吗?如果你的数据结构太复杂,不要使用XML::Simple
  • 我确实滚动了一下,发现您在示例中尝试访问的数据不存在。访问(不存在的)键时,自动激活将创建一个undef 值。你是use strict;use warnings;吗?
  • @SinanÜnür - 凌乱,我同意,但我认为这比错误地更改对想要帮助的人可能很重要的部分数据要好。
  • @mugenkenichi - 感谢您花时间查看。使用严格;只是告诉我,一旦我到达第二个项目数组,我正在使用一个未初始化的值,这就是我在这里发布的原因。我显然没有正确阅读回复。也许您可以提供解决方案或指出我可能误读的地方。对我来说,看起来我正在以与返回结构相同的方式向下钻取结构。
  • 解决方案是发布实际的 XML,而不是坚持我们在这个数据结构中做出正面或反面。

标签: xml arrays perl hash xml-parsing


【解决方案1】:

这基本上就是你所拥有的。

$VAR1 = {
    'soapenv:Body' => {
        'ns1:SelectCmDeviceResponse' => {
            'SelectCmDeviceResult' => {
                'CmNodes' => {
                    'item' => [
                        {
                            'xsi:type'  => 'ns1:CmNode',
                            'CmDevices' => {
                                'soapenc:arrayType' => 'ns1:CmDevice[0]',
                                'xsi:type'          => 'soapenc:Array'
                            },
                        },
                    ],
                    # plus some more items with DIFFERENT structure
                },
            },
        },
    },
};

您正在尝试访问

$data->{'soapenv:Body'}
    ->{'ns1:SelectCmDeviceResponse'}
        ->{'SelectCmDeviceResult'}
            ->{'CmNodes'}
                ->{'item'}->[0]                # you probably want ->[1] here! (wild guess)
                    ->{'CmDevices'}
                        ->{'item'}->[0]        # this data does not exist
                            ->{'IpAddress'}    # and this does not exist
                                ->{'content'}; # and this

不存在的值由 perl 在首次访问时创建(这称为自动激活)并初始化 undef

这就是你发出警告的原因。

【讨论】:

  • 我猜这个问题被否决了,因为您似乎懒得阅读问题下方的 cmets 并思考超过 10 秒。
  • @mugenenkenichi - 不,你错了。它几乎立即被否决了,很可能是因为我发布的巨大的 Dumper 响应很难阅读,但我确实解释说我认为包含更多细节比不包含它们更好(我也遵守发布您请求的 XML)。关于懒惰,超级优雅。那你就很了解我了。我非常感谢最新的回复,其中包含一些关于我可能出错的细节。一旦我真正掌握了这一点,如果我能够做出贡献,我一定会对其他懒惰的人更容易一些。
  • 在提问时,通常希望发布显示您的问题的工作代码示例(适用于任何人)。如有必要,完成(!)输入和预期输出。这使其他懒惰的人更容易回答您的问题。无论如何..我不是故意粗鲁的,但我在遍历其他人的哈希时有点咄咄逼人;)
  • 以后我会尽量让我的请求更清晰。这很困难,因为我有大量数据,但有一个非常具体的问题。该代码确实有效,除了为 IpAddress 分配哈希值。我有十几个或更多的脚本可以正确地做大致相同的事情,但这个哈希是一个艰难的。再次感谢以上详细信息。我还没有机会回到这个,但如果它有效,我会告诉你。
  • @mugenenkenichi - 所以肯定是 [1] 而不是 [0] 让我绊倒了,感谢您指出这一点。我认为元素总是从 [0] 开始。你有什么推荐的读物来更好地理解配对 xml 哈希结构吗?
【解决方案2】:

你可以试试SOAP::Deserializer。我无法尝试,因为我无权访问您正在解析的 XML 文档。它返回一个SOAP::SOM 对象。

valueof(node)

    $res = $som->valueof('[1]');

SOAP::SOM 对象在内部使用 match 方法匹配路径时,此方法允许检索任何匹配节点内的数据。数据作为本地 Perl 数据返回,而不是类实例(参见 dataof)。在标量上下文中,此方法仅返回匹配节点集中的第一个元素。在 array 列表上下文中,返回所有元素。假设之前的调用发生在之前的 match 调用之后,它会从 $som 中包含的方法响应中检索结果实体,因为这是方法响应标记中的第一个子元素。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-11
    • 2021-08-12
    相关资源
    最近更新 更多