I need to read an XML file and generate a SOAP request body using Nokogiri in Ruby on Rails.
The request body that I need to generate is:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:c2="http://c2_0.customer.webservices.csx.dtv.com/">
<soapenv:Header>
<soapenv:Body>
<c2:getCustomer>
<customerId>10</customerId>
<UserId>adminUser</UserId>
</c2:getCustomer>
</soapenv:Body>
</soapenv:Header>
</soapenv:Envelope>
I am using this code:
require 'nokogiri'
doc = Nokogiri::XML(File.open('p_l_s.xml'))
wsID =doc.xpath('//transaction:WsID' , 'transaction' => 'http://www.nrf-arts.org/IXRetail/namespace/').inner_text
builder = Nokogiri::XML::Builder.new do |xml|
xml.Envelope("xmlns:soapenv" => "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:c2" => "http://c2_0.customer.webservices.csx.dtv.com/") do
xml.parent.namespace = xml.parent.namespace_definitions.first
xml['soapenv'].Header {
xml.Body {
xml['c2'].getCustomer{
#xml.remove_namespaces!
xml.customerId wsID
xml.UserId "adminUser"
}
}
}
end
end
puts builder.to_xml
And, when executing it from the terminal in Ubuntu, I get:
<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:c2="http://c2_0.customer.webservices.csx.dtv.com/">
<soapenv:Header>
<soapenv:Body>
<c2:getCustomer>
<c2:customerId>10</c2:customerId>
<c2:UserId>adminUser</c2:UserId>
</c2:getCustomer>
</soapenv:Body>
</soapenv:Header>
</soapenv:Envelope>
I get the c2 namespace for the XML elements customerId and UserId which is not required for the method I'm invoking in the WSDL file I will be calling.
I was able to generate the output you needed with the following code:
builder = Nokogiri::XML::Builder.new do |xml|
xml.Envelope("xmlns:soapenv" => "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:c2" => "http://c2_0.customer.webservices.csx.dtv.com/") do
xml.parent.namespace = xml.parent.namespace_definitions.first
xml.Header {
xml.Body {
xml.getCustomer {
xml.customerId {
xml.parent.content=(wsID)
xml.parent.namespace = xml.parent.namespace_definitions.first
}
xml.UserId{
xml.parent.content=("adminUser")
xml.parent.namespace = xml.parent.namespace_definitions.first
}
xml.parent.namespace = xml.parent.namespace_scopes[1]
}
}
}
end
end
puts builder.to_xml
You don't have to explicitly set namespaces until you get to 'getCustomer'. With a combination of the 'content' and 'namespace_definition' methods, you can then specify the namespace and content of the child nodes - which should output what you were looking for.
The Nogokiri pages for the Builder: http://nokogiri.org/Nokogiri/HTML/Builder.html and Node: http://nokogiri.org/Nokogiri/XML/Node.html are super useful and will hopefully help shed more light on this for you.
I think this code in Mel T's answer:
xml.customerId {
xml.parent.content=(wsID)
xml.parent.namespace = xml.parent.namespace_definitions.first
}
Would output:
<soapenv:customerId>10</soapenv:customerId>
I did this instead:
xml.customerId {
xml.parent.content=(wsID)
xml.parent.namespace = nil
}
Related
Using gem Nokogiri I'm trying to generate XML like:
<?xml version='1.0'?>
<env:Envelope xmln:env = "http://abc.ca">
<env:Header>
<mm7:TransactionID xmlns:mm7="http://def.ca"> Some Text Here </mm7:TransactionID>
</env:Header>
</env:Envelope>
The code I have is:
env_ns = {
"xmlns:env" => "http://abc.ca"
}
mm7_ns = {
"xmlns:mm7" => "http://def.ca"
}
env_header = Nokogiri::XML::Builder.new do |xml|
xml['mm7'].TransactionID(mm7_ns) do
"Some Text Here"
end
end
builder = Nokogiri::XML::Builder.new { |xml|
xml['env'].Envelope(env_ns) do
xml.Header do
env_header
end
end
}
puts env_header.to_xml
puts "----------------------"
puts builder.to_xml
However, the output is not as desired because the value "Some Text Here" did not go inside the mm7:TranactionID tag. The mm7 tag did not go inside the header tag. Also, the header tag did not go inside the envelope tag.
<?xml version="1.0"?>
<mm7:TransactionID xmlns:mm7="http://def.ca"/>
-----------------------------------------------------------
<?xml version="1.0"?>
<env:Envelope xmlns:env="http://abc.ca">
<env:Header/>
</env:Envelope>
Thanks.
You only need 1 builder:
env_ns = {
"xmlns:env" => "http://abc.ca"
}
mm7_ns = {
"xmlns:mm7" => "http://def.ca"
}
builder = Nokogiri::XML::Builder.new do |xml|
xml['env'].Envelope(env_ns) do
xml.Header do
xml['mm7'].TransactionID(mm7_ns, "Some Text Here")
end
end
end
puts builder.to_xml
# will render the following:
# <?xml version="1.0"?>
# <env:Envelope xmlns:env="http://abc.ca">
# <env:Header>
# <mm7:TransactionID xmlns:mm7="http://def.ca">Some Text Here</mm7:TransactionID>
# </env:Header>
# </env:Envelope>
I'm using Savon gem in my Rails app and I need to submit my form fields to Equifax.
Here's the Equifax XML:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v3="http://eid.equifax.com/soap/schema/canada/v3">
<soapenv:Header/>
<soapenv:Body>
<v3:InitialRequest>
<v3:Identity>
<v3:Name>
<v3:FirstName>Michael</v3:FirstName>
<v3:LastName>Smith</v3:LastName>
</v3:Name>
<!--1 to 3 repetitions:-->
<v3:Address timeAtAddress="72" addressType="current">
<v3:HybridAddress>
<!--1 to 6 repetitions:-->
<v3:AddressLine>1028 Summerville</v3:AddressLine>
<v3:City>Vancouver</v3:City>
<v3:Province>BC</v3:Province>
<v3:PostalCode>V7B0A8</v3:PostalCode>
</v3:HybridAddress>
</v3:Address>
<!--Optional:-->
<v3:DateOfBirth>
<v3:Day>26</v3:Day>
<v3:Month>08</v3:Month>
<v3:Year>1984</v3:Year>
</v3:DateOfBirth>
<v3:PhoneNumber phoneType="current">
<v3:PhoneNumber>6045556666</v3:PhoneNumber>
</v3:PhoneNumber>
</v3:Identity>
<v3:ProcessingOptions>
<v3:Language>English</v3:Language>
</v3:ProcessingOptions>
</v3:InitialRequest>
</soapenv:Body>
</soapenv:Envelope>
This is my Ruby code making the SOAP Request.
<%= client.call(:start_transaction, message: { "v3:InitialRequest" => { "v3:Identity" => { "v3:Name" => { "v3:FirstName" => "michael", "v3:LastName" => "Smith"}, "v3:Address" => {"v3:AddressLine" => "233 Cambie St", "v3:City" => "Vancouve", "v3:Province" => "BC", "v3:PostalCode" => "V6B0E8"}, "v3:DateOfBirth" => {"v3:Day" => "26", "v3:Month" => "08", "v3:Year" => "1984"}, "v3:PhoneNumber" => {"v3:PhoneNumber" => "6048885555"}}}, attributes: { "v3:Address" => { "timeAtAddress" => "72", "addressType" => "current"}}) %>
The error message that is returned: (soap:Client) Error reading XMLStreamReader.
Can someone point me in the right direction?
Please try the below way by sending one string of that xml
xml_str = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:v3="http://eid.equifax.com/soap/schema/canada/v3"><soapenv:Header/><soapenv:Body><v3:InitialRequest><v3:Identity><v3:Name><v3:FirstName>Michael</v3:FirstName><v3:LastName>Smith</v3:LastName></v3:Name><!--1 to 3 repetitions:--><v3:Address timeAtAddress="72" addressType="current"><v3:HybridAddress><v3:AddressLine>1028 Summerville</v3:AddressLine><v3:City>Vancouver</v3:City><v3:Province>BC</v3:Province><v3:PostalCode>V7B0A8</v3:PostalCode></v3:HybridAddress></v3:Address><v3:DateOfBirth><v3:Day>26</v3:Day><v3:Month>08</v3:Month><v3:Year>1984</v3:Year></v3:DateOfBirth><v3:PhoneNumber phoneType="current"><v3:PhoneNumber>6045556666</v3:PhoneNumber></v3:PhoneNumber></v3:Identity><v3:ProcessingOptions><v3:Language>English</v3:Language></v3:ProcessingOptions></v3:InitialRequest></soapenv:Body></soapenv:Envelope>'
client.call(:start_transaction, xml: xml_str)
Remember you can use here document to made the xml string but the string should not consis "\n
my require header is
<SOAP-ENV:Header xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:Security SOAP-ENV:mustUnderstand="1">
<wsse:UsernameToken>
<wsse:Username>123</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">1234</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
this is my message :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:dictionary:com.test.webservices">
<soapenv:Body>
<urn:getPIN/>
</soapenv:Body>
</soapenv:Envelope>
my code
soap_header = {
"wsse:Security" => {
"#soapenv:mustUnderstand" => 1,
"wsse:UsernameToken" =>{
"Username" => "123",
"Password" => "1234",
"digest" => false
}
}
}
client = Savon.client(wsdl: #wsdl, log: true,:ssl_verify_mode => :none,:soap_header=> soap_header)
response = client.call :getPIN, xml: #message
how to pass header in Savon 2.7 we not able to access services please help us.
Try this please (found on Savon 2 documentation):
Savon.client(wsse_auth: ["123", "1234"])
The soap endpoint I was contacting was very picky and I had this same problem. (wsse_auth did not work)
I solved this by modifying your answer slightly.
soap_header = {
"wsse:Security" => {
"#soapenv:mustUnderstand" => 1,
"#xmlns:soap" => "http://schemas.xmlsoap.org/wsdl/soap/",
"#xmlns:wsse" => "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
"wsse:UsernameToken" =>{
"wsse:Username" => "123",
"wsse:Password" => "12345",
}
}
}
The problem was the missing soapenv:mustUnderstand="1"
I have an element in my XML but I am not sure how to get it generated in Nokogiri::XML::Builder.
<ns0:SearchCondition expressionLanguage='String'expressionType='PartyNumber'>31955854</ns0:SearchCondition>
I tried this:
def test_xml
builder = Nokogiri::XML::Builder.new do |xml|
xml.root {
xml.products {
xml.widget {
xml.id_ "10"
xml.name "Awesome widget"
xml.SearchCondition('expressionLanguage' => 'String', 'expressionType' => 'PartyNumber')
}
}
}
end
puts builder.to_xml
This produces the following
<?xml version="1.0"?>
<root>
<products>
<widget>
<id>10</id>
<name>Awesome widget</name>
<SearchCondition expressionLanguage="String" expressionType="PartyNumber"/>
</widget>
</products>
</root>
But I am not sure how I pass the value to the PartyNumber.
Not sure if this is what you are asking, but you can use the builder text method to create a text element inside another:
xml.SearchCondition('expressionLanguage' => 'String', 'expressionType' => 'PartyNumber') {
xml.text "31955854"
}
You're not using the namespace in your example, but you don't mention that, so I guess that is not an issue.
I need to generate an xml for a feed which looks roughly like this :-
<?xml version="1.0" encoding="iso-8859-1" ?>
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<item>
<g:id><![CDATA[id]]></g:id>
<title><![CDATA[Product Name]]></title>
<description><![CDATA[This should be a relatively detailed description with as little formatting as possible.]]></description>
<g:brand>Brand X</g:brand>
<g:sale_id>new</g:sale_id>
</item>
<item>
Next product...
</item>
</channel>
</rss>
My code currently looks something like this :-
xml=Builder::XmlMarkup.new(:indent => 3)
xml.instruct!
xml.rss("version" => "2.0" , "xmlns:g" => "http://base.google.com/ns/1.0" , "xmlns:atom" => "http://www.w3.org/2005/Atom"){
xml.channel{
# remove xml.namespace = xml.namespace_definitions.find{|ns|ns.prefix=="atom"}
sale_products.each do |sp|
sid = (products_info[sp.product_id]["sale_id"]).to_s()
xml.item {
#xml.id{ |xml| xml.cdata!(products_info[sp.product_id].own_id) }
#xml.g :id,{ xml.cdata!("sdaf") }
xml.product_title{ |xml| xml.cdata!(products_info[sp.product_id].name) }
xml.description{ |xml| xml.cdata!(ActionController::Base.helpers.strip_tags(products_info[sp.product_id].description)) }
xml.item {
xml.brand { |xml| xml.cdata!(products_info[sp.product_id].designer_1) }
xml.sale_id{ |xml| xml.cdata!(sid) }
}
}
end
}
}
My problem is around getting both namespace prefixes and cdata tags working at the same time.
xml.g :id, "fdsafsad"
This gets the namesapce prefix.
xml.product_title{ |xml| xml.cdata!(products_info[sp.product_id].name) }
This gets cdata tags around the values.
xml.g :id,{ xml.cdata!("sdaf") }
This fails to do the trick.
How do i get both the namespace prefix as well as the cdata tags working at the same time for the same tag. What am I doing wrong?
Edit:- The output that I am currently getting is like:-
<g:id>
<![CDATA[10005-0003]]>
</g:id>
The output that I want should just have the value inside cdata tags (no newline etc) :-
<g:id><![CDATA[10005-0003]]></g:id>
Note that I do not want to remove the :indent => 3 while creating the markup, so that other tags are formatted as required.
xml.tag!("g:id") { xml.cdata!("sdaf") }