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

GWT Date Picker Format problem when saving a java date through hibernate in postgresql

java database hibernate gwt date

I am using Java Date and Hibernate which is then being saved in the database (Postgresql). I am not that good in hibernate

Need an ideal way to handle tab bar in app

iphone ios objective-c uitabbarcontroller uitabbar

I want to create an app where there is a login screen, and when user logs in open up home page with tab bar controller.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.