moxml 0.1.8 → 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/.rubocop_todo.yml +22 -39
- data/docs/_config.yml +3 -3
- data/docs/_guides/index.adoc +6 -0
- data/docs/_guides/modifying-xml.adoc +0 -1
- data/docs/_guides/parsing-xml.adoc +0 -1
- data/docs/_guides/xml-declaration.adoc +450 -0
- data/docs/_pages/adapter-compatibility.adoc +1 -1
- data/docs/_pages/adapters/headed-ox.adoc +9 -9
- data/docs/_pages/adapters/index.adoc +0 -1
- data/docs/_pages/adapters/libxml.adoc +1 -2
- data/docs/_pages/adapters/nokogiri.adoc +1 -2
- data/docs/_pages/adapters/oga.adoc +1 -2
- data/docs/_pages/adapters/ox.adoc +2 -1
- data/docs/_pages/adapters/rexml.adoc +1 -2
- data/docs/_pages/best-practices.adoc +0 -1
- data/docs/_pages/compatibility.adoc +0 -1
- data/docs/_pages/configuration.adoc +0 -1
- data/docs/_pages/error-handling.adoc +0 -1
- data/docs/_pages/headed-ox-limitations.adoc +16 -0
- data/docs/_pages/installation.adoc +0 -1
- data/docs/_pages/node-api-reference.adoc +0 -1
- data/docs/_pages/performance.adoc +0 -1
- data/docs/_pages/quick-start.adoc +0 -1
- data/docs/_pages/thread-safety.adoc +0 -1
- data/docs/_references/document-api.adoc +0 -1
- data/docs/_tutorials/basic-usage.adoc +0 -1
- data/docs/_tutorials/builder-pattern.adoc +0 -1
- data/docs/_tutorials/namespace-handling.adoc +0 -1
- data/docs/_tutorials/xpath-queries.adoc +0 -1
- data/lib/moxml/adapter/customized_rexml/formatter.rb +2 -2
- data/lib/moxml/adapter/libxml.rb +19 -3
- data/lib/moxml/adapter/nokogiri.rb +37 -2
- data/lib/moxml/adapter/oga.rb +67 -3
- data/lib/moxml/adapter/ox.rb +45 -7
- data/lib/moxml/adapter/rexml.rb +32 -10
- data/lib/moxml/context.rb +18 -1
- data/lib/moxml/declaration.rb +9 -0
- data/lib/moxml/document.rb +14 -0
- data/lib/moxml/document_builder.rb +7 -0
- data/lib/moxml/node.rb +61 -1
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xpath/compiler.rb +2 -0
- data/spec/integration/shared_examples/node_wrappers/declaration_behavior.rb +0 -3
- data/spec/moxml/declaration_preservation_spec.rb +217 -0
- data/spec/performance/memory_usage_spec.rb +3 -2
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b2faed71c75f32fcfe528621b1d3ed01c603b04f81e75d5b45b6c525d9d1836
|
|
4
|
+
data.tar.gz: 68ead788c2e5098b2f1f88653443fe4d8ff1e76b17dab09c08badb64c7ab70a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8aa59a486d44d101e1c078fc6fde2eea1cad9d4d7690d937dc7faef233a4143206432ffab53da3a2ef01af9245d17f706ddb01bc55b2c3555b358a6a3df10143
|
|
7
|
+
data.tar.gz: b5256089daef1f94012046bd7ff0fcbff1827c615b46d416231933de3193f3a262063a6ad761a87c629cdfe15fc51644fc1887d43eaeff5f8710bd16a791ea3c
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on 2025-11-24
|
|
3
|
+
# on 2025-11-24 13:45:20 UTC using RuboCop version 1.80.0.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
|
8
8
|
|
|
9
|
-
# Offense count:
|
|
9
|
+
# Offense count: 192
|
|
10
10
|
# This cop supports safe autocorrection (--autocorrect).
|
|
11
11
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
|
|
12
12
|
# URISchemes: http, https
|
|
13
13
|
Layout/LineLength:
|
|
14
14
|
Enabled: false
|
|
15
15
|
|
|
16
|
-
# Offense count:
|
|
16
|
+
# Offense count: 7
|
|
17
17
|
# Configuration parameters: AllowedMethods.
|
|
18
18
|
# AllowedMethods: enums
|
|
19
19
|
Lint/ConstantDefinitionInBlock:
|
|
20
20
|
Exclude:
|
|
21
|
+
- 'spec/moxml/declaration_preservation_spec.rb'
|
|
21
22
|
- 'spec/moxml/sax_spec.rb'
|
|
22
23
|
|
|
23
24
|
# Offense count: 6
|
|
@@ -59,7 +60,7 @@ Lint/NoReturnInBeginEndBlocks:
|
|
|
59
60
|
Exclude:
|
|
60
61
|
- 'examples/api_client/api_client.rb'
|
|
61
62
|
|
|
62
|
-
# Offense count:
|
|
63
|
+
# Offense count: 93
|
|
63
64
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
|
64
65
|
Metrics/AbcSize:
|
|
65
66
|
Enabled: false
|
|
@@ -75,12 +76,12 @@ Metrics/BlockLength:
|
|
|
75
76
|
Metrics/BlockNesting:
|
|
76
77
|
Max: 4
|
|
77
78
|
|
|
78
|
-
# Offense count:
|
|
79
|
+
# Offense count: 63
|
|
79
80
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
80
81
|
Metrics/CyclomaticComplexity:
|
|
81
82
|
Enabled: false
|
|
82
83
|
|
|
83
|
-
# Offense count:
|
|
84
|
+
# Offense count: 164
|
|
84
85
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
85
86
|
Metrics/MethodLength:
|
|
86
87
|
Max: 110
|
|
@@ -90,23 +91,10 @@ Metrics/MethodLength:
|
|
|
90
91
|
Metrics/ParameterLists:
|
|
91
92
|
Max: 7
|
|
92
93
|
|
|
93
|
-
# Offense count:
|
|
94
|
+
# Offense count: 42
|
|
94
95
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
95
96
|
Metrics/PerceivedComplexity:
|
|
96
|
-
|
|
97
|
-
- 'benchmarks/generate_report.rb'
|
|
98
|
-
- 'examples/web_scraper/web_scraper.rb'
|
|
99
|
-
- 'lib/moxml/adapter/customized_oga/xml_generator.rb'
|
|
100
|
-
- 'lib/moxml/adapter/customized_rexml/formatter.rb'
|
|
101
|
-
- 'lib/moxml/adapter/libxml.rb'
|
|
102
|
-
- 'lib/moxml/adapter/ox.rb'
|
|
103
|
-
- 'lib/moxml/adapter/rexml.rb'
|
|
104
|
-
- 'lib/moxml/document.rb'
|
|
105
|
-
- 'lib/moxml/xpath/compiler.rb'
|
|
106
|
-
- 'lib/moxml/xpath/conversion.rb'
|
|
107
|
-
- 'lib/moxml/xpath/lexer.rb'
|
|
108
|
-
- 'lib/moxml/xpath/parser.rb'
|
|
109
|
-
- 'lib/moxml/xpath/ruby/generator.rb'
|
|
97
|
+
Enabled: false
|
|
110
98
|
|
|
111
99
|
# Offense count: 15
|
|
112
100
|
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
|
|
@@ -130,14 +118,7 @@ Naming/PredicateMethod:
|
|
|
130
118
|
Exclude:
|
|
131
119
|
- 'lib/moxml/xpath/ruby/node.rb'
|
|
132
120
|
|
|
133
|
-
# Offense count:
|
|
134
|
-
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
135
|
-
# Configuration parameters: OnlySumOrWithInitialValue.
|
|
136
|
-
Performance/Sum:
|
|
137
|
-
Exclude:
|
|
138
|
-
- 'lib/moxml/xpath/compiler.rb'
|
|
139
|
-
|
|
140
|
-
# Offense count: 42
|
|
121
|
+
# Offense count: 46
|
|
141
122
|
# Configuration parameters: Prefixes, AllowedPatterns.
|
|
142
123
|
# Prefixes: when, with, without
|
|
143
124
|
RSpec/ContextWording:
|
|
@@ -145,11 +126,12 @@ RSpec/ContextWording:
|
|
|
145
126
|
- 'spec/integration/headed_ox_integration_spec.rb'
|
|
146
127
|
- 'spec/moxml/adapter/headed_ox_spec.rb'
|
|
147
128
|
- 'spec/moxml/adapter/shared_examples/adapter_contract.rb'
|
|
129
|
+
- 'spec/moxml/declaration_preservation_spec.rb'
|
|
148
130
|
- 'spec/moxml/xpath/lexer_spec.rb'
|
|
149
131
|
- 'spec/moxml/xpath/parser_spec.rb'
|
|
150
132
|
- 'spec/performance/benchmark_spec.rb'
|
|
151
133
|
|
|
152
|
-
# Offense count:
|
|
134
|
+
# Offense count: 15
|
|
153
135
|
# Configuration parameters: IgnoredMetadata.
|
|
154
136
|
RSpec/DescribeClass:
|
|
155
137
|
Exclude:
|
|
@@ -161,6 +143,7 @@ RSpec/DescribeClass:
|
|
|
161
143
|
- 'spec/consistency/adapter_parity_spec.rb'
|
|
162
144
|
- 'spec/integration/all_adapters_spec.rb'
|
|
163
145
|
- 'spec/integration/headed_ox_integration_spec.rb'
|
|
146
|
+
- 'spec/moxml/declaration_preservation_spec.rb'
|
|
164
147
|
- 'spec/moxml/error_spec.rb'
|
|
165
148
|
- 'spec/moxml/xpath/axes_spec.rb'
|
|
166
149
|
- 'spec/moxml/xpath/functions/boolean_functions_spec.rb'
|
|
@@ -173,7 +156,7 @@ RSpec/DescribeClass:
|
|
|
173
156
|
- 'spec/moxml/xpath_capabilities_spec.rb'
|
|
174
157
|
- 'spec/performance/xpath_benchmark_spec.rb'
|
|
175
158
|
|
|
176
|
-
# Offense count:
|
|
159
|
+
# Offense count: 217
|
|
177
160
|
# Configuration parameters: CountAsOne.
|
|
178
161
|
RSpec/ExampleLength:
|
|
179
162
|
Max: 85
|
|
@@ -197,9 +180,10 @@ RSpec/InstanceVariable:
|
|
|
197
180
|
Exclude:
|
|
198
181
|
- 'spec/moxml/sax_spec.rb'
|
|
199
182
|
|
|
200
|
-
# Offense count:
|
|
183
|
+
# Offense count: 7
|
|
201
184
|
RSpec/LeakyConstantDeclaration:
|
|
202
185
|
Exclude:
|
|
186
|
+
- 'spec/moxml/declaration_preservation_spec.rb'
|
|
203
187
|
- 'spec/moxml/sax_spec.rb'
|
|
204
188
|
|
|
205
189
|
# Offense count: 2
|
|
@@ -208,7 +192,7 @@ RSpec/LeakyConstantDeclaration:
|
|
|
208
192
|
RSpec/MessageSpies:
|
|
209
193
|
EnforcedStyle: receive
|
|
210
194
|
|
|
211
|
-
# Offense count:
|
|
195
|
+
# Offense count: 317
|
|
212
196
|
RSpec/MultipleExpectations:
|
|
213
197
|
Max: 10
|
|
214
198
|
|
|
@@ -217,6 +201,11 @@ RSpec/MultipleExpectations:
|
|
|
217
201
|
RSpec/MultipleMemoizedHelpers:
|
|
218
202
|
Max: 7
|
|
219
203
|
|
|
204
|
+
# Offense count: 10
|
|
205
|
+
# Configuration parameters: AllowedGroups.
|
|
206
|
+
RSpec/NestedGroups:
|
|
207
|
+
Max: 4
|
|
208
|
+
|
|
220
209
|
# Offense count: 3
|
|
221
210
|
# Configuration parameters: AllowedPatterns.
|
|
222
211
|
# AllowedPatterns: ^expect_, ^assert_
|
|
@@ -262,12 +251,6 @@ Security/Eval:
|
|
|
262
251
|
Exclude:
|
|
263
252
|
- 'spec/moxml/xpath/ruby/generator_spec.rb'
|
|
264
253
|
|
|
265
|
-
# Offense count: 1
|
|
266
|
-
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
267
|
-
Style/CombinableLoops:
|
|
268
|
-
Exclude:
|
|
269
|
-
- 'lib/moxml/adapter/customized_rexml/formatter.rb'
|
|
270
|
-
|
|
271
254
|
# Offense count: 1
|
|
272
255
|
Style/DocumentDynamicEvalDefinition:
|
|
273
256
|
Exclude:
|
data/docs/_config.yml
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Site settings
|
|
2
|
-
title: Moxml
|
|
2
|
+
title: "Moxml: Modern XML for Ruby"
|
|
3
3
|
description: >-
|
|
4
4
|
Modern XML processing for Ruby with unified adapter interface
|
|
5
5
|
baseurl: "/moxml"
|
|
6
|
-
url: "https://lutaml.
|
|
6
|
+
url: "https://www.lutaml.org"
|
|
7
7
|
|
|
8
8
|
# Theme
|
|
9
9
|
theme: just-the-docs
|
|
@@ -43,7 +43,7 @@ back_to_top_text: "Back to top"
|
|
|
43
43
|
# Footer content
|
|
44
44
|
footer_content: >-
|
|
45
45
|
Copyright © 2025 Ribose. Distributed by a
|
|
46
|
-
<a href="https://github.com/lutaml/moxml/blob/main/LICENSE.md">BSD-
|
|
46
|
+
<a href="https://github.com/lutaml/moxml/blob/main/LICENSE.md">BSD-3-Clause license</a>.
|
|
47
47
|
|
|
48
48
|
# Footer last edit timestamp
|
|
49
49
|
last_edit_timestamp: true
|
data/docs/_guides/index.adoc
CHANGED
|
@@ -12,6 +12,12 @@ Task-oriented guides for common XML processing operations with Moxml.
|
|
|
12
12
|
link:parsing-xml[Parsing XML]::
|
|
13
13
|
Parse XML from strings, files, and IO streams with different adapters.
|
|
14
14
|
|
|
15
|
+
link:sax-parsing[SAX parsing]::
|
|
16
|
+
Memory-efficient event-driven parsing for large XML files and streaming data.
|
|
17
|
+
|
|
18
|
+
link:xml-declaration[XML declaration preservation]::
|
|
19
|
+
Understand how Moxml preserves XML declarations for round-trip fidelity and standards compliance.
|
|
20
|
+
|
|
15
21
|
link:creating-documents[Creating documents]::
|
|
16
22
|
Build XML documents from scratch using the builder pattern or direct
|
|
17
23
|
manipulation.
|
|
@@ -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
|