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

RE: [soaplite] SOAP::Lite hash references

Expand Messages
  • Maurice McCabe
    I have used SOAP::Lite with Java (Axis SOAP toolkit). I have been able to use it with Coldfusion MX which uses the Axis SOAP toolkit and it works seamlessly.
    Message 1 of 3 , May 25, 2004
      Message
      I have used SOAP::Lite with Java (Axis SOAP toolkit). I have been able to use it with Coldfusion MX which uses the Axis SOAP toolkit and it works seamlessly. The same SOAP::Lite server will work with .NET and Java. I am using a WSDL to connect and I am able to send complex datatypes in either direction between the SOAP::Lite server and a Java or .NET client.
       
      In my SOAP::Lite server implementation I have standardized on 'document/literal' instead of 'rpc/encoded'. I am using Alberto's serializer (http://ads.harvard.edu/~alberto/SOAP/)
      and building any required complex types explicitly on the server side. So for example to build what you are describing I would use something like:
       
      SOAP::Data->name('getResponse' => \convertHashOfHashesToSOAP('myHashName', %myHash);
       
      where 'convertHashOfHashesToSOAP' might be implemented as something like:
       
      sub convertHashOfHashesToSOAP {
          my ($myHashName, %myHash) = @_;
          my @mySOAP;
          for my $myHashKey (keys %myHash) {
              if isRef($myHashItem)
                  push @mySOAP, SOAP::Data->name($myHashName => \SOAP::Data->value(SOAP::Data->name($myHashKey => \SOAP::value(convertHashOfHashesToSOAP($myHashKey, %{myHashItem})));
              else   
                  push @mySOAP, SOAP::Data->name($myHashName => \SOAP::Data->value(SOAP::Data->name($myHashKey => $myHash($myHashKey));
          }
          return @mySOAP;
      }
       
      sub isRef {
          # not implemented (getting late)
      }
      The WSDL has to be built for this. Then use wsdl2java to build the java stubs (Coldfusion MX does this for you when you first invoke the web service).
       
      Maurice
      -----Original Message-----
      From: andrew_tfcc [mailto:andrew_tfcc@...]
      Sent: Tuesday, May 25, 2004 3:28 PM
      To: soaplite@yahoogroups.com
      Subject: [soaplite] SOAP::Lite hash references

      SOAP::Lite hash references

      I am trying to get a SOAP client written in Java to communicate with a
      SOAP::Lite server. Everything seems to work fine until the SOAP::Lite
      server returns a hash reference.  The problem is that when SOAP::Lite
      attempts to serialize the hash reference, it doesn't include a
      "xsi:type" in the xml element that is sent back.

      I know that it is possible to specify a type by using a SOAP::Data
      object, but I can't use a SOAP::Data object on the server end because
      the method MUST return a hash reference (so that it can be used by
      other local perl methods without using ->value() on the data first).

      I have tried writing my own SOAP::Serializer::as_myHash function and
      adding myHash  type to the typelookup hash, but this is useless
      because the as_myHash function never gets called on objects that are
      references (see encode_scalar in Lite.pm).

      Here is an example of what is happening:
      SOAP request:
      <SOAP-ENV:Body>
      <namesp2:get xmlns:namesp2="MapExtent">
          <c-gensym3 xsi:type="xsd:string">
              cc304177396d8c8205c1e9
          </c-gensym3>
          <c-gensym6 xsi:type="namesp1:SOAPStruct">
              <ext_id xsi:type="xsd:int">0</ext_id>
          </c-gensym6>
      </namesp2:get>
      </SOAP-ENV:Body>

      The perl script on the server then executes and returns a hash
      reference that looks like this:

      $hashRef = { cli_id => "1",
                             ext_id => "0",
                             minx => "-77.092030913858",
                             miny => "38.834563673865",
                             maxx => "-76.925233545092",
                             maxy => "38.947347800733",
                             _attr => [$anotherHashRef]
                       };

      The SOAP  Response is something like this

      <SOAP-ENV:Body>
          <namesp261:getResponse xmlns:namesp261="MapExtent">
              <s-gensym804>
                  <cli_id xsi:type="xsd:int">1</cli_id>
                  <ext_id xsi:type="xsd:int">0</ext_id>
                  <miny xsi:type="xsd:float">38.834563673865</miny>
                  <minx xsi:type="xsd:float">-77.092030913858</minx>
                  <maxx xsi:type="xsd:float">-76.925233545092</maxx>
                  <maxy xsi:type="xsd:float">38.947347800733</maxy>
                  <_attr xsi:type="SOAP-ENC:Array"
      SOAP-ENC:arrayType="xsd:ur-type[1]">
                      <item>
                          <cli_id xsi:type="xsd:int">1</cli_id>
                          <ext_id xsi:type="xsd:int">0</ext_id>
                          <miny xsi:type="xsd:float">-22.564583633865</miny>
                          <minx xsi:type="xsd:float">-71.592637983839</minx>
                          <maxx xsi:type="xsd:float">66.442253545031</maxx>
                          <maxy xsi:type="xsd:float">17.823384780732</maxy>
                      </item>
                  </_attr>
              </s-gensym804>
          </namesp261:getResponse>
      </SOAP-ENV:Body>

      The problem is that in order for java to be able unmarshal the data,
      both the <gensym804> and the <item> tags need to have a xsi:type.

      Does anyone have any idea how this can be done? Has anyone used
      SOAP::Lite with java before?

      ps: I am very tempted to put the following code in the encode_scalar
      function in Lite.pm. Is there a good reason why I shouldn't do this?: 

          # hash reference
            return [$name, {'xsi:type' => $self->maptypetouri('SOAPStruct'),
      %$attr}, [$self->encode_object($$value)], $self->gen_id($value)] if
      ref $value && UNIVERSAL::isa($$value => 'HASH');

          # object reference
          ....

      Thanks,
      Andrew


    • Maurice McCabe
      One suggestion is to patch the outgoing envelope to insert the xsi:type. This could be done by inheriting from the envelope method and inserting at the
      Message 2 of 3 , May 27, 2004
        One suggestion is to patch the outgoing envelope to insert the xsi:type.
        This could be done by inheriting from the 'envelope' method and
        inserting at the appropriate place. See Alberto's serializer for an
        example of inheriting from 'envelope'. There may be other ways to patch
        the outgoing envelope.

        Maurice

        -----Original Message-----
        From: andrew_tfcc [mailto:andrew_tfcc@...]
        Sent: Thursday, May 27, 2004 8:12 AM
        To: Maurice McCabe
        Subject: Re: SOAP::Lite hash references


        Thanks for the response. The only problem is that in this situation,
        complex types cannot be built on the server side using the SOAP::Data
        constructor. Instead the function on the server only returns (and must
        return) a hash reference. Somehow I need SOAP::Lite to be able to give
        this a xsi:type when it serializes it. Is this possible?

        Thanks,
        Andrew


        --- In soaplite@yahoogroups.com, "Maurice McCabe" <mmccabe@o...> wrote:
        > I have used SOAP::Lite with Java (Axis SOAP toolkit). I have been able

        > to use it with Coldfusion MX which uses the Axis SOAP toolkit and it
        > works seamlessly. The same SOAP::Lite server will work with .NET and
        > Java. I am using a WSDL to connect and I am able to send complex
        > datatypes in either direction between the SOAP::Lite server and a Java

        > or .NET client.
        >
        > In my SOAP::Lite server implementation I have standardized on
        > 'document/literal' instead of 'rpc/encoded'. I am using Alberto's
        > serializer ( <http://ads.harvard.edu/~alberto/SOAP/>
        > http://ads.harvard.edu/~alberto/SOAP/)
        > and building any required complex types explicitly on the server side.

        > So for example to build what you are describing I would use something
        > like:
        >
        > SOAP::Data->name('getResponse' =>
        > \convertHashOfHashesToSOAP('myHashName', %myHash);
        >
        > where 'convertHashOfHashesToSOAP' might be implemented as something
        > like:
        >
        >
        > sub convertHashOfHashesToSOAP {
        > my ($myHashName, %myHash) = @_;
        > my @mySOAP;
        >
        > for my $myHashKey (keys %myHash) {
        > if isRef($myHashItem)
        >
        > push @mySOAP, SOAP::Data->name($myHashName =>
        > \SOAP::Data->value(SOAP::Data->name($myHashKey =>
        > \SOAP::value(convertHashOfHashesToSOAP($myHashKey, %{myHashItem})));
        > else
        > push @mySOAP, SOAP::Data->name($myHashName =>
        > \SOAP::Data->value(SOAP::Data->name($myHashKey =>
        $myHash($myHashKey));
        > }
        > return @mySOAP;
        > }
        >
        > sub isRef {
        > # not implemented (getting late)
        > }
        >
        > The WSDL has to be built for this. Then use wsdl2java to build the
        > java stubs (Coldfusion MX does this for you when you first invoke the
        > web service).
        >
        > Maurice
        >
      Your message has been successfully submitted and would be delivered to recipients shortly.