Lightweight XML builder DSL without Nokogiri dependency
gem install philiprehberger-xml_builderLightweight XML builder DSL without Nokogiri dependency
Add to your Gemfile:
gem "philiprehberger-xml_builder"
Or install directly:
gem install philiprehberger-xml_builder
require "philiprehberger/xml_builder"
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.tag(:root) do
doc.tag(:item, id: "1") { doc.text("Hello") }
end
end
puts xml
# <?xml version="1.0" encoding="UTF-8"?><root><item id="1">Hello</item></root>
Use method names directly as tag names for a cleaner syntax:
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.person(name: "John") do
doc.age("30")
doc.email("john@example.com")
end
end
# <person name="John"><age>30</age><email>john@example.com</email></person>
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.tag(:root) do
doc.comment("Generated XML")
doc.tag(:script) { doc.cdata('var x = 1 < 2;') }
end
end
Pass keyword attributes to emit a structured PI with XML-escaped values:
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.pi("xml-stylesheet", href: "style.xsl", type: "text/xsl")
doc.tag(:root) { doc.text("content") }
end
puts xml
# <?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="style.xsl" type="text/xsl"?><root>content</root>
A legacy positional content string is also supported:
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.processing_instruction("xml-stylesheet", 'type="text/xsl" href="style.xsl"')
doc.tag(:root) { doc.text("content") }
end
doc = Philiprehberger::XmlBuilder::Document.new
doc.tag(:root) do
doc.tag(:child) { doc.text("value") }
end
puts doc.to_xml(indent: 2)
# <?xml version="1.0" encoding="UTF-8"?>
# <root>
# <child>value</child>
# </root>
Or use #pretty for the same output with sane defaults:
puts doc.pretty
# Equivalent to doc.to_xml(indent: 2)
puts doc.pretty(indent: 4)
# Equivalent to doc.to_xml(indent: 4)
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.tag(:root) { doc.raw("<pre>formatted</pre>") }
end
Omit the <?xml ... ?> declaration when building fragments:
xml = Philiprehberger::XmlBuilder.build(declaration: false) do |doc|
doc.tag(:item, id: "1") { doc.text("fragment") }
end
puts xml
# <item id="1">fragment</item>
Register namespace prefixes and create namespace-aware elements:
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.namespace(:soap, "http://schemas.xmlsoap.org/soap/envelope/")
doc.namespace(:wsse, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")
doc.namespace_tag(:soap, :Envelope) do
doc.namespace_tag(:soap, :Header) do
doc.namespace_tag(:wsse, :Security)
end
doc.namespace_tag(:soap, :Body)
end
end
You can also use string tag names directly:
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.tag("soap:Envelope", "xmlns:soap" => "http://schemas.xmlsoap.org/soap/envelope/") do
doc.tag("soap:Body")
end
end
Build SOAP 1.1 or 1.2 envelopes with a convenience DSL:
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.soap_envelope(version: "1.1") do |header, body|
header << ->(d) { d.tag("auth") { d.text("token123") } }
body << ->(d) { d.tag("GetPrice") { d.text("Widget") } }
end
end
Or use the top-level shortcut:
xml = Philiprehberger::XmlBuilder.build_soap(soap_version: "1.2") do |header, body|
body << ->(d) { d.tag("GetStockPrice") { d.tag("Symbol") { d.text("AAPL") } } }
end
Combine separately built document fragments:
# Build fragments independently
header = Philiprehberger::XmlBuilder::Document.new
header.tag(:title) { header.text("My Document") }
body = Philiprehberger::XmlBuilder::Document.new
body.tag(:paragraph) { body.text("Hello world") }
# Compose into a single document
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.tag(:document) do
doc.tag(:header) { doc.append(header) }
doc.tag(:body) { doc.append(body) }
end
end
Insert raw XML fragment strings:
xml = Philiprehberger::XmlBuilder.build do |doc|
doc.tag(:root) do
doc.insert_fragment('<existing>data</existing>')
end
end
Philiprehberger::XmlBuilder| Method | Description |
|---|---|
.build(encoding: "UTF-8", version: "1.0", declaration: true) { |doc| ... } | Build an XML document and return the string |
.build_soap(soap_version: "1.1", encoding: "UTF-8", version: "1.0", declaration: true) { |header, body| ... } | Build a SOAP envelope document |
Document| Method | Description |
|---|---|
#tag(name, attributes = {}) { ... } | Add an element with optional attributes and children |
#text(content) | Add escaped text content |
#cdata(content) | Add a CDATA section |
#comment(text) | Add an XML comment |
#processing_instruction(target, content) | Add a processing instruction |
#processing_instruction(target, **attrs) | Append an XML processing instruction (alias: #pi) |
#raw(string) | Add raw unescaped XML |
#namespace(prefix, uri) | Register an XML namespace prefix and URI |
#namespace_tag(prefix, name, attributes = {}) { ... } | Add a namespace-prefixed element with auto xmlns |
#soap_envelope(version: "1.1") { |header, body| ... } | Build a SOAP envelope with Header and Body |
#append(other_document) | Append children from another Document |
#insert_fragment(xml_string) | Insert a raw XML fragment string |
#to_s | Render compact XML string |
#to_xml(indent: nil) | Render XML with optional indentation |
#pretty(indent: 2) | Renders with default 2-space indentation |
Escaper| Method | Description |
|---|---|
.escape(text) | Escape XML entities (&, <, >, ", ') |
bundle install
bundle exec rspec
bundle exec rubocop
If you find this project useful: