Error handling
Purpose
Comprehensive guide to handling errors in Moxml including error class hierarchy, error context, and best practices for debugging.
Error class hierarchy
All Moxml errors inherit from Moxml::Error:
Moxml::Error (< StandardError)
├── ParseError # XML parsing failures
├── XPathError # XPath expression errors
├── ValidationError # XML validation failures
├── NamespaceError # Namespace-related errors
├── AdapterError # Adapter loading/operation errors
├── SerializationError # XML serialization failures
├── DocumentStructureError # Invalid document structure
├── AttributeError # Attribute operation errors
└── NotImplementedError # Unimplemented adapter featuresParseError
Raised when XML parsing fails.
begin
doc = Moxml.new.parse('<invalid><unclosed>', strict: true)
rescue Moxml::ParseError => e
puts "Parse error at line #{e.line}, column #{e.column}"
puts "Source excerpt: #{e.source}"
puts e.to_s # Includes helpful hint
# => "Hint: Check XML syntax and ensure all tags are properly closed"
endAttributes:
-
line- Line number where error occurred -
column- Column number where error occurred -
source- Excerpt of problematic XML
XPathError
Raised when XPath expression evaluation fails.
begin
results = doc.xpath('//invalid[[[')
rescue Moxml::XPathError => e
puts "Expression: #{e.expression}"
puts "Adapter: #{e.adapter}"
puts "Node: #{e.node.name}" if e.node
puts e.to_s
# => "Hint: Verify XPath syntax and ensure adapter supports the expression"
endAttributes:
-
expression- The XPath expression that failed -
adapter- Adapter name -
node- Context node (if available)
ValidationError
Raised when XML content violates XML specifications.
begin
doc.version = '2.0' # Invalid XML version
rescue Moxml::ValidationError => e
puts "Constraint: #{e.constraint}" # => "version"
puts "Value: #{e.value}" # => "2.0"
puts "Allowed: #{e.allowed_values}" # => ["1.0", "1.1"]
puts e.to_s
endAttributes:
-
constraint- What was being validated -
value- The invalid value -
allowed_values- Valid options
NamespaceError
Raised when namespace operations fail.
begin
element.add_namespace('invalid:prefix', 'http://example.org')
rescue Moxml::NamespaceError => e
puts "Prefix: #{e.prefix}"
puts "URI: #{e.uri}"
puts "Element: #{e.element.name}"
puts e.to_s
# => "Hint: Check namespace registration and URI validity"
endAttributes:
-
prefix- Namespace prefix -
uri- Namespace URI -
element- Element where error occurred
AdapterError
Raised when adapter loading or operations fail.
begin
context = Moxml.new
context.config.adapter = :nonexistent
rescue Moxml::AdapterError => e
puts "Adapter: #{e.adapter_name}" # => :nonexistent
puts "Operation: #{e.operation}" # => "set_adapter"
puts "Native Error: #{e.native_error}" # Original error
puts e.to_s
# => "Hint: Ensure the adapter gem is properly installed"
endAttributes:
-
adapter_name- Name of adapter -
operation- Operation that failed -
native_error- Underlying error from adapter library
Best practices
Catch specific errors
begin
doc = Moxml.new.parse(xml_string, strict: true)
results = doc.xpath('//book[@id="1"]')
rescue Moxml::ParseError => e
# Handle parsing errors
logger.error("XML parsing failed: #{e.to_s}")
rescue Moxml::XPathError => e
# Handle XPath errors
logger.error("XPath query failed: #{e.expression}")
rescue Moxml::NamespaceError => e
# Handle namespace errors
logger.error("Namespace error: #{e.prefix}:#{e.uri}")
rescue Moxml::Error => e
# Catch-all for other Moxml errors
logger.error("XML processing error: #{e.message}")
endUse error context
begin
doc = Moxml.new.parse(xml)
rescue Moxml::ParseError => e
# Log comprehensive error information
logger.error <<~ERROR
XML Parse Error:
Line: #{e.line}
Column: #{e.column}
Source: #{e.source}
Message: #{e.message}
Hint: #{e.to_s}
ERROR
# Re-raise or handle
raise unless development_mode?
endValidate before operations
# Validate adapter available
def ensure_adapter_available(adapter_name)
context = Moxml.new
context.config.adapter = adapter_name
rescue Moxml::AdapterError => e
raise "Required adapter #{adapter_name} not available: #{e.message}"
end
# Validate document structure
def validate_structure(doc)
required = ['title', 'author']
required.each do |elem|
unless doc.at_xpath("//#{elem}")
raise Moxml::ValidationError, "Missing required element: #{elem}"
end
end
endError recovery strategies
Graceful degradation
def parse_with_fallback(xml)
# Try strict parsing first
begin
return Moxml.new.parse(xml, strict: true)
rescue Moxml::ParseError
logger.warn("Strict parsing failed, trying relaxed mode")
end
# Fall back to relaxed parsing
begin
return Moxml.new.parse(xml, strict: false)
rescue Moxml::ParseError => e
logger.error("Cannot parse XML: #{e.message}")
raise
end
endAdapter fallback
def parse_with_adapter_fallback(xml)
adapters = [:nokogiri, :libxml, :oga, :rexml]
adapters.each do |adapter_name|
begin
context = Moxml.new
context.config.adapter = adapter_name
return context.parse(xml)
rescue Moxml::AdapterError
# Adapter not available, try next
next
rescue Moxml::ParseError => e
# Parsing failed with this adapter
logger.warn("#{adapter_name} failed: #{e.message}")
next
end
end
raise "Could not parse XML with any available adapter"
endCommon error scenarios
Malformed XML
xml = '<root><unclosed>'
begin
doc = Moxml.new.parse(xml, strict: true)
rescue Moxml::ParseError => e
puts "Malformed XML detected"
puts "Error at line #{e.line}" if e.line
# Handle or fix the XML
endInvalid XPath
begin
doc.xpath('//book[[[invalid]]]')
rescue Moxml::XPathError => e
puts "Invalid XPath expression: #{e.expression}"
# Use corrected expression
doc.xpath('//book[@id]')
endUnsupported features
# Ox doesn't support complex XPath
context = Moxml.new
context.config.adapter = :ox
begin
doc.xpath('//book[price < 30]')
rescue Moxml::NotImplementedError => e
puts "Feature not supported by #{e.adapter}: #{e.feature}"
# Use workaround
books = doc.xpath('//book')
cheap = books.select { |b| b.at_xpath('.//price').text.to_f < 30 }
endDebugging techniques
See also
-
Error classes reference - Complete API
-
Error handling guide - Advanced patterns
-
Adapters - Adapter-specific error behaviors