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

force an array on deserialization

Expand Messages
  • adolbey2004
    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
    Message 1 of 3 , Jun 25, 2004
    • 0 Attachment
      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. What I really would like is for
      SOAP::Lite to treat the `QueryData' piece as an array, with anonymous
      hashes for elements, even though they are not actually anonmous. It
      would be something like using Perl classes or SOAP::Data functions on
      messages *coming in* (rather than on the data being used in the
      creation of out-going messages), i.e., making calls to "cast" the
      data coming in. But I don't know how to do this, or if it's even
      possible.

      Does anybody here have any ideas how I could solve this problem??
      Since the message I'm getting is well-formed XML, I could always take
      the raw XML of the response and just iterate through the elements
      with an XML parser. But this somehow smacks of admitting defeat.
      What I'm hoping is that there's a possible solution, I'm just not
      seeing it yet.

      If you have any ideas, please let me know.

      Thanks!!

      Andy
    • Duncan Cameron
      ... Does this code do what you want to achieve? use strict; use SOAP::Lite; my $xml =
      Message 2 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 3 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.