using XML::Builder to build a doc
node
#3065
-
Please describe the bug I am working with xsd and I need to build a xml with the node "doc". Using the nokogiri builder I was expecting to use def method_missing(method, *args, &block) # :nodoc:
if @context&.respond_to?(method)
@context.send(method, *args, &block)
else
add_node(method.to_s.sub(/[_!]$/, ""), *args, &block)
end
end
def add_node(name, *args, &block)
node = @doc.create_element(name, *args) do |n|
# Set up the namespace
if @ns.is_a?(Nokogiri::XML::Namespace)
n.namespace = @ns
@ns = nil
end
end
if @ns.is_a?(Hash)
node.namespace = node.namespace_definitions.find { |x| x.prefix == @ns[:pending] }
if node.namespace.nil?
raise ArgumentError, "Namespace #{@ns[:pending]} has not been defined"
end
@ns = nil
end
insert(node, &block)
end If this is a patch you want included I would very much like to open the PR and contribute to nokogiri. Help us reproduce what you're seeing I did't write it as a test but I have a script to demo it. # frozen_string_literal: true
require "nokogiri"
XSD = <<~XSD
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="theDoc" type="Amount"/>
<xsd:complexType name="Amount">
<xsd:sequence>
<xsd:element name="doc" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
XSD
class Scribe
def self.write_xml(root)
new.write_xml(root)
end
def write_xml(root)
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
build_xsd__root(xml, root)
end
builder.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::DEFAULT_XML)
end
private
attr_reader(:xml)
def build_xsd__root(xml, root)
# avoid passing the xml builder instance
@xml = xml
xml.theDoc() {
build_amount(root[:theDoc])
}
@xml = nil
end
def build_xsd_string(str)
xml.text(str)
end
def build_amount(value)
node = xml.doc.create_element("doc")
xml.add_node("doc") do
build_xsd_string(value[:doc] + "new_code")
end
xml.doc() {
build_xsd_string(value[:doc] + "old_code")
}
xml.add_node("doco") do
build_xsd_string(value[:doc] + "new_code")
end
xml.doco() {
build_xsd_string(value[:doc] + "old_code")
}
end
end
xml = <<~XML
<?xml version="1.0" encoding="UTF-8"?>
<theDoc>
<doc>info</doc>
</theDoc>
XML
doc = Nokogiri::XML(xml)
xsd = Nokogiri::XML::Schema(XSD)
pp({doc_errors: doc.errors, xsd_errors: xsd.validate(doc)})
root = {theDoc: {doc: "info"}}
scribed = Scribe.write_xml(root)
puts "====== scribed"
puts scribed
puts "====== xml"
puts xml
# > output
# {:doc_errors=>[], :xsd_errors=>[]}
# ====== scribed
# <?xml version="1.0" encoding="UTF-8"?>
# <theDoc>
# <doc>infonew_code</doc>
# <doco>infonew_code</doco>
# <doco>infoold_code</doco>
# </theDoc>
# ====== xml
# <?xml version="1.0" encoding="UTF-8"?>
# <theDoc>
# <doc>info</doc>
# </theDoc>
# [Finished in 1.4s] If you haven't included a test, please provide whatever code you can, the inputs (if any), along with the output that you're seeing. We need to reproduce what you're seeing to be able to help! Expected behavior Environment |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
Hi! Thanks for asking this question. Because it falls under user help, I've converted it to a discussion (hope that's OK). The builder allows you to create nodes for names bound to pre-existing methods by appending an underscore ( Here's an example of what happens when you use #!/usr/bin/env ruby
require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "nokogiri"
end
builder = Nokogiri::XML::Builder.new do |xml|
xml.root {
xml.doc {
xml.thing "content"
}
}
end
builder.to_xml
# => "<?xml version=\"1.0\"?>\n" + "<root/>\n"
builder = Nokogiri::XML::Builder.new do |xml|
xml.root {
xml.doc_ {
xml.thing "content"
}
}
end
builder.to_xml
# => "<?xml version=\"1.0\"?>\n" +
# "<root>\n" +
# " <doc>\n" +
# " <thing>content</thing>\n" +
# " </doc>\n" +
# "</root>\n" I hope this is helpful? |
Beta Was this translation helpful? Give feedback.
Hi! Thanks for asking this question. Because it falls under user help, I've converted it to a discussion (hope that's OK).
The builder allows you to create nodes for names bound to pre-existing methods by appending an underscore (
_
). Docs explaining this are here.Here's an example of what happens when you use
doc
versusdoc_
: