I am trying the following, but the output XML is badly formed:
xml = Builder::XmlMarkup.new({:target => display })
xml.instruct!
xml.mail {
xml.documents {
xml.document {
xml.template {
xml.source "gallery"
xml.name "Postcard: Image fill front"
}
xml.sections {
xml.section {
xml.name "Text"
xml.text "Hello, world"
}
xml.section {
xml.name "Image"
xml.attachment "1136946686-3425"
}
} #sections
} #document
} #documents
xml.addresses {
xml.addressee {
xml.name "Me"
xml.address "Street"
xml.city "San Francisco"
xml.state "CA"
}
}
}
#xml_display = xml
I need it to look more like this:
<?xml version="1.0" encoding="UTF-8"?>
<mail>
<documents>
<document>
<template>
<source>gallery</source>
<name>Postcard: Image fill front</name>
</template>
<sections>
<section>
<name>Text</name>
<text>Hello, World!</text>
</section>
<section>
<name>Image</name>
<attachment>...attachment id...</attachment>
</section>
</sections>
</document>
</documents>
<addressees>
<addressee>
<name>John Doe</name>
<address>123 Main St</address>
<city>Anytown</city>
<state>AZ</state>
<postal-code>10000</postal-code>
</addressee>
</addressees>
</mail>
My code ends up looking like this in the view (page source):
<?xml version="1.0" encoding="UTF-8"?><mail/><documents/><document/><template/><source>gallery</source><name>Postcard: Image fill front</name>
NOTE: The final XML needs to be POSTED to a URL, and I am using rest-client and so want everything to become an instance variable that is passed as the body.
The instance variable is just #xml_display = xml but it's not working... :(
You can pass a block to each of those to nest the items.
xml.mail do |mail|
mail.documents do |documents|
documents.document do |document|
document.template do |template|
template.source "gallery"
template.name "Postcard: Image fill front"
end
end
end
end
There's more information on the builder rubyforge page.
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 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
}
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") }
Using markup with the render is not adding the form tag.
I tried this with contentType "text/html", "txt/xml" and it does not work.
I have this in my controller:
def myTest= {
render(contentType: "text/plain") {
div(id:"myDiv") {
p "somess text inside the div"
form (action:'get') {
p "inside form"
}
}
}
And I'm getting this:
<div id='myDiv'><p>somess text inside the div</p><p>inside form</p></div>
I want this:
<div id='myDiv'><p>somess text inside the div</p><form><p>inside form</p></form></div>
Does anybody know why is not adding the form and how to add it?
Thanks,
Federico
i found this problem earlier and the work around was to use the builder directly
def test = {
def sw = new StringWriter()
def b = new MarkupBuilder(sw)
b.html(contentType: "text/html") {
div(id: "myDiv") {
p "somess text inside the div"
b.form(action: 'get') {
p "inside form"
}
}
}
render sw
}
will render the following HTML
<html contentType='text/html'>
<div id='myDiv'>
<p>somess text inside the div</p>
<form action='get'>
<p>inside form</p>
</form>
</div>
</html>