moxml 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/dependent-repos.json +5 -0
- data/.github/workflows/dependent-tests.yml +20 -0
- data/.github/workflows/docs.yml +59 -0
- data/.github/workflows/rake.yml +10 -10
- data/.github/workflows/release.yml +5 -3
- data/.gitignore +37 -0
- data/.rubocop.yml +15 -7
- data/.rubocop_todo.yml +224 -43
- data/Gemfile +14 -9
- data/LICENSE.md +6 -2
- data/README.adoc +535 -373
- data/Rakefile +53 -0
- data/benchmarks/.gitignore +6 -0
- data/benchmarks/generate_report.rb +550 -0
- data/docs/Gemfile +13 -0
- data/docs/_config.yml +138 -0
- data/docs/_guides/advanced-features.adoc +87 -0
- data/docs/_guides/development-testing.adoc +165 -0
- data/docs/_guides/index.adoc +51 -0
- data/docs/_guides/modifying-xml.adoc +292 -0
- data/docs/_guides/parsing-xml.adoc +230 -0
- data/docs/_guides/sax-parsing.adoc +603 -0
- data/docs/_guides/working-with-documents.adoc +118 -0
- data/docs/_guides/xml-declaration.adoc +450 -0
- data/docs/_pages/adapter-compatibility.adoc +369 -0
- data/docs/_pages/adapters/headed-ox.adoc +237 -0
- data/docs/_pages/adapters/index.adoc +97 -0
- data/docs/_pages/adapters/libxml.adoc +285 -0
- data/docs/_pages/adapters/nokogiri.adoc +251 -0
- data/docs/_pages/adapters/oga.adoc +291 -0
- data/docs/_pages/adapters/ox.adoc +56 -0
- data/docs/_pages/adapters/rexml.adoc +292 -0
- data/docs/_pages/best-practices.adoc +429 -0
- data/docs/_pages/compatibility.adoc +467 -0
- data/docs/_pages/configuration.adoc +250 -0
- data/docs/_pages/error-handling.adoc +349 -0
- data/docs/_pages/headed-ox-limitations.adoc +574 -0
- data/docs/_pages/headed-ox.adoc +1025 -0
- data/docs/_pages/index.adoc +35 -0
- data/docs/_pages/installation.adoc +140 -0
- data/docs/_pages/node-api-reference.adoc +49 -0
- data/docs/_pages/performance.adoc +35 -0
- data/docs/_pages/quick-start.adoc +243 -0
- data/docs/_pages/thread-safety.adoc +28 -0
- data/docs/_references/document-api.adoc +407 -0
- data/docs/_references/index.adoc +48 -0
- data/docs/_tutorials/basic-usage.adoc +267 -0
- data/docs/_tutorials/builder-pattern.adoc +342 -0
- data/docs/_tutorials/index.adoc +33 -0
- data/docs/_tutorials/namespace-handling.adoc +324 -0
- data/docs/_tutorials/xpath-queries.adoc +358 -0
- data/docs/index.adoc +122 -0
- data/examples/README.md +124 -0
- data/examples/api_client/README.md +424 -0
- data/examples/api_client/api_client.rb +394 -0
- data/examples/api_client/example_response.xml +48 -0
- data/examples/headed_ox_example/README.md +90 -0
- data/examples/headed_ox_example/headed_ox_demo.rb +71 -0
- data/examples/rss_parser/README.md +194 -0
- data/examples/rss_parser/example_feed.xml +93 -0
- data/examples/rss_parser/rss_parser.rb +189 -0
- data/examples/sax_parsing/README.md +50 -0
- data/examples/sax_parsing/data_extractor.rb +75 -0
- data/examples/sax_parsing/example.xml +21 -0
- data/examples/sax_parsing/large_file.rb +78 -0
- data/examples/sax_parsing/simple_parser.rb +55 -0
- data/examples/web_scraper/README.md +352 -0
- data/examples/web_scraper/example_page.html +201 -0
- data/examples/web_scraper/web_scraper.rb +312 -0
- data/lib/moxml/adapter/base.rb +107 -28
- data/lib/moxml/adapter/customized_libxml/cdata.rb +28 -0
- data/lib/moxml/adapter/customized_libxml/comment.rb +24 -0
- data/lib/moxml/adapter/customized_libxml/declaration.rb +85 -0
- data/lib/moxml/adapter/customized_libxml/element.rb +39 -0
- data/lib/moxml/adapter/customized_libxml/node.rb +44 -0
- data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +31 -0
- data/lib/moxml/adapter/customized_libxml/text.rb +27 -0
- data/lib/moxml/adapter/customized_oga/xml_generator.rb +1 -1
- data/lib/moxml/adapter/customized_ox/attribute.rb +28 -1
- data/lib/moxml/adapter/customized_rexml/formatter.rb +13 -8
- data/lib/moxml/adapter/headed_ox.rb +161 -0
- data/lib/moxml/adapter/libxml.rb +1564 -0
- data/lib/moxml/adapter/nokogiri.rb +156 -9
- data/lib/moxml/adapter/oga.rb +190 -15
- data/lib/moxml/adapter/ox.rb +322 -28
- data/lib/moxml/adapter/rexml.rb +157 -28
- data/lib/moxml/adapter.rb +21 -4
- data/lib/moxml/attribute.rb +6 -0
- data/lib/moxml/builder.rb +40 -4
- data/lib/moxml/config.rb +8 -3
- data/lib/moxml/context.rb +57 -2
- data/lib/moxml/declaration.rb +9 -0
- data/lib/moxml/doctype.rb +13 -1
- data/lib/moxml/document.rb +53 -6
- data/lib/moxml/document_builder.rb +34 -5
- data/lib/moxml/element.rb +71 -2
- data/lib/moxml/error.rb +175 -6
- data/lib/moxml/node.rb +155 -4
- data/lib/moxml/node_set.rb +34 -0
- data/lib/moxml/sax/block_handler.rb +194 -0
- data/lib/moxml/sax/element_handler.rb +124 -0
- data/lib/moxml/sax/handler.rb +113 -0
- data/lib/moxml/sax.rb +31 -0
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xml_utils/encoder.rb +4 -4
- data/lib/moxml/xml_utils.rb +7 -4
- data/lib/moxml/xpath/ast/node.rb +159 -0
- data/lib/moxml/xpath/cache.rb +91 -0
- data/lib/moxml/xpath/compiler.rb +1770 -0
- data/lib/moxml/xpath/context.rb +26 -0
- data/lib/moxml/xpath/conversion.rb +124 -0
- data/lib/moxml/xpath/engine.rb +52 -0
- data/lib/moxml/xpath/errors.rb +101 -0
- data/lib/moxml/xpath/lexer.rb +304 -0
- data/lib/moxml/xpath/parser.rb +485 -0
- data/lib/moxml/xpath/ruby/generator.rb +269 -0
- data/lib/moxml/xpath/ruby/node.rb +193 -0
- data/lib/moxml/xpath.rb +37 -0
- data/lib/moxml.rb +5 -2
- data/moxml.gemspec +3 -1
- data/old-specs/moxml/adapter/customized_libxml/.gitkeep +6 -0
- data/spec/consistency/README.md +77 -0
- data/spec/{moxml/examples/adapter_spec.rb → consistency/adapter_parity_spec.rb} +4 -4
- data/spec/examples/README.md +75 -0
- data/spec/{support/shared_examples/examples/attribute.rb → examples/attribute_examples_spec.rb} +1 -1
- data/spec/{support/shared_examples/examples/basic_usage.rb → examples/basic_usage_spec.rb} +2 -2
- data/spec/{support/shared_examples/examples/namespace.rb → examples/namespace_examples_spec.rb} +3 -3
- data/spec/{support/shared_examples/examples/readme_examples.rb → examples/readme_examples_spec.rb} +6 -4
- data/spec/{support/shared_examples/examples/xpath.rb → examples/xpath_examples_spec.rb} +10 -6
- data/spec/integration/README.md +71 -0
- data/spec/{moxml/all_with_adapters_spec.rb → integration/all_adapters_spec.rb} +3 -2
- data/spec/integration/headed_ox_integration_spec.rb +326 -0
- data/spec/{support → integration}/shared_examples/edge_cases.rb +37 -10
- data/spec/integration/shared_examples/high_level/.gitkeep +0 -0
- data/spec/{support/shared_examples/context.rb → integration/shared_examples/high_level/context_behavior.rb} +2 -1
- data/spec/{support/shared_examples/integration.rb → integration/shared_examples/integration_workflows.rb} +23 -6
- data/spec/integration/shared_examples/node_wrappers/.gitkeep +0 -0
- data/spec/{support/shared_examples/cdata.rb → integration/shared_examples/node_wrappers/cdata_behavior.rb} +6 -1
- data/spec/{support/shared_examples/comment.rb → integration/shared_examples/node_wrappers/comment_behavior.rb} +2 -1
- data/spec/{support/shared_examples/declaration.rb → integration/shared_examples/node_wrappers/declaration_behavior.rb} +5 -5
- data/spec/{support/shared_examples/doctype.rb → integration/shared_examples/node_wrappers/doctype_behavior.rb} +2 -2
- data/spec/{support/shared_examples/document.rb → integration/shared_examples/node_wrappers/document_behavior.rb} +1 -1
- data/spec/{support/shared_examples/node.rb → integration/shared_examples/node_wrappers/node_behavior.rb} +9 -2
- data/spec/{support/shared_examples/node_set.rb → integration/shared_examples/node_wrappers/node_set_behavior.rb} +1 -18
- data/spec/{support/shared_examples/processing_instruction.rb → integration/shared_examples/node_wrappers/processing_instruction_behavior.rb} +6 -2
- data/spec/moxml/README.md +41 -0
- data/spec/moxml/adapter/.gitkeep +0 -0
- data/spec/moxml/adapter/README.md +61 -0
- data/spec/moxml/adapter/base_spec.rb +27 -0
- data/spec/moxml/adapter/headed_ox_spec.rb +311 -0
- data/spec/moxml/adapter/libxml_spec.rb +14 -0
- data/spec/moxml/adapter/ox_spec.rb +9 -8
- data/spec/moxml/adapter/shared_examples/.gitkeep +0 -0
- data/spec/{support/shared_examples/xml_adapter.rb → moxml/adapter/shared_examples/adapter_contract.rb} +39 -12
- data/spec/moxml/adapter_spec.rb +16 -0
- data/spec/moxml/attribute_spec.rb +30 -0
- data/spec/moxml/builder_spec.rb +33 -0
- data/spec/moxml/cdata_spec.rb +31 -0
- data/spec/moxml/comment_spec.rb +31 -0
- data/spec/moxml/config_spec.rb +3 -3
- data/spec/moxml/context_spec.rb +28 -0
- data/spec/moxml/declaration_preservation_spec.rb +217 -0
- data/spec/moxml/declaration_spec.rb +36 -0
- data/spec/moxml/doctype_spec.rb +33 -0
- data/spec/moxml/document_builder_spec.rb +30 -0
- data/spec/moxml/document_spec.rb +105 -0
- data/spec/moxml/element_spec.rb +143 -0
- data/spec/moxml/error_spec.rb +266 -22
- data/spec/{moxml_spec.rb → moxml/moxml_spec.rb} +9 -9
- data/spec/moxml/namespace_spec.rb +32 -0
- data/spec/moxml/node_set_spec.rb +39 -0
- data/spec/moxml/node_spec.rb +37 -0
- data/spec/moxml/processing_instruction_spec.rb +34 -0
- data/spec/moxml/sax_spec.rb +1067 -0
- data/spec/moxml/text_spec.rb +31 -0
- data/spec/moxml/version_spec.rb +14 -0
- data/spec/moxml/xml_utils/.gitkeep +0 -0
- data/spec/moxml/xml_utils/encoder_spec.rb +27 -0
- data/spec/moxml/xml_utils_spec.rb +49 -0
- data/spec/moxml/xpath/ast/node_spec.rb +83 -0
- data/spec/moxml/xpath/axes_spec.rb +296 -0
- data/spec/moxml/xpath/cache_spec.rb +358 -0
- data/spec/moxml/xpath/compiler_spec.rb +406 -0
- data/spec/moxml/xpath/context_spec.rb +210 -0
- data/spec/moxml/xpath/conversion_spec.rb +365 -0
- data/spec/moxml/xpath/fixtures/sample.xml +25 -0
- data/spec/moxml/xpath/functions/boolean_functions_spec.rb +114 -0
- data/spec/moxml/xpath/functions/node_functions_spec.rb +145 -0
- data/spec/moxml/xpath/functions/numeric_functions_spec.rb +164 -0
- data/spec/moxml/xpath/functions/position_functions_spec.rb +93 -0
- data/spec/moxml/xpath/functions/special_functions_spec.rb +89 -0
- data/spec/moxml/xpath/functions/string_functions_spec.rb +381 -0
- data/spec/moxml/xpath/lexer_spec.rb +488 -0
- data/spec/moxml/xpath/parser_integration_spec.rb +210 -0
- data/spec/moxml/xpath/parser_spec.rb +364 -0
- data/spec/moxml/xpath/ruby/generator_spec.rb +421 -0
- data/spec/moxml/xpath/ruby/node_spec.rb +291 -0
- data/spec/moxml/xpath_capabilities_spec.rb +199 -0
- data/spec/moxml/xpath_spec.rb +77 -0
- data/spec/performance/README.md +83 -0
- data/spec/performance/benchmark_spec.rb +64 -0
- data/spec/{support/shared_examples/examples/memory.rb → performance/memory_usage_spec.rb} +4 -1
- data/spec/{support/shared_examples/examples/thread_safety.rb → performance/thread_safety_spec.rb} +3 -1
- data/spec/performance/xpath_benchmark_spec.rb +259 -0
- data/spec/spec_helper.rb +58 -1
- data/spec/support/xml_matchers.rb +1 -1
- metadata +178 -34
- data/spec/support/shared_examples/examples/benchmark_spec.rb +0 -51
- /data/spec/{support/shared_examples/builder.rb → integration/shared_examples/high_level/builder_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/document_builder.rb → integration/shared_examples/high_level/document_builder_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/attribute.rb → integration/shared_examples/node_wrappers/attribute_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/element.rb → integration/shared_examples/node_wrappers/element_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/namespace.rb → integration/shared_examples/node_wrappers/namespace_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/text.rb → integration/shared_examples/node_wrappers/text_behavior.rb} +0 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Error handling
|
|
3
|
+
nav_order: 8
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
== Error handling
|
|
7
|
+
|
|
8
|
+
=== Purpose
|
|
9
|
+
|
|
10
|
+
Comprehensive guide to handling errors in Moxml including error class
|
|
11
|
+
hierarchy, error context, and best practices for debugging.
|
|
12
|
+
|
|
13
|
+
=== Error class hierarchy
|
|
14
|
+
|
|
15
|
+
All Moxml errors inherit from `Moxml::Error`:
|
|
16
|
+
|
|
17
|
+
[source]
|
|
18
|
+
----
|
|
19
|
+
Moxml::Error (< StandardError)
|
|
20
|
+
├── ParseError # XML parsing failures
|
|
21
|
+
├── XPathError # XPath expression errors
|
|
22
|
+
├── ValidationError # XML validation failures
|
|
23
|
+
├── NamespaceError # Namespace-related errors
|
|
24
|
+
├── AdapterError # Adapter loading/operation errors
|
|
25
|
+
├── SerializationError # XML serialization failures
|
|
26
|
+
├── DocumentStructureError # Invalid document structure
|
|
27
|
+
├── AttributeError # Attribute operation errors
|
|
28
|
+
└── NotImplementedError # Unimplemented adapter features
|
|
29
|
+
----
|
|
30
|
+
|
|
31
|
+
=== ParseError
|
|
32
|
+
|
|
33
|
+
Raised when XML parsing fails.
|
|
34
|
+
|
|
35
|
+
[source,ruby]
|
|
36
|
+
----
|
|
37
|
+
begin
|
|
38
|
+
doc = Moxml.new.parse('<invalid><unclosed>', strict: true)
|
|
39
|
+
rescue Moxml::ParseError => e
|
|
40
|
+
puts "Parse error at line #{e.line}, column #{e.column}"
|
|
41
|
+
puts "Source excerpt: #{e.source}"
|
|
42
|
+
puts e.to_s # Includes helpful hint
|
|
43
|
+
# => "Hint: Check XML syntax and ensure all tags are properly closed"
|
|
44
|
+
end
|
|
45
|
+
----
|
|
46
|
+
|
|
47
|
+
**Attributes:**
|
|
48
|
+
|
|
49
|
+
* `line` - Line number where error occurred
|
|
50
|
+
* `column` - Column number where error occurred
|
|
51
|
+
* `source` - Excerpt of problematic XML
|
|
52
|
+
|
|
53
|
+
=== XPathError
|
|
54
|
+
|
|
55
|
+
Raised when XPath expression evaluation fails.
|
|
56
|
+
|
|
57
|
+
[source,ruby]
|
|
58
|
+
----
|
|
59
|
+
begin
|
|
60
|
+
results = doc.xpath('//invalid[[[')
|
|
61
|
+
rescue Moxml::XPathError => e
|
|
62
|
+
puts "Expression: #{e.expression}"
|
|
63
|
+
puts "Adapter: #{e.adapter}"
|
|
64
|
+
puts "Node: #{e.node.name}" if e.node
|
|
65
|
+
puts e.to_s
|
|
66
|
+
# => "Hint: Verify XPath syntax and ensure adapter supports the expression"
|
|
67
|
+
end
|
|
68
|
+
----
|
|
69
|
+
|
|
70
|
+
**Attributes:**
|
|
71
|
+
|
|
72
|
+
* `expression` - The XPath expression that failed
|
|
73
|
+
* `adapter` - Adapter name
|
|
74
|
+
* `node` - Context node (if available)
|
|
75
|
+
|
|
76
|
+
=== ValidationError
|
|
77
|
+
|
|
78
|
+
Raised when XML content violates XML specifications.
|
|
79
|
+
|
|
80
|
+
[source,ruby]
|
|
81
|
+
----
|
|
82
|
+
begin
|
|
83
|
+
doc.version = '2.0' # Invalid XML version
|
|
84
|
+
rescue Moxml::ValidationError => e
|
|
85
|
+
puts "Constraint: #{e.constraint}" # => "version"
|
|
86
|
+
puts "Value: #{e.value}" # => "2.0"
|
|
87
|
+
puts "Allowed: #{e.allowed_values}" # => ["1.0", "1.1"]
|
|
88
|
+
puts e.to_s
|
|
89
|
+
end
|
|
90
|
+
----
|
|
91
|
+
|
|
92
|
+
**Attributes:**
|
|
93
|
+
|
|
94
|
+
* `constraint` - What was being validated
|
|
95
|
+
* `value` - The invalid value
|
|
96
|
+
* `allowed_values` - Valid options
|
|
97
|
+
|
|
98
|
+
=== NamespaceError
|
|
99
|
+
|
|
100
|
+
Raised when namespace operations fail.
|
|
101
|
+
|
|
102
|
+
[source,ruby]
|
|
103
|
+
----
|
|
104
|
+
begin
|
|
105
|
+
element.add_namespace('invalid:prefix', 'http://example.org')
|
|
106
|
+
rescue Moxml::NamespaceError => e
|
|
107
|
+
puts "Prefix: #{e.prefix}"
|
|
108
|
+
puts "URI: #{e.uri}"
|
|
109
|
+
puts "Element: #{e.element.name}"
|
|
110
|
+
puts e.to_s
|
|
111
|
+
# => "Hint: Check namespace registration and URI validity"
|
|
112
|
+
end
|
|
113
|
+
----
|
|
114
|
+
|
|
115
|
+
**Attributes:**
|
|
116
|
+
|
|
117
|
+
* `prefix` - Namespace prefix
|
|
118
|
+
* `uri` - Namespace URI
|
|
119
|
+
* `element` - Element where error occurred
|
|
120
|
+
|
|
121
|
+
=== AdapterError
|
|
122
|
+
|
|
123
|
+
Raised when adapter loading or operations fail.
|
|
124
|
+
|
|
125
|
+
[source,ruby]
|
|
126
|
+
----
|
|
127
|
+
begin
|
|
128
|
+
context = Moxml.new
|
|
129
|
+
context.config.adapter = :nonexistent
|
|
130
|
+
rescue Moxml::AdapterError => e
|
|
131
|
+
puts "Adapter: #{e.adapter_name}" # => :nonexistent
|
|
132
|
+
puts "Operation: #{e.operation}" # => "set_adapter"
|
|
133
|
+
puts "Native Error: #{e.native_error}" # Original error
|
|
134
|
+
puts e.to_s
|
|
135
|
+
# => "Hint: Ensure the adapter gem is properly installed"
|
|
136
|
+
end
|
|
137
|
+
----
|
|
138
|
+
|
|
139
|
+
**Attributes:**
|
|
140
|
+
|
|
141
|
+
* `adapter_name` - Name of adapter
|
|
142
|
+
* `operation` - Operation that failed
|
|
143
|
+
* `native_error` - Underlying error from adapter library
|
|
144
|
+
|
|
145
|
+
=== Best practices
|
|
146
|
+
|
|
147
|
+
==== Catch specific errors
|
|
148
|
+
|
|
149
|
+
[source,ruby]
|
|
150
|
+
----
|
|
151
|
+
begin
|
|
152
|
+
doc = Moxml.new.parse(xml_string, strict: true)
|
|
153
|
+
results = doc.xpath('//book[@id="1"]')
|
|
154
|
+
rescue Moxml::ParseError => e
|
|
155
|
+
# Handle parsing errors
|
|
156
|
+
logger.error("XML parsing failed: #{e.to_s}")
|
|
157
|
+
rescue Moxml::XPathError => e
|
|
158
|
+
# Handle XPath errors
|
|
159
|
+
logger.error("XPath query failed: #{e.expression}")
|
|
160
|
+
rescue Moxml::NamespaceError => e
|
|
161
|
+
# Handle namespace errors
|
|
162
|
+
logger.error("Namespace error: #{e.prefix}:#{e.uri}")
|
|
163
|
+
rescue Moxml::Error => e
|
|
164
|
+
# Catch-all for other Moxml errors
|
|
165
|
+
logger.error("XML processing error: #{e.message}")
|
|
166
|
+
end
|
|
167
|
+
----
|
|
168
|
+
|
|
169
|
+
==== Use error context
|
|
170
|
+
|
|
171
|
+
[source,ruby]
|
|
172
|
+
----
|
|
173
|
+
begin
|
|
174
|
+
doc = Moxml.new.parse(xml)
|
|
175
|
+
rescue Moxml::ParseError => e
|
|
176
|
+
# Log comprehensive error information
|
|
177
|
+
logger.error <<~ERROR
|
|
178
|
+
XML Parse Error:
|
|
179
|
+
Line: #{e.line}
|
|
180
|
+
Column: #{e.column}
|
|
181
|
+
Source: #{e.source}
|
|
182
|
+
Message: #{e.message}
|
|
183
|
+
Hint: #{e.to_s}
|
|
184
|
+
ERROR
|
|
185
|
+
|
|
186
|
+
# Re-raise or handle
|
|
187
|
+
raise unless development_mode?
|
|
188
|
+
end
|
|
189
|
+
----
|
|
190
|
+
|
|
191
|
+
==== Validate before operations
|
|
192
|
+
|
|
193
|
+
[source,ruby]
|
|
194
|
+
----
|
|
195
|
+
# Validate adapter available
|
|
196
|
+
def ensure_adapter_available(adapter_name)
|
|
197
|
+
context = Moxml.new
|
|
198
|
+
context.config.adapter = adapter_name
|
|
199
|
+
rescue Moxml::AdapterError => e
|
|
200
|
+
raise "Required adapter #{adapter_name} not available: #{e.message}"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Validate document structure
|
|
204
|
+
def validate_structure(doc)
|
|
205
|
+
required = ['title', 'author']
|
|
206
|
+
required.each do |elem|
|
|
207
|
+
unless doc.at_xpath("//#{elem}")
|
|
208
|
+
raise Moxml::ValidationError, "Missing required element: #{elem}"
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
----
|
|
213
|
+
|
|
214
|
+
=== Error recovery strategies
|
|
215
|
+
|
|
216
|
+
==== Graceful degradation
|
|
217
|
+
|
|
218
|
+
[source,ruby]
|
|
219
|
+
----
|
|
220
|
+
def parse_with_fallback(xml)
|
|
221
|
+
# Try strict parsing first
|
|
222
|
+
begin
|
|
223
|
+
return Moxml.new.parse(xml, strict: true)
|
|
224
|
+
rescue Moxml::ParseError
|
|
225
|
+
logger.warn("Strict parsing failed, trying relaxed mode")
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Fall back to relaxed parsing
|
|
229
|
+
begin
|
|
230
|
+
return Moxml.new.parse(xml, strict: false)
|
|
231
|
+
rescue Moxml::ParseError => e
|
|
232
|
+
logger.error("Cannot parse XML: #{e.message}")
|
|
233
|
+
raise
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
----
|
|
237
|
+
|
|
238
|
+
==== Adapter fallback
|
|
239
|
+
|
|
240
|
+
[source,ruby]
|
|
241
|
+
----
|
|
242
|
+
def parse_with_adapter_fallback(xml)
|
|
243
|
+
adapters = [:nokogiri, :libxml, :oga, :rexml]
|
|
244
|
+
|
|
245
|
+
adapters.each do |adapter_name|
|
|
246
|
+
begin
|
|
247
|
+
context = Moxml.new
|
|
248
|
+
context.config.adapter = adapter_name
|
|
249
|
+
return context.parse(xml)
|
|
250
|
+
rescue Moxml::AdapterError
|
|
251
|
+
# Adapter not available, try next
|
|
252
|
+
next
|
|
253
|
+
rescue Moxml::ParseError => e
|
|
254
|
+
# Parsing failed with this adapter
|
|
255
|
+
logger.warn("#{adapter_name} failed: #{e.message}")
|
|
256
|
+
next
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
raise "Could not parse XML with any available adapter"
|
|
261
|
+
end
|
|
262
|
+
----
|
|
263
|
+
|
|
264
|
+
=== Common error scenarios
|
|
265
|
+
|
|
266
|
+
==== Malformed XML
|
|
267
|
+
|
|
268
|
+
[source,ruby]
|
|
269
|
+
----
|
|
270
|
+
xml = '<root><unclosed>'
|
|
271
|
+
|
|
272
|
+
begin
|
|
273
|
+
doc = Moxml.new.parse(xml, strict: true)
|
|
274
|
+
rescue Moxml::ParseError => e
|
|
275
|
+
puts "Malformed XML detected"
|
|
276
|
+
puts "Error at line #{e.line}" if e.line
|
|
277
|
+
# Handle or fix the XML
|
|
278
|
+
end
|
|
279
|
+
----
|
|
280
|
+
|
|
281
|
+
==== Invalid XPath
|
|
282
|
+
|
|
283
|
+
[source,ruby]
|
|
284
|
+
----
|
|
285
|
+
begin
|
|
286
|
+
doc.xpath('//book[[[invalid]]]')
|
|
287
|
+
rescue Moxml::XPathError => e
|
|
288
|
+
puts "Invalid XPath expression: #{e.expression}"
|
|
289
|
+
# Use corrected expression
|
|
290
|
+
doc.xpath('//book[@id]')
|
|
291
|
+
end
|
|
292
|
+
----
|
|
293
|
+
|
|
294
|
+
==== Unsupported features
|
|
295
|
+
|
|
296
|
+
[source,ruby]
|
|
297
|
+
----
|
|
298
|
+
# Ox doesn't support complex XPath
|
|
299
|
+
context = Moxml.new
|
|
300
|
+
context.config.adapter = :ox
|
|
301
|
+
|
|
302
|
+
begin
|
|
303
|
+
doc.xpath('//book[price < 30]')
|
|
304
|
+
rescue Moxml::NotImplementedError => e
|
|
305
|
+
puts "Feature not supported by #{e.adapter}: #{e.feature}"
|
|
306
|
+
# Use workaround
|
|
307
|
+
books = doc.xpath('//book')
|
|
308
|
+
cheap = books.select { |b| b.at_xpath('.//price').text.to_f < 30 }
|
|
309
|
+
end
|
|
310
|
+
----
|
|
311
|
+
|
|
312
|
+
=== Debugging techniques
|
|
313
|
+
|
|
314
|
+
==== Enable detailed errors
|
|
315
|
+
|
|
316
|
+
[source,ruby]
|
|
317
|
+
----
|
|
318
|
+
# Error messages include hints
|
|
319
|
+
begin
|
|
320
|
+
doc = Moxml.new.parse(invalid_xml)
|
|
321
|
+
rescue Moxml::Error => e
|
|
322
|
+
puts e.to_s # Full message with debugging hints
|
|
323
|
+
puts e.class.name # Error type
|
|
324
|
+
puts e.backtrace.first(5) # Stack trace
|
|
325
|
+
end
|
|
326
|
+
----
|
|
327
|
+
|
|
328
|
+
==== Inspect error context
|
|
329
|
+
|
|
330
|
+
[source,ruby]
|
|
331
|
+
----
|
|
332
|
+
begin
|
|
333
|
+
result = doc.xpath(expression)
|
|
334
|
+
rescue Moxml::XPathError => e
|
|
335
|
+
# Debug XPath errors
|
|
336
|
+
puts "Failed expression: #{e.expression}"
|
|
337
|
+
puts "On node: #{e.node.name}" if e.node
|
|
338
|
+
puts "With adapter: #{e.adapter}"
|
|
339
|
+
|
|
340
|
+
# Try simpler expression
|
|
341
|
+
doc.xpath('//book')
|
|
342
|
+
end
|
|
343
|
+
----
|
|
344
|
+
|
|
345
|
+
=== See also
|
|
346
|
+
|
|
347
|
+
* link:../references/error-classes[Error classes reference] - Complete API
|
|
348
|
+
* link:../guides/error-handling[Error handling guide] - Advanced patterns
|
|
349
|
+
* link:adapters/[Adapters] - Adapter-specific error behaviors
|