XML::Simple & accessing complex structures

I'm having an issue accessing some nested data in an XML response using Perl/XML::Simple. An extract of the printed XML reply looks like this:

<?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>

Here is the code, which should parse the response and return the IpAddress values of the returned devices:

#!/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();

Answers


This is basically what you have.

$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
                },
            },
        },
    },
};

You are trying to access with

$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

The values that do not exist are created by perl on first access (this is called autovivification) and initialized undef.

That is the reason for your warning.


You can try SOAP::Deserializer. I cannot try it because I don't have access to the XML document you are parsing. It returns a SOAP::SOM object.

valueof(node)

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

When the SOAP::SOM object has matched a path internally with the match method, this method allows retrieval of the data within any of the matched nodes. The data comes back as native Perl data, not a class instance (see dataof). In a scalar context, this method returns just the first element from a matched node set. In an array list context, all elements are returned. Assuming that the earlier call happens after the earlier call to match, it retrieves the result entity from the method response that is contained in $som, as this is the first child element in a method-response tag.


Need Your Help

How can I bind an (outbound, of course) HttpUrlConnection to a specific local ip address, when I have multiple NIC on my client machine?

java http networking

I have a kind of Http-Gateway app, which acts as an http client towards servers outside our local network.

Huffman decoding in c using tree

c tree decoding prefix huffman-code

I've got to decompress a string that was encoded with a Huffman tree, but the code has variable length and not all inputs are in prefix, in which case I should print "invalid" and finish execution....