2

I am attempting to access a SOAP service using the Ruby gem Savon. I can connect to the service and make a request and receive the response, but I cannot parse the response.

The response contains multiple href references to multiRef elements. When I try to decode it using

response.to_hash[:get_user_risk_profile_response][:get_user_risk_profile_return][:href]

I get #id0. How do I follow the id0 reference?

The SOAP response is below. Thanks!

<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>
    <getUserStatusResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <getUserStatusReturn href="#id0"/>
    </getUserStatusResponse>
    <multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns1:UserRiskProfileBean" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://xrisk.api.example.com">
      <parameters xsi:type="ns2:ArrayOf_tns3_ParamBean" xsi:nil="true" xmlns:ns2="http://api.example.com"/>
      <siteID xsi:type="soapenc:string">UNKNOWN</siteID>
      <userID xsi:type="soapenc:string">sam.wiggins</userID>
      <userRiskScore href="#id1"/>
      <userRiskScoreDT xsi:type="xsd:dateTime">2011-02-16T18:15:50.012Z</userRiskScoreDT>
    </multiRef>
    <multiRef id="id1" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">640</multiRef>
  </soapenv:Body>
</soapenv:Envelope>
Dave Isaacs
  • 4,499
  • 6
  • 31
  • 44

2 Answers2

1

It seems that the best approach, for the moment, was to just roll my own parsing using XPath. It's crude and probably not very bullet proof, but will do the job until something better comes along.

class SavonResponseXMLParser

  def initialize(response_xml)
    @doc = REXML::Document.new(response_xml)
  end

  def get_property(property)
    property_value = nil
    elements = REXML::XPath.match(@doc, "//#{property}")
    if (elements.length == 1)
      element = elements[0]
      href = element.attributes['href']
      if (href)
        href =~ /^#id(\d+)/
        multiref_elements = REXML::XPath.match(@doc,"//multiRef[@id='id#{$1}']")
        if (multiref_elements.length == 1)
          multiref_element = multiref_elements[0]
          property_value = multiref_element.text 
        end
      else
        property_value = element.text
      end
    end
    return property_value
  end
end
Dave Isaacs
  • 4,499
  • 6
  • 31
  • 44
0

You'll have to resolve the references manually:

id = response.to_hash[:get_user_risk_profile_response][:get_user_risk_profile_return][:href]
references = response.to_hash[:multi_ref]
result = references.select {|ref| ref[:id] == id.sub('#', '') }

I'd recommend putting the above in a helper method/module:

module MultiRef
  def resolve_ref(id)
    references = to_hash[:multi_ref]
    references.select {|ref| ref[:id] == id.sub('#', '') }
  end
end
Savon::Response.send(:include, MultiRef)

Then simply do:

response.resolve_ref("#id1")

Recursively replacing href hash values with their appropriate referenced values is left as an exercise to the reader ;)

Jacob
  • 22,785
  • 8
  • 39
  • 55
  • I tried something like that, but ran into a roadblock. One of my SOAP responses contains reference values up to #id10. The response.to_hash method decodes the 10 multiRef elements into an Array. Unfortunately the order of the values in the Array are not necessarily sequential. For example, multiRef href=#id5" does not end up being the 5th item in the Array. It could be any item in the Array. I think this is a bug in the to_hash method. – Dave Isaacs Feb 24 '11 at 14:53
  • Also, I believe the the Savon developer is currently working on proper handling for multipart responses. In the meantime, I am parsing the response by using REXML::XPath to query the response.to_xml string. This is working fine. – Dave Isaacs Feb 24 '11 at 14:58
  • The above solution should work, as it looks up by actual ID, and not by array index. But yes, using XPath on the raw XML response makes a lot more sense. – Jacob Feb 25 '11 at 17:18
  • Oh, by the way, why not post your solution here? Answered questions are worth more than partial solutions, even if you are answering your own question. – Jacob Feb 25 '11 at 17:20