Monday, March 5, 2012

Ruby, SOAP, and XML Attributes

If you're looking to interface with a SOAP web service from a mobile device -- especially Android -- don't.  The best SOAP library I could find for Android was ksoap2, and it is way to fringe for comfort.

We decided that it would be best to write a RESTful service facade to front the SOAP service instead.  Trying to flesh it out quickly, I turned to Ruby with soap4r, whereupon I ran smack into a problem that took me a few days to track down the resolution to.

The soap request needed to look something like this:

    

Simple, right? Well the hard part is the attribute.  WSDLDriverFactory is not well documented.  Actually, I don't think it is documented at all, but I found examples on the web of how to use it.  You pass your soap method a map of the values, like this:
{:rootElement => {:childElement => {:myId => "123456"}}}
That works great if "myId" is an element contained by childElement, but in our case it's an attribute.  How do you tell the proxy that myId is the value of an attribute, not the valude of an element? You would think that it would be smart enough to figure it out, but it's not. After digging around for hours, I finally found an obscure reference to the answer: you have to prefix the attribute name with "xmlattr_". So the correct map looks like this:
{:rootElement => {:childElement => {:xmlattr_myId => "123456"}}}
The full code looks like:
require 'soap/wsdlDriver'

wsdl = 'http://host:port/some-service?WSDL'

client = SOAP::WSDLDriverFactory.new( wsdl ).create_rpc_driver
client.wiredump_dev = $stdout
client.options["protocol.http.basic_auth"] << [wsdl, 'MY_USER', 'MY_PASSWORD']
result = client.doSomething(
{:rootElement =>
  {:childElement => {:xmlattr_myId => "123456"}}})