Nokogiri: Searching node with ":" - ruby-on-rails

I have a simple XML structure:
<?xml version="1.0" encoding="utf-8"?>
<response xmlns:msg="..." xmlns:ld="...">
<msg:testResultBatch providerId="12345" testName="Hello Labs">
.
.
.
</msg:testResultBatch>
</response>
When I pass it to Nokogiri.XML like:
req = Nokogiri.XML('
<?xml version="1.0" encoding="utf-8"?>
<response xmlns:msg="..." xmlns:ld="...">
<msg:testResultBatch providerId="12345" testName="Hello Labs">
.
.
.
</msg:testResultBatch>
</response>
')
I'm unable to search nodes with ":". So,
req.search("response") # works
but,
req.search("msg:testResultBatch") # doesn't works
and gives me []

By using xpath and '//msg:testResultBatch' you can get the msg:testResultBatch:
require 'nokogiri'
req = Nokogiri.XML('
<?xml version="1.0" encoding="utf-8"?>
<response xmlns:msg="..." xmlns:ld="...">
<msg:testResultBatch providerId="12345" testName="Hello Labs">
</msg:testResultBatch>
</response>
')
p req.xpath('//msg:testResultBatch').first.name # "testResultBatch"

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>

Nokogiri : NoMethodError (undefined method `inner_html' for nil:NilClass)

I'm trying to parse a simple XML data with nokogiri.
this is my XML:
POST /.... HTTP/1.1
Host: ....
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://...."
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="...." xmlns:xsd="...." xmlns:soap="....">
<soap:Body>
<WS_QueryOnSec xmlns="......">
<type>string</type>
<ID>string</ID>
</WS_QueryOnSec>
</soap:Body>
</soap:Envelope>
and this is my simle request:
require "nokogiri"
#doc = Nokogiri::XML(request.body.read)
#something = #doc.at('type').inner_html
But Nokogiri can not find the Type or ID node.
When I change the data into this every thing works fine:
<soap:Body>
<type>string</type>
<ID>string</ID>
</soap:Body>
It seems the problem is the raw text above the data and the nods with xmlns or the other attributes!
What do you recommend to resolve this ?
The first "XML" isn't XML. It's text that contains XML. Remove the header information down to the blank line and try it again.
I think it'd help you to read the XML spec or to read some tutorials about creating XML which will help you understand how it's defined. XML is a tight specification and doesn't allow any deviation. The syntax is pretty flexible, but you have to play by its rules.
Consider these examples:
require 'nokogiri'
doc = Nokogiri::XML(<<EOT)
foo
<root>
<node />
</root>
EOT
doc.errors # => [#<Nokogiri::XML::SyntaxError: Start tag expected, '<' not found>]
Removing the text, which is outside the root tag results in a proper parse:
require 'nokogiri'
doc = Nokogiri::XML(<<EOT)
<root>
<node />
</root>
EOT
doc.errors # => []
<root> isn't neccesarily the name of the "root" node, it's just the outermost tag:
doc = Nokogiri::XML(<<EOT)
<foo>
<node />
</foo>
EOT
doc.errors # => []
and still results in a valid DOM/internal representation of the document:
puts doc.to_html
# >> <foo>
# >> <node></node>
# >> </foo>
Your XML sample is using namespaces, which complicate matters somewhat. The Nokogiri documentation talks about how to deal with them, so you'll want to understand that part of parsing XML because you'll encounter it again. Here's the easy way of working with them:
require 'nokogiri'
doc = Nokogiri::XML(<<EOT)
<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns:xsi="...." xmlns:xsd="...." xmlns:soap="....">
<Body>
<WS_QueryOnSec xmlns="......">
<type>string</type>
<ID>string</ID>
</WS_QueryOnSec>
</Body>
</Envelope>
EOT
namespaces = doc.collect_namespaces
doc.at('type', namespaces).text # => "string"

Ruby to_xml change attributes name for Api request

I want to convert the keys for xml response So that they match with third party Api request.
class Person1
include ActiveModel::Serializers::Xml
attr_accessor :name, :age
def attributes
{'name' => nil, 'age' => nil}
end
def capitalized_name
name.capitalize
end
end
p = Person1.new
p.name = "test"
puts p.to_xml
output ::-
<?xml version="1.0" encoding="UTF-8"?>
<person1>
<age nil="true"/>
<name>test</name>
</person1>
I am looking for a way to change keys in xml output like.
<?xml version="1.0" encoding="UTF-8"?>
<person1>
<Age nil="true"/>
<Name>test</Name>
</person1>
How about:
puts p.to_xml(:camelize => true)
<?xml version="1.0" encoding="UTF-8"?>
<Person1>
<Age nil="true"/>
<Name>test</Name>
</Person1>
Or if the uppercase Person bothers you, I guess you can do something like that:
puts p.to_xml(:camelize => true).sub('<Person1>','<person1>').sub('</Person1>','</person1>')
<?xml version="1.0" encoding="UTF-8"?>
<person1>
<Age nil="true"/>
<Name>test</Name>
</person1>

Read XML responses in to 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

Resources