or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

builders.mdentities.mdindex.mdjaxb.mdnamespaces.mdnavigation.mdparsing.mdstreaming.mdutilities.md

navigation.mddocs/

0

# XML Navigation

1

2

The GPathResult hierarchy provides XPath-like navigation through parsed XML documents from XmlSlurper, offering powerful querying and traversal capabilities with lazy evaluation.

3

4

## GPathResult

5

6

The base class for all XML navigation results, providing core navigation and querying functionality.

7

8

```java { .api }

9

public abstract class GPathResult implements Writable, Buildable, Iterable<GPathResult> {

10

// Navigation methods

11

public GPathResult parent();

12

public GPathResult children();

13

public GPathResult pop();

14

public String name();

15

public String lookupNamespace(String prefix);

16

17

// Content access

18

public String toString();

19

public abstract String text();

20

public abstract int size();

21

public boolean isEmpty();

22

23

// Type conversions

24

public Integer toInteger();

25

public Long toLong();

26

public Float toFloat();

27

public Double toDouble();

28

public BigDecimal toBigDecimal();

29

public BigInteger toBigInteger();

30

public URL toURL() throws MalformedURLException;

31

public URI toURI() throws URISyntaxException;

32

public Boolean toBoolean();

33

34

// Collection operations

35

public Object getAt(int index);

36

public Object getAt(IntRange range);

37

public void putAt(int index, Object newValue);

38

public List<GPathResult> list();

39

public Iterator<GPathResult> iterator();

40

public Iterator<GPathResult> depthFirst();

41

public Iterator<GPathResult> breadthFirst();

42

43

// Content modification (creates new structures)

44

public Object leftShift(Object newValue);

45

public Object plus(Object newValue);

46

public GPathResult declareNamespace(Map<String, String> newNamespaceMapping);

47

48

// Filtering and searching

49

public abstract GPathResult find(Closure closure);

50

public abstract GPathResult findAll(Closure closure);

51

52

// Utility methods

53

public void writeTo(Writer out) throws IOException;

54

public boolean asBoolean();

55

}

56

```

57

58

## Navigation Patterns

59

60

### Basic Element Navigation

61

62

```groovy

63

def slurper = new XmlSlurper()

64

def catalog = slurper.parseText('''

65

<catalog>

66

<books>

67

<book id="1">

68

<title>The Great Gatsby</title>

69

<author>F. Scott Fitzgerald</author>

70

<genres>

71

<genre>Classic</genre>

72

<genre>Fiction</genre>

73

</genres>

74

</book>

75

<book id="2">

76

<title>1984</title>

77

<author>George Orwell</author>

78

<genres>

79

<genre>Dystopian</genre>

80

<genre>Political</genre>

81

</genres>

82

</book>

83

</books>

84

</catalog>

85

''')

86

87

// Direct navigation

88

println catalog.books.book.title // All book titles

89

println catalog.books.book[0].title // First book title

90

println catalog.books.book.size() // Number of books

91

92

// Attribute access

93

println catalog.books.book.'@id' // All book IDs

94

println catalog.books.book[0].'@id' // First book's ID

95

96

// Nested navigation

97

println catalog.books.book.genres.genre // All genres from all books

98

println catalog.books.book[0].genres.genre.text() // Genres of first book

99

```

100

101

### Deep Navigation with ** Operator

102

103

```groovy

104

// Find all elements with a specific name anywhere in the tree

105

println catalog.'**'.findAll { it.name() == 'genre' } // All genre elements

106

println catalog.'**'.title // All titles at any level

107

println catalog.'**'.findAll { it.'@id' } // All elements with id attribute

108

109

// Complex deep searches

110

def allTextNodes = catalog.'**'.findAll {

111

it.text() && !it.children()

112

} // All leaf text nodes

113

114

def allElements = catalog.'**'.findAll {

115

it.name()

116

} // All elements (non-text nodes)

117

```

118

119

### Filtering and Searching

120

121

```groovy

122

// Find elements by attribute values

123

def book1 = catalog.books.book.find { it.'@id' == '1' }

124

def classicBooks = catalog.books.book.findAll {

125

it.genres.genre.find { it.text() == 'Classic' }

126

}

127

128

// Find by content

129

def orwellBooks = catalog.books.book.findAll {

130

it.author.text().contains('Orwell')

131

}

132

133

// Complex filtering

134

def longTitles = catalog.books.book.findAll {

135

it.title.text().length() > 10

136

}

137

138

// Combine filters

139

def fictionByFitzgerald = catalog.books.book.findAll { book ->

140

book.author.text().contains('Fitzgerald') &&

141

book.genres.genre.find { it.text() == 'Fiction' }

142

}

143

```

144

145

## GPathResult Implementations

146

147

### Node

148

149

Represents a single XML element in the navigation result.

150

151

```java { .api }

152

public class Node extends GPathResult {

153

public Node(Node parent, String name, Map<String, String> attributes,

154

Map<String, String> attributeNamespaces, Map<String, String> namespaceTagHints);

155

156

@Override

157

public String text();

158

@Override

159

public int size();

160

@Override

161

public GPathResult find(Closure closure);

162

@Override

163

public GPathResult findAll(Closure closure);

164

}

165

```

166

167

### NodeChild

168

169

Represents a child element accessed from a parent node.

170

171

```java { .api }

172

public class NodeChild extends GPathResult {

173

public NodeChild(Node node, GPathResult parent, String namespacePrefix,

174

Map<String, String> namespaceTagHints);

175

176

@Override

177

public String text();

178

@Override

179

public int size();

180

@Override

181

public GPathResult find(Closure closure);

182

@Override

183

public GPathResult findAll(Closure closure);

184

}

185

```

186

187

### NodeChildren

188

189

Represents a collection of child elements.

190

191

```java { .api }

192

public class NodeChildren extends GPathResult {

193

public NodeChildren(Node parent, String name, String namespacePrefix,

194

Map<String, String> namespaceTagHints);

195

196

@Override

197

public String text();

198

@Override

199

public int size();

200

@Override

201

public GPathResult find(Closure closure);

202

@Override

203

public GPathResult findAll(Closure closure);

204

}

205

```

206

207

### Attributes

208

209

Provides access to XML attributes as navigable results.

210

211

```java { .api }

212

public class Attributes extends GPathResult {

213

public Attributes(Node parent, String name, String namespacePrefix,

214

Map<String, String> namespaceTagHints);

215

216

@Override

217

public String text();

218

@Override

219

public int size();

220

@Override

221

public GPathResult find(Closure closure);

222

@Override

223

public GPathResult findAll(Closure closure);

224

}

225

```

226

227

## Advanced Navigation Examples

228

229

### Iterating Through Results

230

231

```groovy

232

// Iterate over all books

233

catalog.books.book.each { book ->

234

println "Book: ${book.title} by ${book.author}"

235

println "ID: ${book.'@id'}"

236

book.genres.genre.each { genre ->

237

println " Genre: ${genre}"

238

}

239

}

240

241

// Using iterator explicitly

242

def bookIterator = catalog.books.book.iterator()

243

while (bookIterator.hasNext()) {

244

def book = bookIterator.next()

245

println book.title.text()

246

}

247

248

// Depth-first traversal

249

catalog.depthFirst().each { node ->

250

if (node.text() && !node.children()) {

251

println "Leaf node: ${node.name()} = ${node.text()}"

252

}

253

}

254

```

255

256

### Content Type Conversions

257

258

```groovy

259

def prices = slurper.parseText('''

260

<products>

261

<product price="12.99">Book</product>

262

<product price="25.50">Magazine</product>

263

<product active="true">Subscription</product>

264

<product count="42">Bundle</product>

265

</products>

266

''')

267

268

// Convert to different types

269

prices.product.each { product ->

270

if (product.'@price') {

271

def price = product.'@price'.toDouble()

272

println "Price: \$${price}"

273

}

274

if (product.'@active') {

275

def active = product.'@active'.toBoolean()

276

println "Active: ${active}"

277

}

278

if (product.'@count') {

279

def count = product.'@count'.toInteger()

280

println "Count: ${count}"

281

}

282

}

283

```

284

285

### Working with Namespaces

286

287

```groovy

288

def nsXml = '''

289

<root xmlns:book="http://example.com/book"

290

xmlns:author="http://example.com/author">

291

<book:catalog>

292

<book:item>

293

<book:title>Sample Book</book:title>

294

<author:name>John Doe</author:name>

295

</book:item>

296

</book:catalog>

297

</root>

298

'''

299

300

def nsResult = slurper.parseText(nsXml)

301

302

// Access namespaced elements

303

println nsResult.'book:catalog'.'book:item'.'book:title'

304

println nsResult.'book:catalog'.'book:item'.'author:name'

305

306

// Declare namespace mappings for easier access

307

def mapped = nsResult.declareNamespace([

308

'b': 'http://example.com/book',

309

'a': 'http://example.com/author'

310

])

311

312

println mapped.'b:catalog'.'b:item'.'b:title'

313

println mapped.'b:catalog'.'b:item'.'a:name'

314

315

// Look up namespace URIs

316

println nsResult.lookupNamespace('book') // "http://example.com/book"

317

println nsResult.lookupNamespace('author') // "http://example.com/author"

318

```

319

320

### Complex Queries

321

322

```groovy

323

def library = slurper.parseText('''

324

<library>

325

<section name="Fiction">

326

<book rating="4.5" available="true">

327

<title>The Great Gatsby</title>

328

<author>F. Scott Fitzgerald</author>

329

<year>1925</year>

330

</book>

331

<book rating="4.8" available="false">

332

<title>To Kill a Mockingbird</title>

333

<author>Harper Lee</author>

334

<year>1960</year>

335

</book>

336

</section>

337

<section name="Science">

338

<book rating="4.2" available="true">

339

<title>A Brief History of Time</title>

340

<author>Stephen Hawking</author>

341

<year>1988</year>

342

</book>

343

</section>

344

</library>

345

''')

346

347

// Complex filtering with multiple conditions

348

def highRatedAvailableBooks = library.section.book.findAll { book ->

349

book.'@rating'.toDouble() > 4.0 &&

350

book.'@available'.toBoolean()

351

}

352

353

highRatedAvailableBooks.each { book ->

354

println "${book.title} (${book.'@rating'}) - Available"

355

}

356

357

// Find books published after a certain year

358

def modernBooks = library.'**'.book.findAll {

359

it.year.toInteger() > 1950

360

}

361

362

// Get all unique authors

363

def authors = library.'**'.author.text().unique()

364

println "Authors: ${authors.join(', ')}"

365

366

// Calculate average rating

367

def ratings = library.'**'.book.'@rating'*.toDouble()

368

def avgRating = ratings.sum() / ratings.size()

369

println "Average rating: ${avgRating}"

370

```

371

372

## Performance Considerations

373

374

GPathResult uses lazy evaluation, which provides several benefits:

375

376

```groovy

377

// Lazy evaluation - elements are only processed when accessed

378

def largeDoc = slurper.parseText(veryLargeXmlString)

379

def firstMatch = largeDoc.'**'.findAll { it.name() == 'target' }[0]

380

// Only processes until first match is found

381

382

// Memory efficient navigation

383

largeDoc.section.each { section ->

384

// Process one section at a time without loading entire document into memory

385

processSection(section)

386

}

387

388

// Avoid unnecessary iterations

389

def hasErrors = largeDoc.'**'.find { it.name() == 'error' }

390

if (hasErrors) {

391

// Only searches until first error is found

392

handleErrors()

393

}

394

```

395

396

## Working with Large Documents

397

398

```groovy

399

// Streaming-like processing with slurper

400

def processLargeDocument(xmlFile) {

401

def slurper = new XmlSlurper()

402

def doc = slurper.parse(xmlFile)

403

404

// Process in chunks to manage memory

405

doc.dataSection.records.record.each { record ->

406

if (record.'@type' == 'important') {

407

processImportantRecord(record)

408

}

409

}

410

}

411

412

// Selective loading

413

def loadOnlyRequired = { xmlFile ->

414

def slurper = new XmlSlurper()

415

def doc = slurper.parse(xmlFile)

416

417

// Only load what's needed

418

return doc.configuration.findAll { it.'@enabled' == 'true' }

419

}

420

```