I am attempting to use Savon to communicate with a legacy web service using SOAP. I am having an issue with regards to how Savon constructs a hash out of the response envelope.
client = Savon.client("http://my-service-endpoint/?wsdl")
response = client.request :my_soap_operation do
soap.body do |xml|
xml.in("test")
end
end
response.to_hash[:my_soap_operation_response][:out].each do |a|
puts "Return element: #{a}"
puts "Name #{a[:name]}"
puts "Type #{a[:type]}"
puts "Code #{a[:code]}"
end
end
If the SOAP response contains more than 1 'out' element, such as this:
<mySoapOperationResponse>
<out>
<code>C1947944</code>
<name>Use</name>
<type>type 1</type>
</out>
<out>
<code>C1947946</code>
<name>name 2</name>
<type>type 2</type>
</out>
<out>
<code>C1947947</code>
<name>name 2</name>
<type>type 3</type>
</out>
</mySoapOperationResponse>
The output is as expected:
Return element: {:code=>"C1947944", :name=>"Use", :type=>"type 1"}
Name Use
Type type 1
Code C1947944
Return element: {:code=>"C1947946", :name=>"name 2", :type=>"type 2"}
Name name 2
Type type 2
Code C1947946
Return element: {:code=>"C1947947", :name=>"name 2", :type=>"type 3"}
Name name 2
Type type 3
Code C1947947
However, if the SOAP response only contains a single 'out' element, such as this:
<mySoapOperationResponse>
<out>
<code>C1947944</code>
<name>name 1</name>
<type>type 1</type>
</out>
</mySoapOperationResponse>
I see this in the console
Return element: [:code, "C1947944"]
... and then I get the error message
can't convert Symbol into Integer
It would appear as though the :name and :type elements are not present in the SOAP response, however I can see them via the response XML message.
I am using Savon v1.2.0.
Addendum
I added the following code to try to further debug the issue:
response = client.request :my_soap_operation do
soap.body do |xml|
xml.in("test")
end
end
pp response.to_hash
response.to_hash[:my_soap_operation_response][:out].each do |a|
pp a
puts "Return element: #{a}"
puts "Name #{a[:name]}"
puts "Type #{a[:type]}"
puts "Code #{a[:code]}"
And I can see the elements being pretty-printed in the response hash. Here is a snapshot of the console:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<mySoapOperationResponse>
<out>
<code>C1947944</code>
<name>name 1</name>
<type>type 1</type>
</out>
</mySoapOperationResponse>
</soapenv:Body>
</soapenv:Envelope>
{:my_soap_operation_response=>
{:out=>{:code=>"C1947944", :name=>"name 1", :type=>"type 1"},
:@xmlns=>"http://namespace.removed.com/"}}
[:code, "C1947944"]
Return element: [:code, "C1947944"]
However it seems like inside the .each loop (looping over each 'out' element), only the first element can be accessed?
I think the issue is, that Savon doesn't return an Array in the second case. You need to check whether [:out] is an Array or a single value and act accordingly. What I did was I wrapped the single value into an Array of one element to keep my program structure simple.
def wrapped_soap_call
# prepare Savon::Client to your liking
client = Savon::Client.new do
# your stuff here
end
# call the SOAP WS
result = client.request :wsdl, :function do
# your stuff here
end
list = result.to_hash[:response][:out]
# if there was only one item list is not an array!
if list.is_a? Array
return list
else
return Array.new << list
end
end
That works for me.