0
# Namespace Support
1
2
Groovy XML provides comprehensive namespace support for creating, parsing, and navigating XML documents with proper namespace handling, including namespace-aware builders, parsers, and navigation tools.
3
4
## Namespace Class
5
6
Core class for managing XML namespaces and creating qualified names.
7
8
```java { .api }
9
public class Namespace {
10
// Constructors
11
public Namespace();
12
public Namespace(String uri);
13
public Namespace(String uri, String prefix);
14
15
// Core methods
16
public QName get(String localName);
17
public String getPrefix();
18
public String getUri();
19
20
// Utility methods
21
@Override
22
public boolean equals(Object obj);
23
@Override
24
public int hashCode();
25
@Override
26
public String toString();
27
}
28
```
29
30
### Namespace Usage
31
32
```groovy
33
// Create namespaces
34
def defaultNs = new Namespace('http://example.com/default')
35
def metaNs = new Namespace('http://example.com/metadata', 'meta')
36
def dataNs = new Namespace('http://example.com/data', 'data')
37
38
// Create qualified names
39
def titleQName = metaNs.get('title')
40
def itemQName = dataNs.get('item')
41
def rootQName = defaultNs.get('root')
42
43
println titleQName // {http://example.com/metadata}title
44
println itemQName // {http://example.com/data}item
45
46
// Use in builders
47
def xml = new MarkupBuilder(writer)
48
xml."${rootQName}" {
49
"${titleQName}"('Document Title')
50
"${itemQName}"(id: '1', 'Item content')
51
}
52
```
53
54
## QName Support
55
56
QName class for representing qualified XML names with namespace information.
57
58
```java { .api }
59
public class QName {
60
// Constructors
61
public QName(String localName);
62
public QName(String namespaceURI, String localName);
63
public QName(String namespaceURI, String localName, String prefix);
64
65
// Accessor methods
66
public String getLocalName();
67
public String getNamespaceURI();
68
public String getPrefix();
69
70
// Utility methods
71
@Override
72
public boolean equals(Object obj);
73
@Override
74
public int hashCode();
75
@Override
76
public String toString();
77
}
78
```
79
80
### QName Usage
81
82
```groovy
83
// Create QNames directly
84
def qname1 = new QName('title')
85
def qname2 = new QName('http://example.com/ns', 'title')
86
def qname3 = new QName('http://example.com/ns', 'title', 'ns')
87
88
// Access QName properties
89
println qname3.localName // "title"
90
println qname3.namespaceURI // "http://example.com/ns"
91
println qname3.prefix // "ns"
92
93
// Use in XML operations
94
def builder = new MarkupBuilder(writer)
95
builder."${qname3}" {
96
content('Namespaced content')
97
}
98
```
99
100
## Namespace-Aware Building
101
102
### MarkupBuilder with Namespaces
103
104
```groovy
105
def xml = new MarkupBuilder(writer)
106
107
// Method 1: Explicit namespace declarations
108
xml.root(
109
'xmlns': 'http://example.com/default',
110
'xmlns:meta': 'http://example.com/metadata',
111
'xmlns:data': 'http://example.com/data'
112
) {
113
'meta:header' {
114
'meta:title'('Document Title')
115
'meta:created'(new Date().toString())
116
}
117
118
'data:content' {
119
'data:item'(id: '1') {
120
'data:name'('Item 1')
121
'data:value'('Value 1')
122
}
123
'data:item'(id: '2') {
124
'data:name'('Item 2')
125
'data:value'('Value 2')
126
}
127
}
128
}
129
130
// Method 2: Using Namespace objects
131
def defaultNs = new Namespace('http://example.com/default')
132
def metaNs = new Namespace('http://example.com/metadata', 'meta')
133
def dataNs = new Namespace('http://example.com/data', 'data')
134
135
xml."${defaultNs.get('document')}"(
136
'xmlns': defaultNs.uri,
137
'xmlns:meta': metaNs.uri,
138
'xmlns:data': dataNs.uri
139
) {
140
"${metaNs.get('metadata')}" {
141
"${metaNs.get('title')}"('Namespace Document')
142
}
143
144
"${dataNs.get('records')}" {
145
(1..3).each { i ->
146
"${dataNs.get('record')}"(id: i) {
147
"${dataNs.get('value')}"("Record ${i}")
148
}
149
}
150
}
151
}
152
```
153
154
### StreamingMarkupBuilder with Namespaces
155
156
```groovy
157
def smb = new StreamingMarkupBuilder()
158
def content = smb.bind {
159
mkp.xmlDeclaration(version: '1.0', encoding: 'UTF-8')
160
161
// Define namespace mappings
162
namespaces = [
163
'': 'http://example.com/default',
164
'meta': 'http://example.com/metadata',
165
'data': 'http://example.com/data',
166
'custom': 'http://example.com/custom'
167
]
168
169
document(
170
xmlns: namespaces[''],
171
'xmlns:meta': namespaces['meta'],
172
'xmlns:data': namespaces['data'],
173
'xmlns:custom': namespaces['custom']
174
) {
175
176
'meta:header' {
177
'meta:title'('Streaming Namespace Document')
178
'meta:generator'('Groovy StreamingMarkupBuilder')
179
}
180
181
'data:body' {
182
(1..100).each { i ->
183
'data:record'(id: i, 'custom:priority': i % 3) {
184
'data:content'("Content ${i}")
185
'custom:metadata' {
186
'custom:category'("Category ${i % 5}")
187
'custom:tags' {
188
(1..(i % 3 + 1)).each { j ->
189
'custom:tag'("tag${j}")
190
}
191
}
192
}
193
}
194
}
195
}
196
}
197
}
198
199
content.writeTo(new FileWriter('namespaced-streaming.xml'))
200
```
201
202
## Namespace-Aware Parsing
203
204
### XmlParser with Namespaces
205
206
```groovy
207
// Create namespace-aware parser
208
def parser = new XmlParser(false, true) // not validating, namespace aware
209
def nsXml = '''<?xml version="1.0" encoding="UTF-8"?>
210
<root xmlns="http://example.com/default"
211
xmlns:meta="http://example.com/metadata"
212
xmlns:data="http://example.com/data">
213
<meta:header>
214
<meta:title>Sample Document</meta:title>
215
<meta:version>1.0</meta:version>
216
</meta:header>
217
<data:content>
218
<data:item id="1">
219
<data:name>Item 1</data:name>
220
<data:value>Value 1</data:value>
221
</data:item>
222
<data:item id="2">
223
<data:name>Item 2</data:name>
224
<data:value>Value 2</data:value>
225
</data:item>
226
</data:content>
227
</root>'''
228
229
def root = parser.parseText(nsXml)
230
231
// Access namespaced elements
232
println root.name() // Will include namespace information
233
def header = root['meta:header'][0]
234
def title = header['meta:title'][0].text()
235
def items = root['data:content'][0]['data:item']
236
237
items.each { item ->
238
def name = item['data:name'][0].text()
239
def value = item['data:value'][0].text()
240
println "Item: ${name} = ${value}"
241
}
242
243
// Check namespace information
244
def itemNode = items[0]
245
println "Namespace URI: ${itemNode.namespaceURI}"
246
println "Local name: ${itemNode.localName}"
247
```
248
249
### XmlSlurper with Namespaces
250
251
```groovy
252
// Create namespace-aware slurper
253
def slurper = new XmlSlurper(false, true) // not validating, namespace aware
254
def nsDoc = slurper.parseText(nsXml)
255
256
// Navigate with namespace prefixes
257
println nsDoc.'meta:header'.'meta:title'.text()
258
println nsDoc.'data:content'.'data:item'.size()
259
260
// Access all items
261
nsDoc.'data:content'.'data:item'.each { item ->
262
println "ID: ${item.'@id'}"
263
println "Name: ${item.'data:name'.text()}"
264
println "Value: ${item.'data:value'.text()}"
265
}
266
267
// Declare namespace mappings for easier access
268
def mappedDoc = nsDoc.declareNamespace([
269
'default': 'http://example.com/default',
270
'm': 'http://example.com/metadata',
271
'd': 'http://example.com/data'
272
])
273
274
// Use shorter prefixes
275
println mappedDoc.'m:header'.'m:title'.text()
276
println mappedDoc.'d:content'.'d:item'.'d:name'.text()
277
278
// Look up namespace URIs
279
println nsDoc.lookupNamespace('meta') // "http://example.com/metadata"
280
println nsDoc.lookupNamespace('data') // "http://example.com/data"
281
```
282
283
## NamespaceBuilder Support
284
285
### NamespaceBuilder
286
287
Builder with enhanced namespace support for complex namespace scenarios.
288
289
```java { .api }
290
public class NamespaceBuilder extends NamespaceBuilderSupport {
291
// Constructors
292
public NamespaceBuilder();
293
public NamespaceBuilder(Writer writer);
294
295
// Namespace-aware building methods inherited from parent
296
}
297
```
298
299
### NamespaceBuilderSupport
300
301
Base class providing namespace-aware building functionality.
302
303
```java { .api }
304
public abstract class NamespaceBuilderSupport extends BuilderSupport {
305
// Namespace management
306
protected Map<String, String> namespaceMethodMap;
307
protected boolean autoPrefix;
308
309
// Configuration methods
310
public void setAutoPrefix(boolean autoPrefix);
311
public boolean getAutoPrefix();
312
313
// Namespace declaration methods
314
public void declareNamespace(String prefix, String namespaceURI);
315
public void declareNamespace(Map<String, String> namespaceMap);
316
}
317
```
318
319
### NamespaceBuilder Usage
320
321
```groovy
322
def nsBuilder = new NamespaceBuilder(writer)
323
324
// Configure automatic prefixing
325
nsBuilder.setAutoPrefix(true)
326
327
// Declare namespaces
328
nsBuilder.declareNamespace('meta', 'http://example.com/metadata')
329
nsBuilder.declareNamespace('data', 'http://example.com/data')
330
331
// Build with automatic namespace handling
332
nsBuilder.root {
333
'meta:information' {
334
'meta:title'('Auto-prefixed Document')
335
'meta:description'('Demonstrates automatic namespace handling')
336
}
337
338
'data:records' {
339
record(id: '1') {
340
'data:content'('Content 1')
341
}
342
record(id: '2') {
343
'data:content'('Content 2')
344
}
345
}
346
}
347
```
348
349
## Advanced Namespace Patterns
350
351
### Dynamic Namespace Assignment
352
353
```groovy
354
def createNamespacedDocument = { namespaceMap, data ->
355
def smb = new StreamingMarkupBuilder()
356
357
smb.bind {
358
mkp.xmlDeclaration(version: '1.0', encoding: 'UTF-8')
359
360
// Create root with all namespace declarations
361
def rootAttrs = [:]
362
namespaceMap.each { prefix, uri ->
363
def attrName = prefix ? "xmlns:${prefix}" : 'xmlns'
364
rootAttrs[attrName] = uri
365
}
366
367
document(rootAttrs) {
368
// Use namespaces dynamically
369
data.each { section ->
370
def prefix = section.namespace ?: ''
371
def elementName = prefix ? "${prefix}:${section.name}" : section.name
372
373
"${elementName}"(section.attributes ?: [:]) {
374
section.children?.each { child ->
375
def childPrefix = child.namespace ?: ''
376
def childName = childPrefix ? "${childPrefix}:${child.name}" : child.name
377
"${childName}"(child.content)
378
}
379
}
380
}
381
}
382
}
383
}
384
385
// Usage
386
def namespaces = [
387
'': 'http://example.com/default',
388
'meta': 'http://example.com/metadata',
389
'data': 'http://example.com/data'
390
]
391
392
def documentData = [
393
[name: 'header', namespace: 'meta', children: [
394
[name: 'title', content: 'Dynamic Document'],
395
[name: 'version', content: '1.0']
396
]],
397
[name: 'content', namespace: 'data', children: [
398
[name: 'item', content: 'Item 1'],
399
[name: 'item', content: 'Item 2']
400
]]
401
]
402
403
def result = createNamespacedDocument(namespaces, documentData)
404
result.writeTo(new FileWriter('dynamic-namespaces.xml'))
405
```
406
407
### Namespace Validation and Resolution
408
409
```groovy
410
class NamespaceValidator {
411
412
static boolean validateNamespaces(GPathResult doc) {
413
def namespaces = [:]
414
415
// Collect all namespace declarations
416
doc.depthFirst().each { node ->
417
node.attributes().each { name, value ->
418
if (name.startsWith('xmlns')) {
419
def prefix = name == 'xmlns' ? '' : name.substring(6)
420
namespaces[prefix] = value
421
}
422
}
423
}
424
425
// Validate all prefixed elements have declarations
426
def valid = true
427
doc.depthFirst().each { node ->
428
def nodeName = node.name()
429
if (nodeName.contains(':')) {
430
def prefix = nodeName.split(':')[0]
431
if (!namespaces.containsKey(prefix)) {
432
println "Undeclared namespace prefix: ${prefix}"
433
valid = false
434
}
435
}
436
}
437
438
return valid
439
}
440
441
static Map<String, String> extractNamespaces(GPathResult doc) {
442
def namespaces = [:]
443
444
doc.depthFirst().each { node ->
445
node.attributes().each { name, value ->
446
if (name.startsWith('xmlns')) {
447
def prefix = name == 'xmlns' ? '' : name.substring(6)
448
namespaces[prefix] = value
449
}
450
}
451
}
452
453
return namespaces
454
}
455
}
456
457
// Usage
458
def slurper = new XmlSlurper(false, true)
459
def doc = slurper.parseText(namespacedXml)
460
461
def isValid = NamespaceValidator.validateNamespaces(doc)
462
def namespaces = NamespaceValidator.extractNamespaces(doc)
463
464
println "Document valid: ${isValid}"
465
println "Namespaces: ${namespaces}"
466
```
467
468
### Namespace-Aware Transformation
469
470
```groovy
471
def transformWithNamespaces = { inputXml, transformationRules ->
472
def slurper = new XmlSlurper(false, true)
473
def doc = slurper.parseText(inputXml)
474
475
def smb = new StreamingMarkupBuilder()
476
def result = smb.bind {
477
mkp.xmlDeclaration(version: '1.0', encoding: 'UTF-8')
478
479
// Extract and preserve namespaces
480
def nsDeclarations = [:]
481
doc.attributes().each { name, value ->
482
if (name.toString().startsWith('xmlns')) {
483
nsDeclarations[name] = value
484
}
485
}
486
487
transformedDocument(nsDeclarations) {
488
// Apply transformation rules while preserving namespaces
489
doc.children().each { child ->
490
def childName = child.name()
491
def rule = transformationRules[childName]
492
493
if (rule) {
494
"${rule.newName ?: childName}"(rule.newAttributes ?: [:]) {
495
if (rule.transform) {
496
rule.transform(child)
497
} else {
498
// Copy child content preserving namespaces
499
child.children().each { grandchild ->
500
"${grandchild.name()}"(grandchild.text())
501
}
502
}
503
}
504
}
505
}
506
}
507
}
508
509
return result
510
}
511
512
// Usage
513
def rules = [
514
'meta:header': [
515
newName: 'header',
516
transform: { node ->
517
'title'(node.'meta:title'.text().toUpperCase())
518
'processed'(new Date().toString())
519
}
520
],
521
'data:content': [
522
newName: 'processedData',
523
transform: { node ->
524
node.'data:item'.each { item ->
525
processedItem(id: item.'@id') {
526
originalName(item.'data:name'.text())
527
processedValue(item.'data:value'.text().reverse())
528
}
529
}
530
}
531
]
532
]
533
534
def transformed = transformWithNamespaces(namespacedXml, rules)
535
transformed.writeTo(new FileWriter('transformed.xml'))
536
```
537
538
## Namespace Best Practices
539
540
```groovy
541
class NamespaceBestPractices {
542
543
// Always use meaningful namespace URIs
544
static final Map<String, String> STANDARD_NAMESPACES = [
545
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
546
'xs': 'http://www.w3.org/2001/XMLSchema',
547
'xml': 'http://www.w3.org/XML/1998/namespace'
548
]
549
550
static void declareStandardNamespaces(MarkupBuilder builder) {
551
// Always declare standard namespaces when needed
552
STANDARD_NAMESPACES.each { prefix, uri ->
553
builder."xmlns:${prefix}" = uri
554
}
555
}
556
557
static String createNamespaceURI(String organization, String component, String version = null) {
558
def uri = "http://${organization}/schemas/${component}"
559
return version ? "${uri}/${version}" : uri
560
}
561
562
static boolean isValidNamespaceURI(String uri) {
563
// Basic validation for namespace URIs
564
return uri ==~ /^https?:\/\/.+/ || uri ==~ /^urn:.+/
565
}
566
567
static void validateAndBuild(Map<String, String> namespaces, Closure xmlContent) {
568
// Validate all namespace URIs before building
569
namespaces.each { prefix, uri ->
570
if (!isValidNamespaceURI(uri)) {
571
throw new IllegalArgumentException("Invalid namespace URI: ${uri}")
572
}
573
}
574
575
def smb = new StreamingMarkupBuilder()
576
def result = smb.bind {
577
mkp.xmlDeclaration(version: '1.0', encoding: 'UTF-8')
578
579
def rootAttrs = [:]
580
namespaces.each { prefix, uri ->
581
def attrName = prefix ? "xmlns:${prefix}" : 'xmlns'
582
rootAttrs[attrName] = uri
583
}
584
585
document(rootAttrs, xmlContent)
586
}
587
588
return result
589
}
590
}
591
```