Best practices
Document creation
Use builder pattern for new documents
# Preferred - clean and maintainable
doc = Moxml::Builder.build(Moxml.new) do |xml|
xml.declaration version: "1.0", encoding: "UTF-8"
xml.library do
xml.book "Content"
end
end
# Avoid - verbose and error-prone
doc = Moxml.new.create_document
doc.add_child(doc.create_declaration)
root = doc.create_element('library')
# ... many more linesXPath queries
Use specific paths
# More efficient - specific path
doc.xpath('/library/section/book')
# Less efficient - requires full document scan
doc.xpath('//book')
# Most efficient - from known parent
section.xpath('./book')Adapter selection
Error handling
Always use strict parsing in production
# Production
def parse_production(xml)
Moxml.new.parse(xml, strict: true)
rescue Moxml::ParseError => e
logger.error("Invalid XML received: #{e.message}")
raise
end
# Development/testing only
def parse_development(xml)
Moxml.new.parse(xml, strict: false)
endCatch specific errors
# Good - targeted error handling
begin
doc = Moxml.new.parse(xml)
process(doc)
rescue Moxml::ParseError => e
handle_parse_error(e)
rescue Moxml::XPathError => e
handle_xpath_error(e)
end
# Avoid - too broad
begin
doc = Moxml.new.parse(xml)
process(doc)
rescue StandardError => e
# Catches too much
endNamespace handling
Define namespace mappings clearly
# Good - clear and reusable
NAMESPACES = {
'dc' => 'http://purl.org/dc/elements/1.1/',
'xhtml' => 'http://www.w3.org/1999/xhtml'
}.freeze
titles = doc.xpath('//dc:title', NAMESPACES)
# Avoid - inline everywhere
doc.xpath('//dc:title', { 'dc' => 'http://purl.org/dc/elements/1.1/' })
doc.xpath('//dc:creator', { 'dc' => 'http://purl.org/dc/elements/1.1/' })Check adapter namespace support
def query_with_namespace(doc, expression, namespaces)
adapter = doc.context.config.adapter.name
if adapter.include?('Rexml') || adapter.include?('Ox')
# Fallback for limited namespace support
query_without_namespace(doc, expression)
else
doc.xpath(expression, namespaces)
end
endMemory management
Release document references
# Process large documents
def process_large_xml(xml)
doc = Moxml.new.parse(xml)
result = extract_data(doc)
doc = nil # Allow GC
result
end
# Batch processing
xml_files.each do |file|
doc = Moxml.new.parse(File.read(file))
process(doc)
doc = nil # Release before next iteration
GC.start if large_file?(file)
endThread safety
Use separate contexts per thread
# Good - thread-safe
class XmlProcessor
def process(xml)
context = Moxml.new # New context per call
doc = context.parse(xml)
# Process...
end
end
# Avoid - shared state
class XmlProcessor
def initialize
@context = Moxml.new
end
def process(xml)
# Multiple threads share @context
@context.parse(xml) # Not thread-safe
end
endPerformance optimization
Code organization
Extract XML operations into methods
class BookProcessor
def initialize
@context = Moxml.new
@context.config.adapter = :nokogiri
end
def parse_catalog(xml)
@context.parse(xml)
end
def find_book(doc, id)
doc.at_xpath("//book[@id='#{id}']")
end
def update_price(book, new_price)
price_elem = book.at_xpath('.//price')
price_elem.text = new_price.to_s
end
endUse value objects for XML data
class Book
attr_accessor :id, :title, :author, :price
def self.from_xml(element)
new.tap do |book|
book.id = element['id']
book.title = element.at_xpath('.//title')&.text
book.author = element.at_xpath('.//author')&.text
book.price = element.at_xpath('.//price')&.text&.to_f
end
end
def to_xml(doc)
doc.create_element('book').tap do |elem|
elem['id'] = id
elem.add_child(doc.create_element('title').tap { |e| e.text = title })
elem.add_child(doc.create_element('author').tap { |e| e.text = author })
elem.add_child(doc.create_element('price').tap { |e| e.text = price.to_s })
end
end
endTesting
Test with multiple adapters
RSpec.describe "XML Processing" do
[:nokogiri, :libxml, :oga].each do |adapter_name|
context "with #{adapter_name}" do
let(:context) do
Moxml.new.tap { |c| c.config.adapter = adapter_name }
end
it "processes correctly" do
doc = context.parse(xml)
# Test operations
end
end
end
endSee also
-
Configuration - Setup and options
-
Error handling - Error management