Soap + wsdl troubles

Hi all,

When using wsdl and soap, I fail to get the contents of a second item
(an
array) in a struct across the wire. I see

<?xml version=\"1.0\" encoding=\"utf-8\" ?>
<env:Envelope xmlns:xsd=\"http://www.w3.org/2001/XMLSchema&quot;
    xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/&quot;
    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance&quot;>
  <env:Body>
    <n1:AddObservation xmlns:n1=\"http://tempuri.org/&quot;>
      <n1:ObserverSenderGUID>service@house:vision</n1:ObserverSenderGUID>
      <n1:ObservationEvents>
      </n1:ObservationEvents>
    </n1:AddObservation>
  </env:Body>
</env:Envelope>
              
as you can see the content of observationEvents is missing.
If I dump the Observation w/ SOAPMarshall, I see this

<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <env:Body>
     <Observation xmlns:n1="http://www.ruby-lang.org/xmlns/ruby/type/1.6"
         xsi:type="n1:Struct"
         env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
       <type="xsd:string">Observation</type>
       <member>
         <observerSenderGUID
             xsi:type="xsd:string">service@house:vision</observerSenderGUID>
         <observationEvents n2:arrayType="xsd:anyType[1]"
             xmlns:n2="http://schemas.xmlsoap.org/soap/encoding/"
             xsi:type="n2:Array">
           <item xsi:type="n1:Struct">
             <type xsi:type="xsd:string">ObservationEvent</type>
             <member>
               <observerStationGUID
             xsi:type="xsd:string">
                 service@house:vision
               </observerStationGUID>

....

where the observationEvents are available, but as "anyType". What I want
(i.e.
given as an example for the service that should work), is this:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <AddObservation xmlns="http://tempuri.org/">
       <ObserverSenderGUID>string</ObserverSenderGUID>
       <ObservationEvents>
         <ObservationEvent>
           <ObserverStationGUID>string</ObserverStationGUID>
.....

I have absolutely no idea how to obtain this.
The marshalled stuff is not what is going over the wire.
1) What's the difference between the marshalled snippet and what is
   going across the wire, anyway?
2) How do I pass the array?

ruby 1.8.3 (2005-09-21) [i486-linux]
(on debian testing)

Regards,
Kero.

The (simplified) code to produce the first two xml snippets this is:

require 'soap/marshal'
require 'soap/wsdlDriver'
require 'time'

server = "prc67241668"
port = 8080
service = "ILSA/ilsaservice.asmx"
url = "http://#{server}:#{port}/#{service}"
wsdl_url = "#{url}?WSDL"
client = SOAP::WSDLDriverFactory.new(wsdl_url).create_rpc_driver
client.wiredump_dev = STDERR

Observation = Struct.new(:observerSenderGUID, :observationEvents)
class Observation
  include SOAP::Marshallable
end

ObservationEvent = Struct.new(:observerStationGUID
  # snip rest of fields
)
class ObservationEvent
  include SOAP::Marshallable
end

# simplified string for c.l.r purposes
event1 = ObservationEvent.new("service@house:vision")
ary = [event1]

puts SOAP::Marshal.marshal(obs)

puts client.addObservation(
  obs = Observation.new(
    "urn:ilsa:service@house1.ilsa.com:vision-station-1",
    ary
  )
)

1) What's the difference between the marshalled snippet and what is
   going across the wire, anyway?

One important difference is that the SOAP packets in the two examples are using
different "Soap Styles". Specifically, what you're seeing on the wire (and what
you want) is Document-style. The example that you dumped showed a SOAP packet
that is using Soap Encoding for the XML (i.e., or more specifically, it uses
RPC/Encoded rather than the Doc/Literal style you are looking for).

What's on the wire has the xml document directly in the SOAP message without
using SOAP Encoding:

  <env:Body>
    <n1:AddObservation xmlns:n1=\"http://tempuri.org/\&quot;&gt;
      <n1:ObserverSenderGUID>service@house:vision</n1:ObserverSenderGUID>
      <n1:ObservationEvents>
      </n1:ObservationEvents>
    </n1:AddObservation>
  </env:Body>

See how the XML doc is embedded directly in the body?

What you 'dumped' is different -- notice how both the namespace and the encoding
style are different:

  <env:Body>
     <Observation xmlns:n1="http://www.ruby-lang.org/xmlns/ruby/type/1.6&quot;
         xsi:type="n1:Struct"
         env:encodingStyle="Error;
       <type="xsd:string">Observation</type>

In this fragment the SOAP specifies "SOAP encoding":

   env:encodingStyle="Error;

You're dumping a soap envelope that was serialized using SOAP encoding -- but
the code you're sending SOAP out on the wire uses a different encoding (and
likely different code for serializing the XML).

You can see where you dump the xml that you specify RPC/Encoded style:

client = SOAP::WSDLDriverFactory.new(wsdl_url).create_rpc_driver

See how you're specifying to create an RPC driver (using "create_rpc_driver")?

One of the first steps is to figure out how to dump a version of the soap
envelope that uses Document/Literal rather than the RPC/Encoded for your SOAP.
I'd begin by looking to see if there's something like:

client = SOAP::WSDLDriverFactory.new(wsdl_url).create_doc_driver

If not, look through the documentation to see how to specify Doc/Literal
encoding.

I'm not sure where to go from there, but I think understanding the SOAP encoding
style you need to send should be your first step. I'd also review the WSDL of
the service you're trying to invoke to see what it expects as well -- it would
probably require either one or the other of RPC/Encoded or Doc/Literal. You
need to make sure you using the correct style before trying to invokde the
service.

-kevin

One important difference is that the SOAP packets in the two examples are using
different "Soap Styles". Specifically, what you're seeing on the wire (and what
you want) is Document-style. The example that you dumped showed a SOAP packet
that is using Soap Encoding for the XML (i.e., or more specifically, it uses
RPC/Encoded rather than the Doc/Literal style you are looking for).

Am I looking at different defaults (yuck) or is the wsdl setting this?

[snip direct & soap encoding code fragments]

You can see where you dump the xml that you specify RPC/Encoded style:

client = SOAP::WSDLDriverFactory.new(wsdl_url).create_rpc_driver

See how you're specifying to create an RPC driver (using "create_rpc_driver")?

RPC == remote procedure call (playing advocate of the devil here) what
does that say about encoding? to my uneducated mind, it says nothing
(Ruby Drb does rpc as well; I have a module in my kernel called sunrpc)

No, I don't know soap.

One of the first steps is to figure out how to dump a version of the soap
envelope that uses Document/Literal rather than the RPC/Encoded for your SOAP.
I'd begin by looking to see if there's something like:

client = SOAP::WSDLDriverFactory.new(wsdl_url).create_doc_driver

If not, look through the documentation to see how to specify Doc/Literal
encoding.

"the documentation" ?
I found absolutely no documentation concerning ruby/soap.
I found example code for trivial stuff, so I'm happy I got *something*
across the wire without an error.

I think I want "direct", not soap envelope.

I'm not sure where to go from there, but I think understanding the SOAP encoding
style you need to send should be your first step. I'd also review the WSDL of
the service you're trying to invoke to see what it expects as well -- it would
probably require either one or the other of RPC/Encoded or Doc/Literal. You
need to make sure you using the correct style before trying to invokde the
service.

Does ruby allow me to query it?

What is a SOAPArray? a SOAPStruct? playing with it gives me worse
results, but how can I know which ruby classes match the wsdl
description? Is it built for me? How?

+--- Kero ------------------------- kero@chello@nl ---+

all the meaningless and empty words I spoke |
                      Promises -- The Cranberries |

+--- M38c --- http://members.chello.nl/k.vangelder ---+

One important difference is that the SOAP packets in the two examples are using
different "Soap Styles". Specifically, what you're seeing on the wire (and what
you want) is Document-style. The example that you dumped showed a SOAP packet
that is using Soap Encoding for the XML (i.e., or more specifically, it uses
RPC/Encoded rather than the Doc/Literal style you are looking for).

Am I looking at different defaults (yuck) or is the wsdl setting this?

The server you are attempting to call determines it. THe server describes the web services it makes available (and the soap style, etc) using WSDL (Web Service Description Language).

You can see where you dump the xml that you specify RPC/Encoded style:

client = SOAP::WSDLDriverFactory.new(wsdl_url).create_rpc_driver

See how you're specifying to create an RPC driver (using "create_rpc_driver")?

RPC == remote procedure call (playing advocate of the devil here) what
does that say about encoding? to my uneducated mind, it says nothing
(Ruby Drb does rpc as well; I have a module in my kernel called sunrpc)

RPC SOAP calls generally use SOAP Encoding. See the WSDL spec for more details:

     Web Services Description Language (WSDL) Version 2.0 Part 1: Core Language

You can link from this document to others that define SOAP encoding, etc.

The standards docs provide pretty good detail -- but if you're just beginning, it's likely better to read some articles on-line to get started or pick up a book on web services. I'd look at amazon and figure out which is best. It's really a pretty deep subject and takes more than a few listserv messages to summarize, although you can get by pretty well if you just learn the basics.

One of the first steps is to figure out how to dump a version of the soap
envelope that uses Document/Literal rather than the RPC/Encoded for your SOAP.
I'd begin by looking to see if there's something like:

client = SOAP::WSDLDriverFactory.new(wsdl_url).create_doc_driver

If not, look through the documentation to see how to specify Doc/Literal
encoding.

"the documentation" ?
I found absolutely no documentation concerning ruby/soap.
I found example code for trivial stuff, so I'm happy I got *something*
across the wire without an error.

I think I want "direct", not soap envelope.

There are other ways to send XML over HTTP without using SOAP and "web services" -- assuming you are in control of BOTH the server and the client. If you control both ends, then simply HTTP posting XML as a string to a normal rails controller would work fine. Then the server side of your app can just get the xml and parse it directly.

If you don't control the server you need to interact with and it publishes its data using SOAP/Web Services, then you probably have to use SOAP/Web Services. If that's the case, then get the WSDL from the server (it looks like you've got a URL that points to the WSDL that you can just point a browser to to get the WSDL. Once you've got the WSDL then look it over and do some research to figure out what it wants you to do -- there's really no other way. Whoever publishes the server can likely help you.

Once you understand how the web service works, you can probably figure out the ruby classes without much documentation --

I'm not sure where to go from there, but I think understanding the SOAP encoding
style you need to send should be your first step. I'd also review the WSDL of
the service you're trying to invoke to see what it expects as well -- it would
probably require either one or the other of RPC/Encoded or Doc/Literal. You
need to make sure you using the correct style before trying to invokde the
service.

Does ruby allow me to query it?

What is a SOAPArray? a SOAPStruct? playing with it gives me worse
results, but how can I know which ruby classes match the wsdl
description? Is it built for me? How?

I wish I could answer these. Sorry, but my knowledge is primarily from the Java world -- I've not used web services with Ruby yet. I know how to understand the XML and the data on the wire, but the ruby classes are beyond what I know currently... Sorry!

···

On Nov 2, 2005, at 6:22 PM, Kero wrote:

Am I looking at different defaults (yuck) or is the wsdl setting this?

The server you are attempting to call determines it. THe server
describes the web services it makes available (and the soap style,
etc) using WSDL (Web Service Description Language).

ok
(how woul I tell marshall to do the same?)

The standards docs provide pretty good detail -- but if you're just
beginning, it's likely better to read some articles on-line to get
started or pick up a book on web services. I'd look at amazon and
figure out which is best. It's really a pretty deep subject and takes
more than a few listserv messages to summarize, although you can get
by pretty well if you just learn the basics.

Way too deep (and I just need to tell a system something, I don't want
to do this for a living :slight_smile:

If you don't control the server you need to interact with and it
publishes its data using SOAP/Web Services, then you probably have to
use SOAP/Web Services. If that's the case, then get the WSDL from the
server (it looks like you've got a URL that points to the WSDL that
you can just point a browser to to get the WSDL. Once you've got the
WSDL then look it over and do some research to figure out what it
wants you to do -- there's really no other way. Whoever publishes the
server can likely help you.

Once you understand how the web service works, you can probably
figure out the ruby classes without much documentation --

I got the wsdl. I looked at it (again) and noticed something I hadn't
realized before: the array is the sole field of a ComplexType.

For Ruby that means I need to wrap the array with my observations into a
Struct. And behold! that works.

Does ruby allow me to query it?

What is a SOAPArray? a SOAPStruct? playing with it gives me worse
results, but how can I know which ruby classes match the wsdl
description? Is it built for me? How?

I wish I could answer these. Sorry, but my knowledge is primarily
from the Java world -- I've not used web services with Ruby yet. I
know how to understand the XML and the data on the wire, but the ruby
classes are beyond what I know currently... Sorry!

Your help is appreciated :slight_smile:

Even though my problem is solved, the question about SOAPArray and
SOAPStruct remains. Anyone?

+--- Kero ------------------------- kero@chello@nl ---+

all the meaningless and empty words I spoke |
                      Promises -- The Cranberries |

+--- M38c --- http://members.chello.nl/k.vangelder ---+

Quoting Kero <kero@chello.single-dot.nl>:

>
> Once you understand how the web service works, you can probably
> figure out the ruby classes without much documentation --

I got the wsdl. I looked at it (again) and noticed something I hadn't
realized before: the array is the sole field of a ComplexType.

For Ruby that means I need to wrap the array with my observations into a
Struct. And behold! that works.

Congratulations on solving your problem!

-kevin