Parsing SOAP response using SAVON response.to_hash conversion method - ruby

I am having trouble parsing a SOAP response.
Here is my code:
require 'rubygems'
require 'savon'
client = Savon::Client.new "http://1.2.3.4/xyz/abcd/fsds.wsdl"
res = client.query_unpaid_assesments do |soap,wsse|
soap.namespaces["xmlns:SOAP-ENV"] = "http://schemas.xmlsoap.org/soap/envelope/"
soap.namespaces["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance"
soap.namespaces["xmlns:xsd"] = "http://www.w3.org/2001/XMLSchema"
wsse.username="xyz"
wsse.password="123"
soap.body = {:orderNumber => 111222333 }
end
response = Savon::Response#to_hash
hres = response.to_hash
all_data = hres[:response][:asses_data][:date][:amount][:assesReference][:year][:cusOffCode][:serie][:number][:date][:time]
Here is the error that I am having:
undefined method to_hash for Savon::Response:Class (NoMethodError)
"res" is giving me xml response that I would like to have in hash.
I read previous related questions and they recommending to use response.to_hash , which I did and is throwing the error specified above.
How can i get rid of this error and have my response into hash.
thanks for ur help
I forgot to post the body of the xml response that I would like to parse:
<soapenv:Body>
<response>
<ns203:assesData xmlns:ns203="http://asdfsd.sdfsd.zbc.org">
<ns203:date>2010-09-01</ns203:date>
<ns203:amount>34400</ns203:amount>
<ns203:asesReference>
<ns203:year>2010</ns203:year>
<ns203:cusOffCode>098</ns203:customsOfficeCode>
<ns203:serie>F</ns203:serie>
<ns203:number>524332</ns203:number>
<ns203:date>2010-11-11</ns203:date>
<ns203:time>10:11:103</ns203:time>
</ns203:assesReference>
</ns203:assesData>
</response>
</soapenv:Body>

I believe you need to be trying to #to_hash res itself, the returned Savon::Response object, instead of the Savon::Response class.
So hres = res.to_hash should work.
An example I found (at the end of here: http://blog.nofail.de/2010/01/savon-vs-handsoap-calling-a-service/) should give you the idea.
class SavonBankCode
def self.zip_code(bank_code)
client = Savon::Client.new Shootout.endpoints[:bank_code][:uri]
response = client.get_bank { |soap| soap.body = { "wsdl:blz" => bank_code } }
response.to_hash[:get_bank_response][:details][:plz]
end
end
An alternative would be to parse the result with Nokogiri or similar, meaning you could do something like this:
require 'nokogiri'
response = res.xpath("//ns203:assesData", "ns203" => "http://asdfsd.sdfsd.zbc.org")
date = response.xpath("ns203:date", "ns203" => "http://asdfsd.sdfsd.zbc.org")
amount = response.xpath("ns203:amount", "ns203" => "http://asdfsd.sdfsd.zbc.org")
number = response.xpath("ns203:asesReference/ns203:number", "ns203" => "http://asdfsd.sdfsd.zbc.org")
etc. etc. Ugly as sin of course, but hey it is an (untested or refined) alternative ;)
Good luck!

You can also try response.body.
It returns a hash

Related

TypeError problem: no implicit conversion in Sinatra + JSON.parse

I'm trying to set up a web hook, following this GitHub tutorial
require 'sinatra'
require 'json'
require 'net/http'
require 'pp'
set :port, 31415
# Descarga las diferencias hechas para un push
post '/' do
push = JSON.parse(request.body.read)
piezas = push["compare"].split("/")
api_url = "/repos/#{piezas[3]}/#{piezas[4]}/compare/#{piezas[6]}"
diff = Net::HTTP.get(URI("https://api.github.com#{api_url}"))
puts diff.class
pp(JSON.parse(diff))
end
diff.class prints:
String
And, as a matter of fact, the last sentence works correctly, printing via pp the structure. However, after printing, it yields the error
[2018-10-25 20:00:23] ERROR TypeError: no implicit conversion of Array into String
It's not referencing any line in the script, but would it be possible that the error would be in the first JSON.parse? Could it be that request.body.read would be an array?
Update I couldn't golf it down to any of the JSON.parse separately. Downloading the hook payload works OK, downloading the JSON from the GitHub API works without a glithc. Somehow it's using them together what does not work.
It's possible the library is treating the response like text. Try adding an Accept header. This worked for me:
request["Accept"] = "application/json"
example:
uri = URI.parse("https://api.github.com")
req = Net::HTTP::Get.new(URI("https://api.github.com/repos/JJ/microservices-broker/compare/d5d39c5db99d...bbbf695d1bf2"))
req["Accept"] = 'application/json'
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
response = http.request(req)
json = JSON.parse(response.body)
json['url']
# or
json = JSON.parse(response.body, symbolize_names: true)
json[:url]
(EDIT:) Also, Using Net::HTTP is really painful. Please checkout these libraries:
https://github.com/lostisland/faraday
https://github.com/octokit/octokit.rb

parsing a httparty get response in ruby

I can't figure out how to parse this response. I'm only trying to extract one single count from this.
UPDATE:
How can I parse this XML response?
Here's my API connection information
I'm using Ruby irb, with HTTParty
require 'httparty'
api_key = "6e569b3d-3457-9485-edb2-eccd27272dbf"
secret_key = "MDVkYTQzMDgtOWRiOC04NDULTRkYWUtODQwOTc5ZDFkZTQ3NTZhOG1NWQtNmMzMi0xYjE0LWYMzgtNDc0YzU0MWYxYmUx"
def urlncode(string)
URI.escape(string, Regexp.new("[Generating an API Signature^#{URI::PATTERN::UNRESERVED}]"))
end
salt = rand(10000000000).to_s
hash = OpenSSL::HMAC.digest('sha256', secret_key, salt)
signature = urlncode(Base64.encode64(hash))
# controller / action
api_path = "/Tickets/TicketCount"
url = "http://support.myorganization.org/api/index.php?e=#{api_path}"
response = HTTParty.get("#{url}&apikey=#{api_key}&salt=#{salt}&signature=#{signature}").parsed_response
My response: (truncated)
response = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ticketcount>\n<departments>\n<department id=\"0\">\n<totalitems><![CDATA[11]]></totalitems>\n<lastactivity><![CDATA[1423657406]]></lastactivity>\n<totalunresolveditems><![CDATA[11]]></totalunresolveditems>\n<ticketstatus id=\"1\" lastactivity=\"1423657406\" totalitems=\"6\" />\n<ticketstatus id=\"2\" lastactivity=\"1422566730\" totalitems=\"5\" />\n<tickettype id=\"1\" lastactivity=\"1423657406\" totalitems=\"11\" totalunresolveditems=\"11\" />\n<ownerstaff id=\"0\" lastactivity=\"1423657406\" totalitems=\"7\" totalunresolveditems=\"7\" />\n<ownerstaff id=\"4\" lastactivity=\"1420825202\" totalitems=\"2\" totalunresolveditems=\"2\" />\n<ownerstaff id=\"17\" lastactivity=\"1422452400\" totalitems=\"1\" totalunresolveditems=\"1\" />\n<ownerstaff id=\"26\" lastactivity=\"1422566730\" totalitems=\"1\"
When I try to do:
response['ticket count']
I get this: (which is obviously not all the data included)
irb(main):029:0> data['ticketcount']
=> "ticketcount"

How do I POST XML using Faraday and receive response?

I am attempting to learn Ruby/Faraday. I need to POST XML to a RESTful web service and am confused on how to do this.
I have a string containing the XML as follows:
require "faraday"
require "faraday_middleware"
mystring = %&<xml><auth><user userid='username' pwd='password'/></auth></xml>&
How do I post the XML to a URL and receive the result? I am trying to do something like:
conn = Faraday.new(:url=>'http://url')
conn.post '/logon' {mystring}
I get the message:
SyntaxError: (irb):11: syntax error, unexpected '{', expecting $end
conn.post '/logon' {mystring}
Edit 1
I have gotten the POST request to work. My code is provided below.
require "faraday"
require "faraday_middleware"
myString = %&<xml><auth><user userid='username' pwd='password'/></auth></xml>&
myUrl = %&url&
conn = Faraday.new(:url => myUrl) do |builder|
builder.response :logger #logging stuff
builder.use Faraday::Adapter::NetHttp #default adapter for Net::HTTP
end
res = conn.post do |request|
request.url myUrl
request.body = myString
end
puts res.body
According to the documentation:
conn = Faraday.new(:url=>'http://url')
conn.post '/logon', mystring
There are two errors in your code. The first one is that you are missing a comma between the URL and the variable causing { mystring } to be interpreted as a block.
The second error is that mystring already holds a string and the following code does not make sense in Ruby:
{ "string" }
Thus conn.post '/logon', mystring is wrong. So the final result is:
conn = Faraday.new(:url=>'http://url')
conn.post '/logon', mystring
or:
conn = Faraday.new(:url=>'http://url')
conn.post '/logon', { :key => mystring }
if you want to submit a key/value POST body. But this is not your case, because you are already posting an XML body.

Parameters for a Ruby HTTP Put call

I'm having trouble getting parameters passed in an HTTP Put call, using ruby. Take a look at the "put_data" variable.
When I leave it as a hash, ruby says:
undefined method `bytesize' for #<Hash:0x007fbf41a109e8>
if I convert to a string, I get:
can't convert Net::HTTPUnauthorized into String
I've also tried doing just - '?token=wBsB16NSrfVDpZPoEpM'
def process_activation
uri = URI("http://localhost:3000/api/v1/activation/" + self.member_card_num)
Net::HTTP.start(uri.host, uri.port) do |http|
headers = {'Content-Type' => 'text/plain; charset=utf-8'}
put_data = {:token => "wBsB16NSrfVDpZPoEpM"}
response = http.send_request('PUT', uri.request_uri, put_data, headers)
result = JSON.parse(response)
end
if result['card']['state']['state'] == "active"
return true
else
return false
end
end
I've searched all around, including rubydocs, but can't find an example of how to encode parameters. Any help would be appreciated.
Don't waste your time with NET::HTTP. I used 'rest-client' and had this thing done in minutes...
def process_activation
response = RestClient.put 'http://localhost:3000/api/v1/card_activation/'+ self.member_card_num, :token => "wBsB1pjJNNfiK6NSrfVDpZPoEpM"
result = JSON.parse(response)
return result['card']['state']['state'] == "active"
end

Ruby HTTP get with params

How can I send HTTP GET request with parameters via ruby?
I have tried a lot of examples but all of those failed.
I know this post is old but for the sake of those brought here by google, there is an easier way to encode your parameters in a URL safe manner. I'm not sure why I haven't seen this elsewhere as the method is documented on the Net::HTTP page. I have seen the method described by Arsen7 as the accepted answer on several other questions also.
Mentioned in the Net::HTTP documentation is URI.encode_www_form(params):
# Lets say we have a path and params that look like this:
path = "/search"
params = {q: => "answer"}
# Example 1: Replacing the #path_with_params method from Arsen7
def path_with_params(path, params)
encoded_params = URI.encode_www_form(params)
[path, encoded_params].join("?")
end
# Example 2: A shortcut for the entire example by Arsen7
uri = URI.parse("http://localhost.com" + path)
uri.query = URI.encode_www_form(params)
response = Net::HTTP.get_response(uri)
Which example you choose is very much dependent on your use case. In my current project I am using a method similar to the one recommended by Arsen7 along with the simpler #path_with_params method and without the block format.
# Simplified example implementation without response
# decoding or error handling.
require "net/http"
require "uri"
class Connection
VERB_MAP = {
:get => Net::HTTP::Get,
:post => Net::HTTP::Post,
:put => Net::HTTP::Put,
:delete => Net::HTTP::Delete
}
API_ENDPOINT = "http://dev.random.com"
attr_reader :http
def initialize(endpoint = API_ENDPOINT)
uri = URI.parse(endpoint)
#http = Net::HTTP.new(uri.host, uri.port)
end
def request(method, path, params)
case method
when :get
full_path = path_with_params(path, params)
request = VERB_MAP[method].new(full_path)
else
request = VERB_MAP[method].new(path)
request.set_form_data(params)
end
http.request(request)
end
private
def path_with_params(path, params)
encoded_params = URI.encode_www_form(params)
[path, encoded_params].join("?")
end
end
con = Connection.new
con.request(:post, "/account", {:email => "test#test.com"})
=> #<Net::HTTPCreated 201 Created readbody=true>
I assume that you understand the examples on the Net::HTTP documentation page but you do not know how to pass parameters to the GET request.
You just append the parameters to the requested address, in exactly the same way you type such address in the browser:
require 'net/http'
res = Net::HTTP.start('localhost', 3000) do |http|
http.get('/users?id=1')
end
puts res.body
If you need some generic way to build the parameters string from a hash, you may create a helper like this:
require 'cgi'
def path_with_params(page, params)
return page if params.empty?
page + "?" + params.map {|k,v| CGI.escape(k.to_s)+'='+CGI.escape(v.to_s) }.join("&")
end
path_with_params("/users", :id => 1, :name => "John&Sons")
# => "/users?name=John%26Sons&id=1"

Resources