Read XML responses in to Rails - ruby-on-rails

I have a URL in my controller that when called return an XML response. Let say, the response looks like this;
<?xml version="1.0" encoding="UTF-8"?>
<AutoCreate>
<Response>
<Status>YES</Status>
<name>JOSEPH</name>
<location>HOME</location>
</Response>
</AutoCreate>
How can i read these values status, name and location into variables in my controller and use them.
Thank you in advance.

can you try this,
response_json = Hash.from_xml(response).to_json

So heres an update for Rails 5,
If you are receiving an XML response the header will be 'application/xml'
You access the data using
#read the response and create a Hash from the XML
response = Hash.from_xml(request.body.read)
#read value from the Hash
status = response["AutoCreate"]["Response"]["Status"]

If the value of respose.body is;
<?xml version="1.0" encoding="UTF-8"?>
<AutoCreate>
<Response>
<Status>YES </Status>
<name> JOSEPH </name>
<location> HOME </location>
</Response>
</AutoCreate>
Then i think this should be fine.
require ‘active_support’
result = Hash.from_xml(response.body)
then;
result.status == "YES"
Would this work?

You can use https://github.com/alsemyonov/xommelier
feed = Xommelier::Atom::Feed.parse(open('spec/fixtures/feed.atom.xml'))
puts feed.id, feed.title, feed.updated
feed.entries do |entry|
puts feed.id, feed.title, feed.published, feed.updated
puts feed.content || feed.summary
end

Related

Savon using built in request builder instead of raw xml

If i use savon with raw xml everything works fine, this is the raw xml example:
request = client.call(:authenticate, xml:'<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="xxxx/">
<SOAP-ENV:Body>
<ns1:authenticate>
<ns1:username>user</ns1:username>
<ns1:password>pwd</ns1:password>
<ns1:cultureInfo>it</ns1:cultureInfo>
</ns1:authenticate>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>')
If I use the builtin method to call the method I got an error, this is the code:
credentials={ username: 'user', password: 'pwd!!', cultureInfo: "it" }
response = client.call(:authenticate, message: credentials)
This is the xml produced by the above code:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://tempuri.org/"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<wsdl:authenticate>
<username>user</username>
<password>pwd</password>
<cultureInfo>it</cultureInfo>
</wsdl:authenticate>
</env:Body>
</env:Envelope>
Any Idea?
I usually do just this (untested). It's not pretty but it works.
credentials = { 'ns1:username' => 'user',
'ns1:password' => 'pwd!!',
'ns1:cultureInfo' => "it" }
response = client.call(:authenticate, message: credentials)
You might want to adapt the use of ns1 to the actually used namespace in your case.

Add a node to XML using Nokogiri::XML::Builder

I have a Nokogiri::XML::Builder instance, when I call to_xml it produces following structure:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>...</item>
<item>...</item>
</root>
Using this instance I'd like to add one more <item> node under <root> like this:
def add_static_job(builder)
source = builder.doc.root
item = Nokogiri::XML::Node.new('item', source)
item.content = '<title>Hello</title>'
source << item
end
Unfortunatelly this doesn't produce valid xml in the end, rather something like:
<item><title>Hello<title></item>
What could the problem be?
You could do it in 2 steps :
create title node with "Hello" as content
create item node with title as content
xml = '<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>A</item>
<item>B</item>
</root>'
require 'nokogiri'
doc = Nokogiri::XML.parse(xml)
source = doc.root
title = Nokogiri::XML::Node.new('title', doc)
title.content = "Hello"
item = Nokogiri::XML::Node.new('item', doc)
item << title
source << item
puts doc
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <root>
# <item>A</item>
# <item>B</item>
# <item><title>Hello</title></item></root>

How to parse this returned XML with Nokogiri

I'm attempting to parse this XML with nokogirl but I'm having trouble. Any ideas where I'm going wrong? I'd like to get each Dealer and get the values for each of them.
doc = Nokogiri::Slop(response.body)
puts doc.content #works, shows the response below
puts doc.DTX_LEAD_ID.content #errors, no method found.
puts doc.NEWCAR_PINGGX_RESPONSE.content #errors, no method found
returned XML:
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="www.example.com/">
<?xml version="1.0" encoding="utf-8"?>
<NEWCAR_PINGGX_RESPONSE xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="www.example.com/SellerMessages">
<DTX_LEAD_ID>1779853194</DTX_LEAD_ID>
<SUCCESS>true</SUCCESS>
<CACHED_RESPONSE>false</CACHED_RESPONSE>
<PRICE>20</PRICE>
<DealerList>
<Dealer>
<BUYER_ID>0000-2127</BUYER_ID>
<Reservation_ID>1779853194|0000-2067|520a8037-57c8-497e-be4b-f4ea8dfa6c6f|14187-20</Reservation_ID>
<Price>20</Price>
<Name>Randy's Rides</Name>
<State>MI</State>
<City>Southfield</City>
<Street>2001 Town Center</Street>
<Postalcode>48076</Postalcode>
<Distance>2.56002068066733</Distance>
<DealerGroup id="2067" max_post="5" />
<Contact><Name>John Campbell</Name>
<Phone>2483521314</Phone>
</Contact>
</Dealer>
</DealerList>
</NEWCAR_PINGGX_RESPONSE></string>
Previously I've had a response like this:
<?xml version="1.0" encoding="utf-8"?>
<results>
<status>accepted</status>
<id>1724128693</id>
<purchaseprice>8.0000</purchaseprice>
<error>false</error>
<messages>
<message>coverage available</message>
</messages>
</results>
Which parses really easily with nokogiri:
doc.results.messages.message.content #coverage available
I want to do something like:
doc.NEWCAR_PINGGX_RESPONSE.DealerList.Dealer.Name.content #returns "Randy's Rides"
To see what's wrong with a document use the errors method. After parsing your XML:
doc.errors
# => [#<Nokogiri::XML::SyntaxError: xmlns: URI www.example.com/ is not absolute>,
# #<Nokogiri::XML::SyntaxError: XML declaration allowed only at the start of the document>,
# #<Nokogiri::XML::SyntaxError: xmlns: URI www.example.com/SellerMessages is not absolute>]
To extract the data I'd use something like this:
doc = Nokogiri::XML(XML)
doc.remove_namespaces!
dealers = doc.search('Dealer').map{ |dealer|
{
buyer_id: dealer.at( 'BUYER_ID' ).text,
reservation_id: dealer.at( 'Reservation_ID' ).text,
name: dealer.at( 'Name' ).text
}
}
dealers
# => [{:buyer_id=>"0000-2127",
# :reservation_id=>
# "1779853194|0000-2067|520a8037-57c8-497e-be4b-f4ea8dfa6c6f|14187-20",
# :name=>"Randy's Rides"},
# {:buyer_id=>"0000-2127",
# :reservation_id=>
# "1779853194|0000-2067|e42fd5c6-0a36-4552-8b6a-ad2decebd0db|14200-10",
# :name=>"Jarrett's New Car Dealership 01"},
# {:buyer_id=>"0000-2127",
# :reservation_id=>
# "1779853194|0000-2067|3fecb591-3a81-49f9-82b3-1f0d7fb3f7a6|14160-20",
# :name=>"Campbell's Crazy Cars"},
# {:buyer_id=>"0000-2127",
# :reservation_id=>
# "1779853194|0000-2067|731b09e9-700b-4f41-8cb0-eaf80e861d76|14158-7",
# :name=>"Demo Dealer 3"}]
Of course you'll want to add/remove/change fields being extracted to fit your use-case.
Using slop mode has its dangers, as stated by the Nokogiri documentation.
Don’t use this.
This may or may not be a backhanded compliment.
No, really, don’t use this. If you use it, don’t report bugs.
You’ve been warned!
I've never used it as a result. Often we don't want to use remove_namespaces! either, but it appears safe in your situation.

Rails 2: #to_xml(:methods => :method_name) when #method_name returns an Array

Is it possible to override how #to_xml renders a method result if it's an Array?
Given #numbers is not a field but a method
And #object.numbers #=> [0,1,2,3,4,5]
currently it does:
#object.to_xml(:methods => :numbers)
=> "<object><numbers>012345</numbers></object>"
is it possible to override this behavior so it returns:
#object.to_xml(:methods => :numbers)
=> "<object>
<numbers>
<number>0</number>
<number>1</number>
<number>2</number>
<number>3</number>
<number>4</number>
<number>5</number>
</numbers>
</object>"
(formatted so it's easily read)
Thanks in advance for your suggestions!
You have two options:
Upgrade to Rails 3. It already has the desired output.
gem "activemodel", '~> 3.2.12'
require "active_model"
# This could be an Active Record model
class Result
include ActiveModel::Serializers::Xml
def numbers
(0..5).to_a
end
def attributes
{}
end
end
result = Result.new
puts result.to_xml(:methods => :numbers)
Outputs:
<?xml version="1.0" encoding="UTF-8"?>
<result>
<numbers type="array">
<number type="integer">0</number>
<number type="integer">1</number>
<number type="integer">2</number>
<number type="integer">3</number>
<number type="integer">4</number>
<number type="integer">5</number>
</numbers>
</result>
Use XML Builder where you are in full control of the output. You will have to specify all attributes manually.
# app/views/results/show.xml.builder
xml.instruct!
xml.result {
xml.numbers {
result.numbers.each do |n|
xml.number n
end
}
}
Outputs:
<?xml version="1.0" encoding="UTF-8"?>
<result>
<numbers>
<number>0</number>
<number>1</number>
<number>2</number>
<number>3</number>
<number>4</number>
<number>5</number>
</numbers>
</result>
You can use the :procs option to add a custom rendering for your method inside the model:
def to_xml(options={})
numbers_proc = Proc.new do |options|
xml = options[:builder]
xml.numbers do
numbers.each do |n|
xml.number n
end
end
end
super options.merge(:procs => numbers_proc)
end

Savon + Rails 2 How to modify XML's request structure

I'm using Savon to develop a Web Service Client. Since I'm a beginner I decided to try at first with an example WDSL, which in my case is:
http://www.webservicex.com/CurrencyConvertor.asmx?wsdl
My controller is very simple:
require 'savon'
class WebServiceController < ApplicationController
def index
puts "web_service: IN"
client = Savon::Client.new do
wsdl.document = "http://www.webservicex.com/CurrencyConvertor.asmx?wsdl"
end
response = client.request :conversion_rate do
soap.body = {
:from_currency => 'USD',
:to_currency => 'EUR'
}
end
puts response.to_hash;
render :text => response.to_hash
end
end
The XML produced by that code is:
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://www.webserviceX.NET/"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<ConversionRate>
<wsdl:fromCurrency>USD</wsdl:fromCurrency>
<wsdl:toCurrency>EUR</wsdl:toCurrency>
</ConversionRate>
</env:Body>
</env:Envelope>
However, the XML should be (and I know this because I'm using soapUI):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:web="http://www.webserviceX.NET/">
<soapenv:Header/>
<soapenv:Body>
<web:ConversionRate>
<web:FromCurrency>USD</web:FromCurrency>
<web:ToCurrency>EUR</web:ToCurrency>
</web:ConversionRate>
</soapenv:Body>
</soapenv:Envelope>
I know my XML Request isn't working because I always get '0' (zero) as response, and with the "right" XML Request generated by soapUI I get correct values (such as '0.6959' ...).
Is something missing in my code?
Thank you!!!
two things:
you need to add :wsdl to the call
you need to make sure the spelling of the tags is correct
change to
response = client.request :wsdl, :conversion_rate do
and to
"FromCurrency" => 'USD',
"ToCurrency" => 'EUR'
that should do it for you.

Resources