I am using ruby 2.0.0 and the Nokogiri gem. I have the following code:
builder = Nokogiri::XML::Builder.new do |xml|
xml['s'].Envelope('xmlns:s' => "http://schemas.xmlsoap.org/soap/envelope/") {
xml['s'].Body {
xml.GetList('xmlns' => "http://tempuri.org/") {
xml.listRequest('i:type' => "b:NpsListRequest", 'xmlns:a' => "http://schemas.datacontract.org/2004/07/Services.List", 'xmlns:i' => "http://www.w3.org/2001/XMLSchema-instance", 'xmlns:b' => "http://schemas.datacontract.org/2004/07/Services.List.Strategies")
}
}
}
end
The above is incomplete. I need to pass in an ID to actually make the call, however this is what the above returns:
<?xml version="1.0" encoding="ISO-8859-1"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetList xmlns="http://tempuri.org/">
<listRequest xmlns:a="http://schemas.datacontract.org/2004/07/Services.List" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:b="http://schemas.datacontract.org/2004/07/Services.List.Strategies" i:type="b:NpsListRequest"/>
</GetList>
</s:Body>
</s:Envelope>
The issue is with the <listRequest xmlns:a code block. In the XML I am creating, I am passing in i:type as the first key, however the response is putting xmlns:a as the first key. Why is that happening?
You are using Nokogiri Builder to create XML attributes.
The XML specification declares that attribute order is not significant. See the XML Specification specifically Section 3.1 where it says:
the order of attribute specifications in a start-tag ... is not significant.
Therefore, the specific way it is handled by an XML library is undefined, but all will produce valid results.
Related
I am setting up a method in a Rails app which posts 2 bits of data to an endpoint api.
Using Nokogiri to parse the data, and Faraday to send request, I have got very close to what is needed for successful post of multiple order items data, excepting a key issue.
This is that I am getting unwanted repeats of the top level node, because it is inside an each loop, which is needed for the items, whereas we only need that once. Below is the method, whereby an object is parsed and the order items put in to place in xml with Nokogiri and here we are just accessing the closed orders in the data object by definition, and just sending the id and quantity for each of them.
def update_backoffice_closed_orders(bj_update)
bj_update.each do |item|
if item['order_status'] == 'closed'
id = item['order_number']
del = item['quantity_fulfilled']
# Set up the connection to the Backoffice API and items to be updated
#xml_post = Nokogiri::XML::Builder.new do |xml|
xml.supplier {
xml.sid SID
xml.password PASSWORD
xml.order {
xml.id id
xml.volume_Delivered del
}
}
end
# Send the request to the Backoffice API
#response = CONNECTION.post do |req|
req.url '/suppliers/orderDataUpdate.php'
req.body = #xml_post.to_xml
end
end
end
end
This is working OK except failing at api because the top level nodes repeat like so:
<?xml version="1.0"?>
<supplier>
<sid>344</sid>
<password>4657gdhtey6</password>
in each order, whereas all are only needed once at top, with supplier closing tag after all the orders in between, as in the example below.
<?xml version="1.0"?>
<supplier>
<sid>344</sid>
<pw>4657gdhtey6</pw>
<order>
<id>3686414</id>
<volume_delivered>480</volume_delivered>
</order>
<order>
<id>3686425</id>
<volume_delivered>480</volume_delivered>
</order>
<order>
<id>3686443</id>
<volume_delivered>480</volume_delivered>
</order>
</supplier>
CONNECTION is a Faraday constant and works fine, as are SID and PASSWORD, if I can get the Nokogiri #xml_post like above, then it is exactly as needed and used in Postman tests for the api, and should work. If anyone knows how to adjust the xml builder to achieve that in this method, please share?
Biggest problem seems the <?xml opening tag which is default with Nokogiri.
Many Thanks
In Qmetry, trying to validate xpath from the soap api response. But it returns false.
API response has root is <soapenv:Envelope (can't paste full response as its content is big)
When tried running below steps. With both options below.
And response should have xpath '/soapenv:Envelope'
And response should have xpath '/Envelope'
This returns False though the response has xpath.
Can any one please help me in resolving this isse?
If Envelope is root element you should not use it in argument to response should have xpath. Given following as sample response:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
SOAP-ENV:encodingStyle = "http://www.w3.org/2001/12/soap-encoding">
<SOAP-ENV:Body xmlns:m = "http://www.example.com/quotation">
<m:GetQuotationResponse>
<m:Quotation>Here is the quotation</m:Quotation>
</m:GetQuotationResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The root node (Envelope in this case)need not to be added in xpath or used as .
Following xpaths should work:
./Body
.//Body
//Body
//GetQuotationResponse
//Body/GetQuotationResponse/Quotation
//GetQuotationResponse/Quotation
//Quotation
I am using Ruby on Rails and need to read the contents of an xml file into an array?
I pulled the data from an API using the following:
uri = URI.parse("myAPIurl.com&show=storeID,name")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
Looking for a lean method to iterate this xml file into an array using Ruby on Rails.
For simple use cases you can use:
xml = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<hash>
<foo type="integer">1</foo>
<bar type="integer">2</bar>
</hash>
XML
hash = Hash.from_xml(xml)
# => {"hash"=>{"foo"=>1, "bar"=>2}}
Hash - Ruby on Rails
Try using Nokogiri gem. It's super easy to use, the website has some nice examples.
require 'open-uri'
doc = Nokogiri::HTML(open("myAPIurl.com&show=storeID,name"))
you can then query your document using xpath or css selectors. It then returns a NodeSet that you can iterate over like you would do with an array
Hope it helps
so i'm trying to send xml back from my controller..
render xml: ['hello world']
correctly gives me:
<?xml version="1.0" encoding="UTF-8"?>
<strings type="array">
<string>hello world</string>
</strings>
however
render xml: 'hello world'
gives xml headers but the body is just:
hello world
which is not xml format.
bug?
From the API documentation:
When a request comes in, for example for an XML response, three steps
happen:
1) the responder searches for a template at people/index.xml;
2) if the template is not available, it will invoke
#to_xml on the given resource;
3) if the responder does not respond_to :to_xml, call
#to_format on it.
See: http://api.rubyonrails.org/classes/ActionController/Responder.html
In Rails, Arrays respond to to_xml but Strings do not.
I seem to be getting this error message:
(a:ActionNotSupported) The message with Action 'GetServices' cannot be
processed at the receiver, due to a ContractFilter mismatch at the
EndpointDispatcher. This may be because of either a contract mismatch
(mismatched Actions between sender and receiver) or a binding/security
mismatch between the sender and the receiver. Check that sender and
receiver have the same contract and the same binding (including
security requirements, e.g. Message, Transport, None).
I assume it is something to do with the security/binding setup.
My connection uses HTTP, with basichttpbinding. I've done a lot of searching for the answer, as I always do, but am unable to fix it, and no one here has expertise on Ruby on Rails.
Help would be appreciated.
Below is my code, in Ruby on Rails, which initialises the service and then calls it. Note: I can connect to it fine. It has successfully reported the available methods. Just calling the methods seems to be the problem. I have successfully connected to online test services using the same code. And I use Savon.
def test
puts "web_service: IN"
client = Savon::Client.new do
wsdl.document = "http://hidden.co.uk/myService.svc?wsdl"
end
#response = client.request "GetServices", :xmlns => "http://tempuri.org/" do
soap.header = {}
soap.body = {
"CostCentreNo" => 1,
"filter" => 0
}
end
puts '##########################'
puts #response.to_hash;
end
Below is what my Ruby on Rails sends:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:wsdl="http://tempuri.org/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<GetServices xmlns="http://tempuri.org/">
<CostCentreNo>1</CostCentreNo>
<filter>0</filter>
</GetServices>
</env:Body>
</env:Envelope>
This is what WCF test client sends, (which works)
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IIBCSServices/GetServices</Action>
</s:Header>
<s:Body>
<GetServices xmlns="http://tempuri.org/">
<CostCentreNo>0</CostCentreNo>
<filter>0</filter>
</GetServices>
</s:Body>
</s:Envelope>
It seems to be the way it was being called... Something so simple.
The override Stated on the SAVON Tutorial, recommended if you have an uppercase starting camelcase doesnt work. Maybe the tutorial is outdated. (Note, :wsdl IS required in my case)
So this was not working:
response = client.request :wsdl, "GetCustomerCentreDetails"
Changing it to:
response = client.request :wsdl, :get_customer_centre_details
Then obviously I need a body added to it, and header etc.
The assumption that caused me confusion : Being able to get the WSDL does not mean you are connected to the webservice.
it seems you're missing this part
<Action s:mustUnderstand="1" ...>
you should insert something like the following into your request
soap.header = {"Action" =>
{'env:mustUnderstand' =>
'http://tempuri.org/IIBCSServices/GetServices',
attributes! => { 'mustUnderstand' => "1", 'xmlns' => "..." }
}