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

SOAP::Lite hash references

Expand Messages
  • andrew_tfcc
    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
    Message 1 of 3 , May 25 3:27 PM
    • 0 Attachment
      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
      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 2 of 3 , May 25 10:44 PM
      • 0 Attachment
        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 3 of 3 , May 27 9:26 AM
        • 0 Attachment
          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.