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,450 @@
|
|
|
1
|
+
= XML Declaration Preservation
|
|
2
|
+
:toc:
|
|
3
|
+
:toclevels: 3
|
|
4
|
+
|
|
5
|
+
== Overview
|
|
6
|
+
|
|
7
|
+
Moxml automatically preserves the presence or absence of XML declarations (`<?xml version="1.0"?>`) when parsing and serializing documents. This ensures round-trip fidelity and compliance with standards that require specific declaration handling.
|
|
8
|
+
|
|
9
|
+
=== Why This Matters
|
|
10
|
+
|
|
11
|
+
Some XML use cases require specific declaration handling:
|
|
12
|
+
|
|
13
|
+
* **SVG Files**: Often have no XML declaration
|
|
14
|
+
* **XML Fragments**: Should not have declarations
|
|
15
|
+
* **Standards Compliance**: Some specs prohibit declarations in certain contexts
|
|
16
|
+
* **Round-Trip Fidelity**: Parse → Modify → Serialize should preserve format
|
|
17
|
+
|
|
18
|
+
=== Key Features
|
|
19
|
+
|
|
20
|
+
* **Automatic Detection**: Moxml detects whether input had a declaration
|
|
21
|
+
* **Automatic Preservation**: Output matches input format by default
|
|
22
|
+
* **Explicit Override**: Force add or remove declarations when needed
|
|
23
|
+
* **All Adapters**: Works across all 6 XML adapters
|
|
24
|
+
|
|
25
|
+
== Basic Usage
|
|
26
|
+
|
|
27
|
+
=== Automatic Preservation
|
|
28
|
+
|
|
29
|
+
Moxml automatically preserves whether input had an XML declaration:
|
|
30
|
+
|
|
31
|
+
[source,ruby]
|
|
32
|
+
----
|
|
33
|
+
require 'moxml'
|
|
34
|
+
|
|
35
|
+
# Document without declaration
|
|
36
|
+
svg = '<svg xmlns="http://www.w3.org/2000/svg"><rect/></svg>'
|
|
37
|
+
doc = Moxml.new.parse(svg)
|
|
38
|
+
doc.to_xml
|
|
39
|
+
# => "<svg xmlns=\"http://www.w3.org/2000/svg\"><rect/></svg>"
|
|
40
|
+
# ✓ No <?xml...?> added
|
|
41
|
+
|
|
42
|
+
# Document with declaration
|
|
43
|
+
xml = '<?xml version="1.0" encoding="UTF-8"?><root><child/></root>'
|
|
44
|
+
doc = Moxml.new.parse(xml)
|
|
45
|
+
doc.to_xml
|
|
46
|
+
# => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root><child/></root>"
|
|
47
|
+
# ✓ Declaration preserved
|
|
48
|
+
----
|
|
49
|
+
|
|
50
|
+
=== Checking Declaration Presence
|
|
51
|
+
|
|
52
|
+
Use the `has_xml_declaration` attribute to check if a document has a declaration:
|
|
53
|
+
|
|
54
|
+
[source,ruby]
|
|
55
|
+
----
|
|
56
|
+
# Document without declaration
|
|
57
|
+
doc = Moxml.new.parse('<root/>')
|
|
58
|
+
doc.has_xml_declaration # => false
|
|
59
|
+
|
|
60
|
+
# Document with declaration
|
|
61
|
+
doc = Moxml.new.parse('<?xml version="1.0"?><root/>')
|
|
62
|
+
doc.has_xml_declaration # => true
|
|
63
|
+
----
|
|
64
|
+
|
|
65
|
+
== Explicit Control
|
|
66
|
+
|
|
67
|
+
=== Forcing Declaration Addition
|
|
68
|
+
|
|
69
|
+
Add a declaration to documents that don't have one:
|
|
70
|
+
|
|
71
|
+
[source,ruby]
|
|
72
|
+
----
|
|
73
|
+
svg = '<svg><rect/></svg>'
|
|
74
|
+
doc = Moxml.new.parse(svg)
|
|
75
|
+
|
|
76
|
+
# Force add declaration
|
|
77
|
+
output = doc.to_xml(declaration: true)
|
|
78
|
+
# => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><svg><rect/></svg>"
|
|
79
|
+
----
|
|
80
|
+
|
|
81
|
+
=== Removing Declarations
|
|
82
|
+
|
|
83
|
+
Remove declaration from documents that have one:
|
|
84
|
+
|
|
85
|
+
[source,ruby]
|
|
86
|
+
----
|
|
87
|
+
xml = '<?xml version="1.0"?><root><item/></root>'
|
|
88
|
+
doc = Moxml.new.parse(xml)
|
|
89
|
+
|
|
90
|
+
# Force remove declaration
|
|
91
|
+
output = doc.to_xml(declaration: false)
|
|
92
|
+
# => "<root><item/></root>"
|
|
93
|
+
----
|
|
94
|
+
|
|
95
|
+
== Use Cases
|
|
96
|
+
|
|
97
|
+
=== SVG File Processing
|
|
98
|
+
|
|
99
|
+
SVG files often don't have XML declarations. Moxml preserves this:
|
|
100
|
+
|
|
101
|
+
[source,ruby]
|
|
102
|
+
----
|
|
103
|
+
# Original SVG without declaration
|
|
104
|
+
svg_content = File.read('image.svg')
|
|
105
|
+
doc = Moxml.new.parse(svg_content)
|
|
106
|
+
|
|
107
|
+
# Modify SVG (add viewBox)
|
|
108
|
+
doc.root['viewBox'] = '0 0 100 100'
|
|
109
|
+
|
|
110
|
+
# Save - no declaration added
|
|
111
|
+
File.write('image.svg', doc.to_xml)
|
|
112
|
+
----
|
|
113
|
+
|
|
114
|
+
=== XML Fragment Generation
|
|
115
|
+
|
|
116
|
+
Create XML fragments without declarations:
|
|
117
|
+
|
|
118
|
+
[source,ruby]
|
|
119
|
+
----
|
|
120
|
+
context = Moxml.new
|
|
121
|
+
doc = context.create_document
|
|
122
|
+
|
|
123
|
+
# Build fragment
|
|
124
|
+
root = doc.create_element('fragment')
|
|
125
|
+
root << doc.create_element('item')
|
|
126
|
+
doc.root = root
|
|
127
|
+
|
|
128
|
+
# Serialize without declaration (default for built documents)
|
|
129
|
+
doc.to_xml # => "<fragment><item/></fragment>"
|
|
130
|
+
----
|
|
131
|
+
|
|
132
|
+
=== Standards-Compliant XML
|
|
133
|
+
|
|
134
|
+
Some XML standards require or prohibit declarations:
|
|
135
|
+
|
|
136
|
+
[source,ruby]
|
|
137
|
+
----
|
|
138
|
+
# Standard prohibits declarations
|
|
139
|
+
doc = Moxml.new.parse(compliant_xml_without_decl)
|
|
140
|
+
output = doc.to_xml # Declaration correctly absent
|
|
141
|
+
|
|
142
|
+
# Standard requires declarations
|
|
143
|
+
doc = Moxml.new.parse(standard_xml_with_decl)
|
|
144
|
+
output = doc.to_xml # Declaration correctly present
|
|
145
|
+
----
|
|
146
|
+
|
|
147
|
+
=== Round-Trip Processing
|
|
148
|
+
|
|
149
|
+
Preserve original format through multiple parse/serialize cycles:
|
|
150
|
+
|
|
151
|
+
[source,ruby]
|
|
152
|
+
----
|
|
153
|
+
original = '<data><item id="1"/></data>'
|
|
154
|
+
|
|
155
|
+
# First round-trip
|
|
156
|
+
doc1 = Moxml.new.parse(original)
|
|
157
|
+
intermediate = doc1.to_xml
|
|
158
|
+
|
|
159
|
+
# Second round-trip
|
|
160
|
+
doc2 = Moxml.new.parse(intermediate)
|
|
161
|
+
final = doc2.to_xml
|
|
162
|
+
|
|
163
|
+
# All three are identical
|
|
164
|
+
original == intermediate && intermediate == final # => true
|
|
165
|
+
----
|
|
166
|
+
|
|
167
|
+
== Adapter Behavior
|
|
168
|
+
|
|
169
|
+
All 6 adapters support declaration preservation:
|
|
170
|
+
|
|
171
|
+
[cols="1,3"]
|
|
172
|
+
|===
|
|
173
|
+
|Adapter |Implementation
|
|
174
|
+
|
|
175
|
+
|Nokogiri
|
|
176
|
+
|Uses `SaveOptions::NO_DECLARATION` flag
|
|
177
|
+
|
|
178
|
+
|Oga
|
|
179
|
+
|Custom serialization logic
|
|
180
|
+
|
|
181
|
+
|REXML
|
|
182
|
+
|Conditional declaration output
|
|
183
|
+
|
|
184
|
+
|Ox
|
|
185
|
+
|Declaration control in serialize
|
|
186
|
+
|
|
187
|
+
|LibXML
|
|
188
|
+
|Custom serializer respects flag
|
|
189
|
+
|
|
190
|
+
|HeadedOx
|
|
191
|
+
|Inherits Ox implementation
|
|
192
|
+
|===
|
|
193
|
+
|
|
194
|
+
== Advanced Usage
|
|
195
|
+
|
|
196
|
+
=== Programmatically Built Documents
|
|
197
|
+
|
|
198
|
+
Documents built from scratch default to no declaration:
|
|
199
|
+
|
|
200
|
+
[source,ruby]
|
|
201
|
+
----
|
|
202
|
+
doc = Moxml.new.create_document
|
|
203
|
+
root = doc.create_element('config')
|
|
204
|
+
doc.root = root
|
|
205
|
+
|
|
206
|
+
doc.has_xml_declaration # => false
|
|
207
|
+
doc.to_xml # => "<config/>"
|
|
208
|
+
|
|
209
|
+
# Explicitly add declaration if needed
|
|
210
|
+
doc.to_xml(declaration: true)
|
|
211
|
+
# => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><config/>"
|
|
212
|
+
----
|
|
213
|
+
|
|
214
|
+
=== Element Serialization
|
|
215
|
+
|
|
216
|
+
Only document nodes can have declarations. Element serialization never includes declarations:
|
|
217
|
+
|
|
218
|
+
[source,ruby]
|
|
219
|
+
----
|
|
220
|
+
doc = Moxml.new.parse('<?xml version="1.0"?><root><child/></root>')
|
|
221
|
+
element = doc.root
|
|
222
|
+
|
|
223
|
+
# Element serialization - no declaration
|
|
224
|
+
element.to_xml # => "<root><child/></root>"
|
|
225
|
+
|
|
226
|
+
# Even with explicit request (ignored for elements)
|
|
227
|
+
element.to_xml(declaration: true) # => "<root><child/></root>"
|
|
228
|
+
----
|
|
229
|
+
|
|
230
|
+
=== Custom Declaration Attributes
|
|
231
|
+
|
|
232
|
+
When forcing declaration addition, use standard attributes:
|
|
233
|
+
|
|
234
|
+
[source,ruby]
|
|
235
|
+
----
|
|
236
|
+
doc = Moxml.new.parse('<root/>')
|
|
237
|
+
|
|
238
|
+
# Default declaration
|
|
239
|
+
doc.to_xml(declaration: true)
|
|
240
|
+
# => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root/>"
|
|
241
|
+
|
|
242
|
+
# Custom encoding via adapter
|
|
243
|
+
doc.to_xml(declaration: true, encoding: "ISO-8859-1")
|
|
244
|
+
# => "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?><root/>"
|
|
245
|
+
----
|
|
246
|
+
|
|
247
|
+
== Best Practices
|
|
248
|
+
|
|
249
|
+
=== Let Moxml Handle It
|
|
250
|
+
|
|
251
|
+
In most cases, rely on automatic preservation:
|
|
252
|
+
|
|
253
|
+
[source,ruby]
|
|
254
|
+
----
|
|
255
|
+
# Good - automatic preservation
|
|
256
|
+
doc = Moxml.new.parse(xml_content)
|
|
257
|
+
modified = process_document(doc)
|
|
258
|
+
output = doc.to_xml # Declaration preserved automatically
|
|
259
|
+
|
|
260
|
+
# Only use explicit control when required by specific needs
|
|
261
|
+
output = doc.to_xml(declaration: false) # Explicit requirement
|
|
262
|
+
----
|
|
263
|
+
|
|
264
|
+
=== Check Declaration Before Processing
|
|
265
|
+
|
|
266
|
+
Know what you're working with:
|
|
267
|
+
|
|
268
|
+
[source,ruby]
|
|
269
|
+
----
|
|
270
|
+
doc = Moxml.new.parse(xml_source)
|
|
271
|
+
|
|
272
|
+
if doc.has_xml_declaration
|
|
273
|
+
# Handle documents with declarations
|
|
274
|
+
process_with_declaration(doc)
|
|
275
|
+
else
|
|
276
|
+
# Handle fragments without declarations
|
|
277
|
+
process_fragment(doc)
|
|
278
|
+
end
|
|
279
|
+
----
|
|
280
|
+
|
|
281
|
+
=== Document Your Requirements
|
|
282
|
+
|
|
283
|
+
Make declaration requirements explicit in code:
|
|
284
|
+
|
|
285
|
+
[source,ruby]
|
|
286
|
+
----
|
|
287
|
+
def save_svg(doc)
|
|
288
|
+
# SVG files should not have XML declarations
|
|
289
|
+
raise "SVG has declaration" if doc.has_xml_declaration
|
|
290
|
+
File.write('output.svg', doc.to_xml)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def save_xml_config(doc)
|
|
294
|
+
# Config files require declarations
|
|
295
|
+
File.write('config.xml', doc.to_xml(declaration: true))
|
|
296
|
+
end
|
|
297
|
+
----
|
|
298
|
+
|
|
299
|
+
== Migration from Previous Versions
|
|
300
|
+
|
|
301
|
+
=== Behavior Change
|
|
302
|
+
|
|
303
|
+
In Moxml versions before 0.2.1, serialization *always* added an XML declaration. Starting with 0.2.1, behavior changed to preserve input format:
|
|
304
|
+
|
|
305
|
+
[cols="1,2,2"]
|
|
306
|
+
|===
|
|
307
|
+
|Scenario |Before v0.2.1 |v0.2.1+
|
|
308
|
+
|
|
309
|
+
|Parse without declaration
|
|
310
|
+
|Added declaration ❌
|
|
311
|
+
|No declaration ✓
|
|
312
|
+
|
|
313
|
+
|Parse with declaration
|
|
314
|
+
|Preserved declaration ✓
|
|
315
|
+
|Preserved declaration ✓
|
|
316
|
+
|
|
317
|
+
|Built document
|
|
318
|
+
|Added declaration
|
|
319
|
+
|No declaration (can override)
|
|
320
|
+
|===
|
|
321
|
+
|
|
322
|
+
=== Update Your Code
|
|
323
|
+
|
|
324
|
+
If you relied on automatic declaration addition:
|
|
325
|
+
|
|
326
|
+
[source,ruby]
|
|
327
|
+
----
|
|
328
|
+
# Before (relied on automatic declaration)
|
|
329
|
+
doc = Moxml.new.parse('<root/>')
|
|
330
|
+
output = doc.to_xml # Had declaration
|
|
331
|
+
|
|
332
|
+
# After (explicitly request if needed)
|
|
333
|
+
doc = Moxml.new.parse('<root/>')
|
|
334
|
+
output = doc.to_xml(declaration: true) # Force add
|
|
335
|
+
----
|
|
336
|
+
|
|
337
|
+
=== Minimal Impact
|
|
338
|
+
|
|
339
|
+
Most code will see **no change** because:
|
|
340
|
+
|
|
341
|
+
* Documents with declarations still preserve them
|
|
342
|
+
* Only fragments without declarations behave differently
|
|
343
|
+
* New behavior is arguably more correct
|
|
344
|
+
|
|
345
|
+
== Troubleshooting
|
|
346
|
+
|
|
347
|
+
=== Declaration Not Preserved
|
|
348
|
+
|
|
349
|
+
If declaration isn't being preserved, check:
|
|
350
|
+
|
|
351
|
+
1. **Input Format**: Verify input actually has `<?xml...?>`
|
|
352
|
+
+
|
|
353
|
+
[source,ruby]
|
|
354
|
+
----
|
|
355
|
+
xml_content = File.read('file.xml')
|
|
356
|
+
puts "Has declaration: #{xml_content.strip.start_with?('<?xml')}"
|
|
357
|
+
----
|
|
358
|
+
|
|
359
|
+
2. **Explicit Override**: Check if code explicitly removes it
|
|
360
|
+
+
|
|
361
|
+
[source,ruby]
|
|
362
|
+
----
|
|
363
|
+
# This will remove declaration regardless of input
|
|
364
|
+
doc.to_xml(declaration: false)
|
|
365
|
+
----
|
|
366
|
+
|
|
367
|
+
3. **Element vs Document**: Only documents can have declarations
|
|
368
|
+
+
|
|
369
|
+
[source,ruby]
|
|
370
|
+
----
|
|
371
|
+
element = doc.root
|
|
372
|
+
element.to_xml # Never has declaration (correct)
|
|
373
|
+
----
|
|
374
|
+
|
|
375
|
+
=== Unwanted Declaration
|
|
376
|
+
|
|
377
|
+
If declaration is added when you don't want it:
|
|
378
|
+
|
|
379
|
+
[source,ruby]
|
|
380
|
+
----
|
|
381
|
+
# Solution 1: Parse input without declaration
|
|
382
|
+
svg = '<svg><rect/></svg>' # No <?xml...?>
|
|
383
|
+
doc = Moxml.new.parse(svg)
|
|
384
|
+
doc.to_xml # No declaration
|
|
385
|
+
|
|
386
|
+
# Solution 2: Explicitly remove
|
|
387
|
+
doc.to_xml(declaration: false)
|
|
388
|
+
|
|
389
|
+
# Solution 3: Check and fix source
|
|
390
|
+
if doc.has_xml_declaration
|
|
391
|
+
# Input has declaration - remove from source or override
|
|
392
|
+
output = doc.to_xml(declaration: false)
|
|
393
|
+
end
|
|
394
|
+
----
|
|
395
|
+
|
|
396
|
+
=== Whitespace Before Declaration
|
|
397
|
+
|
|
398
|
+
XML declarations must be at the document start. Whitespace before the declaration makes it invalid:
|
|
399
|
+
|
|
400
|
+
[source,ruby]
|
|
401
|
+
----
|
|
402
|
+
# Invalid - whitespace before declaration
|
|
403
|
+
invalid = ' <?xml version="1.0"?><root/>'
|
|
404
|
+
doc = Moxml.new.parse(invalid) # May raise error depending on adapter
|
|
405
|
+
|
|
406
|
+
# Valid - declaration at start
|
|
407
|
+
valid = '<?xml version="1.0"?><root/>'
|
|
408
|
+
doc = Moxml.new.parse(valid) # Works correctly
|
|
409
|
+
----
|
|
410
|
+
|
|
411
|
+
== API Reference
|
|
412
|
+
|
|
413
|
+
=== Document Attributes
|
|
414
|
+
|
|
415
|
+
==== has_xml_declaration
|
|
416
|
+
|
|
417
|
+
[source,ruby]
|
|
418
|
+
----
|
|
419
|
+
doc.has_xml_declaration # => Boolean
|
|
420
|
+
----
|
|
421
|
+
|
|
422
|
+
Returns `true` if the document was parsed from XML that contained an XML declaration, `false` otherwise.
|
|
423
|
+
|
|
424
|
+
* Read/write attribute (can be manually set)
|
|
425
|
+
* Defaults to `false` for programmatically built documents
|
|
426
|
+
* Automatically set during parsing
|
|
427
|
+
|
|
428
|
+
=== Serialization Options
|
|
429
|
+
|
|
430
|
+
==== declaration
|
|
431
|
+
|
|
432
|
+
[source,ruby]
|
|
433
|
+
----
|
|
434
|
+
doc.to_xml(declaration: true) # Force include declaration
|
|
435
|
+
doc.to_xml(declaration: false) # Force exclude declaration
|
|
436
|
+
doc.to_xml # Use automatic preservation
|
|
437
|
+
----
|
|
438
|
+
|
|
439
|
+
Controls whether XML declaration is included in serialized output:
|
|
440
|
+
|
|
441
|
+
* `true`: Always include declaration
|
|
442
|
+
* `false`: Never include declaration
|
|
443
|
+
* Not specified: Use `has_xml_declaration` value (automatic preservation)
|
|
444
|
+
|
|
445
|
+
== See Also
|
|
446
|
+
|
|
447
|
+
* link:parsing-xml.html[Parsing XML] - How to parse XML documents
|
|
448
|
+
* link:modifying-xml.html[Modifying XML] - Working with parsed documents
|
|
449
|
+
* link:../adapters/index.html[Adapters] - Adapter-specific behavior
|
|
450
|
+
* link:../best-practices.html[Best Practices] - General XML processing guidelines
|