SOAP::WSDL :(

Ovid on 2004-10-13T18:33:51

Our customer appears to have set things up correctly at their end. Regrettably, SOAP::Lite is a bit, er, confusing. It's retrieving everything correctly, but it's being coy and refusing to actually let me have the data. I think I'll buy it flowers.


returning multiple items?

lachoy on 2004-10-13T19:27:23

If the SOAP method returns multiple items (e.g., returns a Vector) see a recent blog entry I wrote after running into this. It is possible to make SOAP::Lite behave, just takes a little work...

Re:returning multiple items?

Ovid on 2004-10-13T19:58:22

I note that you state it only returns the last item when fetching multiple items. I am fetching multiple items, but I get nothing.

use the SOM

ChrisDolan on 2004-10-15T15:30:12

I agree that SOAP::Lite is quite opaque.  In my work, I usually skip the "handy" wrappers that SOAP::Lite offers and dig into the XML.  Here's a snippet like what I use in my unreleased SOAPClient package

sub call
{
  my ($uri, $proxy, $timeout, $method, $args, $want) = @_;
  my @args = map {SOAP::Data->name($_, $args->{$_})} keys %$args;
  my $som = SOAP::Lite
     ->uri($uri)
     ->proxy($proxy, timeout => $timeout)
     ->call($method, @args);
  for (@$want) {
    my $wantArray = (s/^\@//);
    my @vals;
    if ($som->match("/Envelope/Body/[1]/$_")) {
       @vals = $som->valueof();
    push @rets, $wantArray ? [@vals], $vals[0];
  }
  return @rets;
}

Then use this like as follows (fictional service):

my ($temp, $humid) = call($uri, $proxy, 30, "getWeather", {zip => 53711}, ["temp", "humidity]);

This works best if the server looks like this:

our @ISA = qw(SOAP::Server::Parameters);
sub getWeather
{
  my %data;
  if ($_[-1] && UNIVERSAL::isa($_[-1] => 'SOAP::SOM') {
    %data = %{$_[-1]->method() || {}};
  } else {
    push @_, undef if (@_ %2 != 0);  # need even number of elements
    %data = @_;
  }
  if (!$data{zip}) {
     die SOAP::Fault->faultcode("MissingData")->faultstring("Zip not provided");
  }
  my ($temp, $windspeed, $humidity) = weather($data{zip});
  return SOAP::Data->name(temperature => $temp),
         SOAP::Data->name(windspeed => $wind),
         SOAP::Data->name(humidity => $humidity);
}

That server can be specified by the following WSDL (which is
incomplete since it lacks a service tag):

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<definitions targetNamespace="urn:Weather" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="urn:Weather" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <documentation>
    Partial WSDL auto-generated from soap.pod by /perl/bin/pod2wsdl
  </documentation>
  <message name="getWeatherRequest">
    <part name="zip" type="xsd:integer"/>
  </message>
  <message name="getWeatherResponse">
    <part name="temperature" type="xsd:float"/>
    <part name="windspeed" type="xsd:float"/>
    <part name="humidity" type="xsd:float"/>
  </message>
  <portType name="WeatherInterface">
    <operation name="getWeather">
      <input message="tns:getWeatherRequest"/>
      <output message="tns:getWeatherResponse"/>
    </operation>
  </portType>
  <binding name="WeatherBinding" type="tns:WeatherInterface">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="getWeather">
      <soap:operation soapAction="http://weather.foo.com/Weather#getWeather"/>
      <input>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://weather.foo.com/Weather" use="encoded"/>
      </input>
      <output>
        <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://weather.foo.com/Weather" use="encoded"/>
      </output>
    </operation>
  </binding>
</definitions>