Loading ...
Sorry, an error occurred while loading the content.

Re: [soaplite] Soaplite server, Axis client, no deserializer for SoapStruct

Expand Messages
  • Byrne Reese
    Such a common problem. I really must post something about this on the SOAP::Lite web site. In fact I will, but for now: SOAP::Lite wants to (be default) use
    Message 1 of 6 , Dec 12, 2003
    • 0 Attachment
      Such a common problem. I really must post something about this on the
      SOAP::Lite web site. In fact I will, but for now:

      SOAP::Lite wants to (be default) use SOAP Encoding to encode perl data
      structures - and this works remarkably poorly. It is always a good idea to
      encode complex types by hand.

      It is a pain - I think everyone on this group will agree with me there.
      The good news is that wsdl2perl will be making its way to market
      relatively soon (January or February timeframe) - which will make creating
      these structures substantially easier.

      For the time being it is a relatively manual process. Here are some
      articles that are routinely referenced that will help you compose complex
      types using SOAP::Lite:

      http://www.majordojo.com/archives/000008.html
      http://builder.com.com/5100-6389-1045078.html

      >
      >
      >
      >
      > Hi, all.
      >
      > I am having a problem, and I have not been able to solve it. I have read
      > the docs
      > extensively, but I am a perl newbie, and thus probably missed some
      > important stuff.
      >
      > I have a server running SOAP::Lite, and it is working well. I can call my
      > methods from
      > a SOAP::Lite client using either the direct call, or via a hand crafted
      > WSDL file, and they
      > work great! In general, they all take a hash containing a config name,
      > and perhaps
      > other stuff. They return hashrefs, integers, or strings. The one I am
      > trying to get to
      > work first takes just configname =< $configname and returns a hashref of
      > names
      > keyed by values.
      >
      > When I try to use Axis either directly or with my hand generated wsdl
      > file, it dies,
      > complaining that it cannot find an unserializer for a SOAPStruct. (I
      > would have been
      > willing to accept a map, myself.
      >
      > I would rather do my calls using the WSDL file, so here is the one I hand
      > crafted. The
      > method we want to call is get_user_ids_by_name:
      >
      >>?xml version="1.0" encoding="utf-8"?definitions
      >> name="GBIB"
      > targetNamespace="http://feature.alodar.com/Alodar/GBIB/"
      > xmlns:schema="http://www.w3.org/2001/XMLSchema"
      > xmlns:tns="http://feature.alodar.com/Alodar/GBIB/"
      > xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
      > xmlns="http://schemas.xmlsoap.org/wsdl/"
      > types!-- I could use the following by defining the type returned from a
      > part of a message as
      > >message name="ListOfUsers"part
      > name="models"
      > type="tns:Vector"/messagexsd:schema
      > targetNamespace="http://feature.alodar.com/Alodar/GBIB/"
      > xmlns="http://www.w3.org/2001/XMLSchema/"schema:complexType
      > name="Vector"xsd:element name="elementData"
      > type="schema:String" /<
      > >xsd:element name="elementCount"
      > type="schema:int" /<
      > >/schema:complexType/xsd:schema
      >
      >
      >
      >
      > To unsubscribe from this group, send an email to:
      > soaplite-unsubscribe@yahoogroups.com
      >
      >
      >
      >
      >
      > Your use of Yahoo! Groups is subject to the Yahoo! Terms of Service.
      >
      >
      >
      >
      >


      ^byrne :/
    • Scott Ellsworth
      Byrne, Thanks much for the reply. I have two followup questions, that I hope are easy to answer. ... Ok - I have read the articles again, and they make more
      Message 2 of 6 , Dec 12, 2003
      • 0 Attachment
        Byrne,

        Thanks much for the reply. I have two followup questions, that I hope
        are easy to answer.

        On Dec 12, 2003, at 8:53 AM, Byrne Reese wrote:

        > SOAP::Lite wants to (be default) use SOAP Encoding to encode perl data
        > structures - and this works remarkably poorly. It is always a good
        > idea to
        > encode complex types by hand.

        Ok - I have read the articles again, and they make more sense.

        Unfortunately, I have limited ability to change what is on the server
        side. I can change the SOAP implementer module, but not the routines
        under it. Right now, the soap implementer just dispatches to the end
        user routines with very little intervention.

        Before asking further questions, here is some code that may make this
        make more sense.

        Alodar::GBIB::SOAPUtils.pm, called by the mod_perl module on urls
        ending in gbib-soap. (It actually dispatches 22 routines, but I have
        deleted all but the three in my example. If I get those to work, I can
        get the others to work.)

        package Alodar::GBIB::SOAPUtils;

        require Exporter;

        use strict;
        use SOAP::Transport::HTTP;

        our @ISA = qw(Exporter);

        our @EXPORT_OK = qw(
        handler
        );

        sub handler {
        SOAP::Transport::HTTP::Apache -> dispatch_to(
        qw(
        Alodar::GBIB::ListUtils::get_public_lists
        Alodar::GBIB::UserUtils::get_id_for_username
        Alodar::GBIB::UserUtils::get_user_ids_by_name
        Alodar::GBIB::UserUtils::get_username_for_id
        )
        ) ->handler(@_)
        # ) ->handler(%args)
        }

        1;
        # $Id: SOAPUtils.pm,v 1.6 2003/11/12 23:18:07 scott Exp $
        # Local Variables:
        # mode: perl
        # End:

        Here is the implementation of get_user_ids_by_name. It returns a hash
        whose keys are usernames, and whose values are integer ids. Assume I
        cannot modify this, as it is used by other functions.

        sub get_user_ids_by_name {
        if ($#_ >= 0) {
        shift if $_[0] eq __PACKAGE__;
        }
        my %args = (
        config_name => '',
        @_, # push key-value pairs into args
        );
        my $config_name = $args{config_name};

        $config_name or die "Alodar::GBIB::UserUtils::get_user_ids_by_name:
        you must supply a configuration name";

        my $configs = get_configs($config_name);

        my $dbh = getdbh($$configs{configname}, '', '');

        my $sth = $dbh->prepare('SELECT id, username FROM users');

        $sth->execute() || die
        "Alodar::GBIB::UserUtils::get_user_ids_by_name: Error: $dbh->errstr";

        my %user_ids_by_name = ();
        while (my @row = $sth->fetchrow_array) {
        my ($id, $username) = @row;
        $user_ids_by_name{$username} = $id;
        }
        $sth->finish();
        $dbh->disconnect();

        return \%user_ids_by_name;
        }

        Note that it returns the hash by reference.

        So, what I seek is either some WSDL that would let Axis handle
        SOAP::Lite sends, or a way to add types to the above SOAPUtils
        dispatcher, without having to change get_user_ids_by_name. Are either
        of these reasonable? Use small words - I am new at Perl and SOAP, but
        have read many perldocs, manpages, and W3C specs.

        Here is what I have as WSDL - it is incomplete, but does work for
        anything not returning a Map.

        <?xml version="1.0" encoding="utf-8"?>
        <definitions name="GBIB"
        targetNamespace="http://feature.alodar.com/Alodar/GBIB/"
        xmlns:schema="http://www.w3.org/2001/XMLSchema"
        xmlns:tns="http://feature.alodar.com/Alodar/GBIB/"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        xmlns:xml-soap="http://xml.apache.org/xml-soap"
        xmlns="http://schemas.xmlsoap.org/wsdl/"
        >

        <types>
        <schema
        xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://feature.alodar.com/Alodar/GBIB/"
        >
        <complexType name="idNameMap">
        <element name="elementData" type="string" />
        <element name="elementCount" type="int" />
        </complexType>
        <complexType name="SOAPStruct">
        <all>
        <element name="varString" type="string"/>
        <element name="varInt" type="int"/>
        <element name="varFloat" type="float"/>
        </all>
        </complexType>
        </schema>
        </types>

        <message name="config_nameRequest">
        <part name="arg1Name" type="schema:string" />
        <part name="arg1Value" type="schema:string" />
        </message>
        <message name="stringStringRequest">
        <part name="arg1Name" type="schema:string" />
        <part name="arg1Value" type="schema:string" />
        <part name="arg2Name" type="schema:string" />
        <part name="arg2Value" type="schema:string" />
        </message>
        <message name="stringIntRequest">
        <part name="arg1Name" type="schema:string" />
        <part name="arg1Value" type="schema:string" />
        <part name="arg2Name" type="schema:string" />
        <part name="arg2Value" type="schema:int" />
        </message>
        <message name="idNameMapResponse">
        <!-- part name="idNameMapReturnValue" type="xml-soap:Map" / -->
        <part name="idNameMapReturnValue" type="tns:idNameMap" />
        </message>
        <message name="stringResponse">
        <part name="stringReturnValue" type="schema:string" />
        </message>
        <message name="intResponse">
        <part name="stringReturnValue" type="schema:int" />
        </message>
        <message name="get_public_listsResponse">
        <part name="get_public_listsReturnValue" type="schema:anyType" />
        </message>

        <porttype name="UserUtilsPort">
        <!-- ports are analogous to method calls. SR says analogous to
        library/module/class -->
        <!-- name must match some binding's type -->
        <operation name="get_user_ids_by_name">
        <input message="tns:config_nameRequest" />
        <output message="tns:idNameMapResponse" />
        </operation>
        <operation name="get_id_for_username">
        <input message="tns:stringStringRequest" />
        <output message="tns:intResponse" />
        </operation>
        <operation name="get_username_for_id">
        <input message="tns:stringIntRequest" />
        <output message="tns:stringResponse" />
        </operation>
        </porttype>

        <porttype name="ListUtilsPort">
        <!-- ports are analogous to method calls. -->
        <!-- name must match some binding's type -->
        <operation name="get_public_lists">
        <input message="tns:config_nameRequest" />
        <output message="tns:stringResponse" />
        </operation>
        </porttype>

        <binding name="UserUtils" type="tns:UserUtilsPort">
        <!-- binding elements tell code how to move data. We are using SOAP
        -->
        <!-- These are analagous to classes -->
        <!-- type matches name of some port -->
        <soap:binding style="rpc"
        transport="http://schemas.xmlsoap.org/soap/http" />
        <operation name="get_id_for_username">
        <soap:operation style="rpc" />
        <input>
        <soap:body use="encoded"
        namespace="http://feature.alodar.com/Alodar/GBIB/UserUtils"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
        </input>
        <output>
        <soap:body use="encoded"
        namespace="http://feature.alodar.com/Alodar/GBIB/UserUtils"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
        </output>
        </operation>
        <operation name="get_user_ids_by_name">
        <soap:operation style="rpc" />
        <input>
        <soap:body use="encoded"
        namespace="http://feature.alodar.com/Alodar/GBIB/UserUtils"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
        </input>
        <output>
        <soap:body use="encoded"
        namespace="http://feature.alodar.com/Alodar/GBIB/UserUtils"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
        </output>
        </operation>
        <operation name="get_username_for_id">
        <soap:operation style="rpc" />
        <input>
        <soap:body use="encoded"
        namespace="http://feature.alodar.com/Alodar/GBIB/UserUtils"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
        </input>
        <output>
        <soap:body use="encoded"
        namespace="http://feature.alodar.com/Alodar/GBIB/UserUtils"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
        </output>
        </operation>
        </binding>

        <binding name="ListUtils" type="tns:ListUtilsPort">
        <!-- binding elements tell code how to move data. We are using SOAP
        -->
        <!-- These are analagous to classes -->
        <!-- type matches name of some port -->
        <soap:binding style="rpc"
        transport="http://schemas.xmlsoap.org/soap/http" />
        <operation name="get_public_lists">
        <soap:operation style="rpc" />
        <input>
        <soap:body use="encoded"
        namespace="http://feature.alodar.com/Alodar/GBIB/ListUtils"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
        </input>
        <output>
        <soap:body use="encoded"
        namespace="http://feature.alodar.com/Alodar/GBIB/ListUtils"
        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
        </output>
        </operation>
        </binding>

        <service name="GBIBService">
        <!-- service tells us what bindings to use. These give actual
        machines that implement the protocol. -->
        <!-- document>
        For a complete description of this service, go to the following URL:
        <a href="http://feature.alodar.com/">
        http://feature.alodar.com/
        </a>
        </document -->
        <port name="UserUtils" binding="tns:UserUtils">
        <soap:address location="http://feature.alodar.com/gbib-soap" />
        </port>
        <port name="ListUtils" binding="tns:ListUtils">
        <soap:address location="http://feature.alodar.com/gbib-soap" />
        </port>
        </service>
        </definitions>

        I may well be missing something due to lack of experience - please let
        me know if this is so.

        > It is a pain - I think everyone on this group will agree with me there.
        > The good news is that wsdl2perl will be making its way to market
        > relatively soon (January or February timeframe) - which will make
        > creating
        > these structures substantially easier.

        I am not sure this will help - does it actually take a WSDL file, and
        produce a perl dispatch module that calls underlying routines?

        > For the time being it is a relatively manual process. Here are some
        > articles that are routinely referenced that will help you compose
        > complex
        > types using SOAP::Lite:

        Thank you. I have read them again, and I think they would apply if I
        were able to rewrite the underlying Perl code. I cannot, really, save
        the SOAPUtils module that gets called directly by apache's mod_perl.

        Scott
        >>
        >>
        >>
        >>
        >> Hi, all.
        >>
        >> I am having a problem, and I have not been able to solve it. I have
        >> read
        >> the docs
        >> extensively, but I am a perl newbie, and thus probably missed some
        >> important stuff.
        >>
        >> I have a server running SOAP::Lite, and it is working well. I can
        >> call my
        >> methods from
        >> a SOAP::Lite client using either the direct call, or via a hand
        >> crafted
        >> WSDL file, and they
        >> work great! In general, they all take a hash containing a config
        >> name,
        >> and perhaps
        >> other stuff. They return hashrefs, integers, or strings. The one I
        >> am
        >> trying to get to
        >> work first takes just configname =< $configname and returns a hashref
        >> of
        >> names
        >> keyed by values.
        >>
        >> When I try to use Axis either directly or with my hand generated wsdl
        >> file, it dies,
        >> complaining that it cannot find an unserializer for a SOAPStruct. (I
        >> would have been
        >> willing to accept a map, myself.
        >>
        >> I would rather do my calls using the WSDL file, so here is the one I
        >> hand
        >> crafted. The
        >> method we want to call is get_user_ids_by_name:
        >>
        >>> ?xml version="1.0" encoding="utf-8"?definitions
        >>> name="GBIB"
        >> targetNamespace="http://feature.alodar.com/Alodar/GBIB/"
        >> xmlns:schema="http://www.w3.org/2001/XMLSchema"
        >> xmlns:tns="http://feature.alodar.com/Alodar/GBIB/"
        >> xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        >> xmlns="http://schemas.xmlsoap.org/wsdl/"
        >> types!-- I could use the following by defining the type returned
        >> from a
        >> part of a message as
        >>> message name="ListOfUsers"part
        >> name="models"
        >> type="tns:Vector"/messagexsd:schema
        >> targetNamespace="http://feature.alodar.com/Alodar/GBIB/"
        >> xmlns="http://www.w3.org/2001/XMLSchema/"schema:complexType
        >> name="Vector"xsd:element name="elementData"
        >> type="schema:String" /<
        >>> xsd:element name="elementCount"
        >> type="schema:int" /<
        >>> /schema:complexType/xsd:schema
        >>
        >>
        >>
        >>
        >> To unsubscribe from this group, send an email to:
        >> soaplite-unsubscribe@yahoogroups.com
        >>
        >>
        >>
        >>
        >>
        >> Your use of Yahoo! Groups is subject to the Yahoo! Terms of Service.
        >>
        >>
        >>
        >>
        >>
        >
        >
        > ^byrne :/
        >
        > ------------------------ Yahoo! Groups Sponsor
        > ---------------------~-->
        > Buy Ink Cartridges or Refill Kits for your HP, Epson, Canon or Lexmark
        > Printer at MyInks.com. Free s/h on orders $50 or more to the US &
        > Canada.
        > http://www.c1tracking.com/l.asp?cid=5511
        > http://us.click.yahoo.com/mOAaAA/3exGAA/qnsNAA/W6uqlB/TM
        > ---------------------------------------------------------------------
        > ~->
        >
        > To unsubscribe from this group, send an email to:
        > soaplite-unsubscribe@yahoogroups.com
        >
        >
        >
        > Your use of Yahoo! Groups is subject to
        > http://docs.yahoo.com/info/terms/
        >
        >
      • Jim Hebert
        ... I ve read the links posted, and now I have my own twist on the question: Seems like many people have the problem where, they make a relatively simple
        Message 3 of 6 , Dec 16, 2003
        • 0 Attachment
          On Fri, 12 Dec 2003, Byrne Reese wrote:

          > Such a common problem. I really must post something about this on the
          > SOAP::Lite web site. In fact I will, but for now:

          I've read the links posted, and now I have my own twist on the question:

          Seems like many people have the problem where, they make a relatively
          simple request from an Apache SOAP for Java client, the SOAP::Lite-based
          service returns a SOAPStruct, and now they want to deserialize it.

          I have an opposite problem: I want an Apache SOAP for Java client to pass
          a SOAPStruct IN to the SOAP::Lite-based service. The service returns an
          integer, so deserializing from SOAPStruct isn't of interest, it's
          serializing TO it in Java that I need.

          You might suggest, as an alternative, change my service's API to make it
          accept something less crazy, but in this case that defeats the purpose.
          I've got an existing perl-based web service and I'm trying to write a
          unit tester in Java.

          I realize this is as much a Apache SOAP question as it is a SOAP::Lite
          question, but since SOAPStruct seems to be a SOAP::Lite invention I assume
          my chances of someone knowing the answer here are greather than someone
          knowing the answer on the java soap lists.

          jim

          (I suppose, as an OT alternative, if someone wanted to show me something
          as powerful as JWebUnit, for perl, I'd stop trying to write this unit
          tester in Java, since I prefer Perl anyhow. :-)
        • jpeyser
          Scott, Your Perl Server should be returning something similar to this. return SOAP::Data- value( SOAP::Data- name( admin = 1), SOAP::Data- name( guest =
          Message 4 of 6 , Dec 19, 2003
          • 0 Attachment
            Scott,

            Your Perl Server should be returning something similar to this.

            return SOAP::Data->value(
            SOAP::Data->name('admin' => 1),
            SOAP::Data->name('guest' => 2),
            );


            See http://majordojo.com/soaplite/

            Jonathan

            --- In soaplite@yahoogroups.com, "scottalodar" <scott@a...> wrote:
            > Hi, all.
            >
            > I am having a problem, and I have not been able to solve it. I
            have read the docs
            > extensively, but I am a perl newbie, and thus probably missed some
            important stuff.
            >
          • Scott Ellsworth
            Hi, Jonathan. Thank you for the help. I got a chance to try this again. Your hint, and a bit of mucking about, made things work the way I wanted them to. The
            Message 5 of 6 , Jan 9, 2004
            • 0 Attachment
              Hi, Jonathan.

              Thank you for the help. I got a chance to try this again. Your hint,
              and a bit of mucking about, made things work the way I wanted them to.

              The code:
              my %users = %{get_user_ids_by_name(config_name => $config_name)};

              my @entries = ();
              foreach my $username (sort keys %users){
              my $entry =
              SOAP::Data->name('mapItem' =>
              \SOAP::Data->value(
              SOAP::Data->name('key' => $username),
              SOAP::Data->name('value' => $users{$username}),
              )
              );
              $entries[$#entries+1]=$entry;
              }

              return SOAP::Data->value(
              SOAP::Data->name('map' =>
              \SOAP::Data->value(
              @entries
              ),
              )->type('gbibns:map')->attr( { 'xmlns:gbibns' =>
              'http://feature.alodar.com/Alodar/GBIB/' } ),
              );

              successfully returns the XML:

              <?xml version="1.0" encoding="UTF-8"?>
              <SOAP-ENV:Envelope
              xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
              xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
              xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
              xmlns:xsd="http://www.w3.org/1999/XMLSchema"
              SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
              <SOAP-ENV:Body>
              <namesp1:typed_get_user_ids_by_nameResponse
              xmlns:namesp1="http://feature.alodar.com/Alodar/GBIB/SOAPUtils">
              <map xsi:type="gbibns:map"
              xmlns:gbibns="http://feature.alodar.com/Alodar/GBIB/">
              <mapItem>
              <key xsi:type="xsd:string">
              admin
              </key>
              <value xsi:type="xsd:int">
              1
              </value>
              </mapItem>
              <mapItem>
              <key xsi:type="xsd:string">
              guest
              </key>
              <value xsi:type="xsd:int">
              2
              </value>
              </mapItem>
              </map>
              </namesp1:typed_get_user_ids_by_nameResponse>
              </SOAP-ENV:Body>
              </SOAP-ENV:Envelope>

              required by the wsdl

              <types>
              <schema
              xmlns="http://www.w3.org/2001/XMLSchema"
              targetNamespace="http://feature.alodar.com/Alodar/GBIB/"
              >
              <!-- import namespace="http://schemas.xmlsoap.org/soap/encoding/" /
              -->

              <complexType name="mapItem">
              <sequence>
              <element name="key" nillable="true" type="anyType" />
              <element name="value" nillable="true" type="anyType" />
              </sequence>
              </complexType>

              <complexType name="map">
              <sequence>
              <element maxOccurs="unbounded" minOccurs="0" name="mapItem"
              type="tns:mapItem" />
              </sequence>
              </complexType>
              </schema>
              </types>

              I still have a number of issues to figure out, such as how to get a
              perl example that can use this wsdl and the like, but this solves the
              main problem I was running into.

              Scott
            Your message has been successfully submitted and would be delivered to recipients shortly.