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

Re: [soaplite] force an array on deserialization

Expand Messages
  • Duncan Cameron
    ... Does this code do what you want to achieve? use strict; use SOAP::Lite; my $xml =
    Message 1 of 3 , Jun 25, 2004
    • 0 Attachment
      At 2004-06-25, 08:38:13 you wrote:

      >Hi all,
      >
      >I'm working on a tool where a Perl SOAP::Lite client has to
      >communicate with a Lisp-based SOAP server. The Lisp service is
      >sending back to me a sequence of structs. SOAP::Lite can handle SOAP
      >*arrays* from Lisp with no problems, but the Lisp API doesn't like
      >sending arrays of structs, and so sends *sequences* of structs
      >instead. Sequences of structs in Lisp are essentially lists of
      >lists, but when Perl receives them, it interprets them as a bunch of
      >identically named hashes. This causes problems.
      >
      >The actual message structure looks like this:
      >
      > <Response>
      > <QueryBundle> -struct
      > <QueryInfo> -struct
      > <inputVal> - one string
      > <inputKind> - one string
      > </QueryInfo>
      > <QueryData> - array of 1+ structs (problem is here!)
      > <datastruct> - struct
      > <resultVal> - one string
      > <resultType> - one string
      > </datastruct>
      > </QueryData>
      > </QueryBundle>
      > </Response>
      >
      >Lisp is sending back a sequence of `datastruct' elements in the
      >`QueryData' piece. SOAP::Lite deserializes this sequence as a hash,
      >and so all we have left at the end is the final `datastruct' element
      >processed. If SOAP::Lite were to deserialize the `QueryData' piece
      >as an array full of anonymous hashes, then I think the problem would
      >go away. But they're not anonymous. Rather, they're named, all with
      >the same name. The name has the status of default XML tags
      >like '<item>' in cases like this:
      >
      > <names>
      > <item>foo</item>
      > <item>bar</item>
      > </names>
      >
      >where the tag is there more to delimit values than to provide a
      >meaningful label.
      >
      >On a trace, I can see that all the `datastruct' instances are sitting
      >in the soap message body, just waiting to be plucked out. But I
      >don't know how I can make calls to access them. I tried making a
      >Perl QueryData class, and using that as a type identifier for the
      >message data, but I don't know how to specify the one-or-more-
      >elements array nature of the the QueryData piece. I also tried a
      >few calls to $som->values, $som->dataof, and the like, but didn't
      >have any success with them either.

      Does this code do what you want to achieve?

      use strict;
      use SOAP::Lite;

      my $xml = <<'END';
      <Response>
      <QueryBundle>
      <QueryInfo>
      <inputVal/>
      <inputKind/>
      </QueryInfo>
      <QueryData>
      <datastruct>
      <resultVal>result val 1</resultVal>
      <resultType>result type 1</resultType>
      </datastruct>
      <datastruct>
      <resultVal>result val 2</resultVal>
      <resultType>result type 2</resultType>
      </datastruct>
      <datastruct>
      <resultVal>result val 3</resultVal>
      <resultType>result type 3</resultType>
      </datastruct>
      </QueryData>
      </QueryBundle>
      </Response>
      END

      my $s = SOAP::Deserializer->new;

      my $som = $s->deserialize($xml); # som is normally returned by your SOAP call

      for my $i ($som->valueof('//datastruct')) {
      print map({qq|$_ => "$i->{$_}" |} keys %{$i}), "\n";
      }


      Regards
      Duncan
    • Andrew Dolbey
      Your suggestion works very well for me. Thanks, Duncan! There s a little busy work I ll have to do to put back in a single hash which datastruct elements go
      Message 2 of 3 , Jun 25, 2004
      • 0 Attachment
        Your suggestion works very well for me.  Thanks, Duncan!  There's a little busy work I'll have to do to put back in a single hash which datastruct elements go with which QueryBundle elements (an unpredictable many-to-one mapping), but this is probably easy enough to do.

        I'm replying here to all, as what I learned might be helpful for others.  Where I went wrong was to call $som->valueof( ) on an element that was too high up in the structure, at a top struct level.  This is where a hash was being created, where keys must be unique, of course, and so will clobber existing values with the same key as the message body is being processed.  So, this will come up short:

           my $data = $som->valueof('//QueryData')

        as it'll be a hash with only one key, and only one of the `datastruct' pieces as its value;  a version like what you showed me, on the other hand:

           my @dataStructs = $som->valueof('//datastruct')

        grabs all of the `datastruct' pieces for me.

        Thanks again for your help with this.

        Andy


        Duncan Cameron wrote:
        At 2004-06-25, 08:38:13 you wrote:
        
          
        Hi all,
        
        I'm working on a tool where a Perl SOAP::Lite client has to 
        communicate with a Lisp-based SOAP server.  The Lisp service is 
        sending back to me a sequence of structs.  SOAP::Lite can handle SOAP 
        *arrays* from Lisp with no problems, but the Lisp API doesn't like 
        sending arrays of structs, and so sends *sequences* of structs 
        instead.  Sequences of structs in Lisp are essentially lists of 
        lists, but when Perl receives them, it interprets them as a bunch of 
        identically named hashes.  This causes problems.  
        
        The actual message structure looks like this:
        
         <Response>
          <QueryBundle> -struct
            <QueryInfo>   -struct
               <inputVal>  - one string
               <inputKind> - one string
            </QueryInfo>
            <QueryData> - array of 1+ structs (problem is here!)
              <datastruct> - struct
                 <resultVal> - one string
                 <resultType> - one string
              </datastruct>
            </QueryData>
          </QueryBundle>
         </Response>
        
        Lisp is sending back a sequence of `datastruct' elements in the 
        `QueryData' piece.  SOAP::Lite deserializes this sequence as a hash, 
        and so all we have left at the end is the final `datastruct' element 
        processed.  If SOAP::Lite were to deserialize the `QueryData' piece 
        as an array full of anonymous hashes, then I think the problem would 
        go away.  But they're not anonymous.  Rather, they're named, all with 
        the same name.  The name has the status of default XML tags 
        like '<item>' in cases like this:
        
          <names>
            <item>foo</item>
            <item>bar</item>
          </names>
        
        where the tag is there more to delimit values than to provide a 
        meaningful label.
        
        On a trace, I can see that all the `datastruct' instances are sitting 
        in the soap message body, just waiting to be plucked out.  But I 
        don't know how I can make calls to access them.  I tried making a 
        Perl QueryData class, and using that as a type identifier for the 
        message data, but I don't know how to specify the one-or-more-
        elements array nature of the the QueryData piece.  I also tried a 
        few calls to $som->values, $som->dataof, and the like, but didn't 
        have any success with them either.
            
        Does this code do what you want to achieve?
        
        use strict;
        use SOAP::Lite;
        
        my $xml = <<'END';
        <Response>
         <QueryBundle>
           <QueryInfo>
              <inputVal/>
              <inputKind/>
           </QueryInfo>
           <QueryData> 
             <datastruct>
                <resultVal>result val 1</resultVal>
                <resultType>result type 1</resultType>
             </datastruct>
             <datastruct>
                <resultVal>result val 2</resultVal>
                <resultType>result type 2</resultType>
             </datastruct>
             <datastruct>
                <resultVal>result val 3</resultVal>
                <resultType>result type 3</resultType>
             </datastruct>
           </QueryData>
         </QueryBundle>
        </Response>
        END
        
        my $s = SOAP::Deserializer->new;
        
        my $som = $s->deserialize($xml); # som is normally returned by your SOAP call
        
        for my $i ($som->valueof('//datastruct')) {
            print map({qq|$_ => "$i->{$_}" |} keys %{$i}), "\n";
        }
        
        
        Regards
        Duncan
        
          
      Your message has been successfully submitted and would be delivered to recipients shortly.