Mapping Soap Response without wsdl
So I've been wrestling with trying to map a Soap4R response to Ruby classes. While I got really close I couldn't quite get this Account object to map an array of attributes (the a element below). Here's a snippet of the xml response:
<CreateAccountResponse xmlns="urn:zimbraAdmin">
<account name="fri1@dev.razorstream.com" id="19832977-01ae-4290-a753-bc78c82196da">
<a n="zimbraPrefMailPollingInterval">5m</a>
<a n="zimbraContactMaxNumEntries">0</a>
<a n="zimbraPrefGroupMailBy">conversation</a>
<snipped a ton of additional 'a' elements>
</account>
</CreateAccountResponse>
Though I suspect there are plenty more inner workings of Soap4R that would make this easier I've come to the easiest solution for accessing this data. Instead of trying to map a hand-coded class I just let it come back as a soap mapping object. This works for most things but in this case the array of 'a' elements are all strings. The strings are just the value of the 'a' element tag. The vital piece, the 'n' attribute, seems to be missing... or hidden. It seems that Soap4R modifies the value's String class to have an __xmlattr attribute. Look at the following yaml output of the account object:
!ruby/object:Account
__xmlattr:
!ruby/object:XSD::QName ?
name: id
namespace:
source:
: 19832977-01ae-4290-a753-bc78c82196da
!ruby/object:XSD::QName ?
name: name
namespace:
source:
: wed7@zdev1.razorstream.com
a:
- !str
str: 5m
\"@__xmlattr\":
!ruby/object:XSD::QName ?
name: n
namespace:
source:
: zimbraPrefMailPollingInterval
Curious, but if you do an inspect you won't see these hidden nuggets. In this particular case I was able to write this converter method to pull these out and make them easier to get to:
if response.account
acct = response.account
class << acct
attr_accessor :config, :id, :name
end
# Iterate over user properties/config and add to the config hash
acct.config = Hash[*acct.a.map do |value|
[value.__xmlattr[XSD::QName.new(nil, 'n')], String.new(value)]
end.flatten]
# Populate our id and name attributes
acct.id = acct.__xmlattr[XSD::QName.new(nil,"id")]
acct.name = acct.__xmlattr[XSD::QName.new(nil,"name")]
@account = acct
end
I'd still prefer to map these objects correctly so that I don't need to run code like this. However, this is an acceptible solution for now.
Posted by Marty Haught on Friday, August 04, 2006
I have not enabled comments for my blog yet. If you'd like to pass on a comment or suggestion, please email me at mghaught __at__ gmail -dot- com.