or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdcore-api.mddocument-creation.mdindex.mdio-operations.mdxpath.md

xpath.mddocs/

0

# DOM4J XPath Support

1

2

DOM4J provides comprehensive XPath 1.0 support through integration with the Jaxen XPath library. This section covers XPath expressions, pattern matching, namespace support, and advanced querying capabilities for powerful document navigation and content selection.

3

4

## XPath Interface

5

6

The XPath interface represents compiled XPath expressions that can be executed against DOM4J documents and nodes. XPath expressions are compiled once and can be reused for efficient querying.

7

8

### Package and Import

9

```java { .api }

10

import org.dom4j.XPath;

11

import org.dom4j.Node;

12

import org.dom4j.DocumentHelper;

13

import org.dom4j.InvalidXPathException;

14

import org.jaxen.VariableContext;

15

import org.jaxen.FunctionContext;

16

import org.jaxen.NamespaceContext;

17

```

18

19

### XPath Interface Methods

20

```java { .api }

21

public interface XPath extends NodeFilter {

22

// Expression text

23

String getText();

24

25

// Node filtering (from NodeFilter)

26

boolean matches(Node node);

27

28

// Expression evaluation

29

Object evaluate(Object context);

30

31

// Node selection

32

List<Node> selectNodes(Object context);

33

List<Node> selectNodes(Object context, XPath sortXPath);

34

List<Node> selectNodes(Object context, XPath sortXPath, boolean distinct);

35

Node selectSingleNode(Object context);

36

37

// Value extraction

38

String valueOf(Object context);

39

Number numberValueOf(Object context);

40

boolean booleanValueOf(Object context);

41

42

// Result sorting

43

void sort(List<Node> list);

44

void sort(List<Node> list, boolean distinct);

45

46

// Context configuration

47

FunctionContext getFunctionContext();

48

void setFunctionContext(FunctionContext functionContext);

49

NamespaceContext getNamespaceContext();

50

void setNamespaceContext(NamespaceContext namespaceContext);

51

void setNamespaceURIs(Map<String, String> map);

52

VariableContext getVariableContext();

53

void setVariableContext(VariableContext variableContext);

54

}

55

```

56

57

### Creating XPath Expressions

58

```java { .api }

59

// Create XPath expressions

60

XPath elementXPath = DocumentHelper.createXPath("//element");

61

XPath attributeXPath = DocumentHelper.createXPath("//@attribute");

62

XPath specificXPath = DocumentHelper.createXPath("//book[@isbn='123-456-789']");

63

64

// With namespace support

65

XPath namespacedXPath = DocumentHelper.createXPath("//ns:element/@ns:attribute");

66

namespacedXPath.setNamespaceURIs(Map.of("ns", "http://example.com/namespace"));

67

68

// With variable context

69

VariableContext variables = new SimpleVariableContext();

70

variables.setVariableValue("id", "P123");

71

XPath variableXPath = DocumentHelper.createXPath("//product[@id=$id]", variables);

72

```

73

74

### Basic XPath Operations

75

```java { .api }

76

Document document = createSampleDocument();

77

78

// Select multiple nodes

79

XPath booksXPath = DocumentHelper.createXPath("//book");

80

List<Node> books = booksXPath.selectNodes(document);

81

82

for (Node bookNode : books) {

83

Element book = (Element) bookNode;

84

System.out.println("Book: " + book.attributeValue("title"));

85

}

86

87

// Select single node

88

XPath firstBookXPath = DocumentHelper.createXPath("//book[1]");

89

Node firstBook = firstBookXPath.selectSingleNode(document);

90

91

// Extract values

92

XPath titleXPath = DocumentHelper.createXPath("//book[1]/title/text()");

93

String title = titleXPath.valueOf(document);

94

95

XPath priceXPath = DocumentHelper.createXPath("//book[1]/@price");

96

Number price = priceXPath.numberValueOf(document);

97

98

// Boolean evaluation

99

XPath hasIsbnXPath = DocumentHelper.createXPath("//book[@isbn]");

100

boolean hasIsbn = hasIsbnXPath.booleanValueOf(document);

101

102

// Filter nodes

103

XPath expensiveBooksXPath = DocumentHelper.createXPath("//book[@price > 50]");

104

List<Node> expensiveBooks = expensiveBooksXPath.selectNodes(document);

105

```

106

107

## DefaultXPath Implementation

108

109

DefaultXPath is the concrete implementation of the XPath interface, providing Jaxen-based XPath processing.

110

111

### Package and Import

112

```java { .api }

113

import org.dom4j.xpath.DefaultXPath;

114

```

115

116

### Advanced XPath Usage

117

```java { .api }

118

// Direct DefaultXPath creation

119

XPath xpath = new DefaultXPath("//book[position() > 1]");

120

121

// Complex XPath expressions

122

XPath complexXPath = new DefaultXPath(

123

"//book[author='John Doe' and @price < 100]/title/text()");

124

125

List<Node> results = complexXPath.selectNodes(document);

126

for (Node result : results) {

127

System.out.println("Title: " + result.getText());

128

}

129

130

// XPath with functions

131

XPath functionXPath = new DefaultXPath(

132

"//book[contains(title, 'XML') and string-length(@isbn) = 13]");

133

134

// Numerical operations

135

XPath mathXPath = new DefaultXPath("sum(//book/@price)");

136

Number totalPrice = mathXPath.numberValueOf(document);

137

138

XPath countXPath = new DefaultXPath("count(//book)");

139

Number bookCount = countXPath.numberValueOf(document);

140

```

141

142

### Namespace-Aware XPath

143

```java { .api }

144

// Document with namespaces

145

String namespaceXML = """

146

<catalog xmlns:books="http://example.com/books"

147

xmlns:meta="http://example.com/metadata">

148

<books:book meta:id="1">

149

<books:title>XML Guide</books:title>

150

<books:author>Jane Smith</books:author>

151

</books:book>

152

</catalog>

153

""";

154

155

Document nsDocument = DocumentHelper.parseText(namespaceXML);

156

157

// Create namespace-aware XPath

158

XPath nsXPath = DocumentHelper.createXPath("//books:book[@meta:id='1']/books:title");

159

160

// Configure namespaces

161

Map<String, String> namespaces = Map.of(

162

"books", "http://example.com/books",

163

"meta", "http://example.com/metadata"

164

);

165

nsXPath.setNamespaceURIs(namespaces);

166

167

// Execute namespace-aware query

168

Node titleNode = nsXPath.selectSingleNode(nsDocument);

169

String title = titleNode.getText(); // "XML Guide"

170

171

// Alternative namespace configuration

172

DefaultNamespaceContext nsContext = new DefaultNamespaceContext();

173

nsContext.addNamespace("b", "http://example.com/books");

174

nsContext.addNamespace("m", "http://example.com/metadata");

175

176

XPath contextXPath = new DefaultXPath("//b:book[@m:id='1']");

177

contextXPath.setNamespaceContext(nsContext);

178

```

179

180

## XPath Expressions and Syntax

181

182

### Node Selection Patterns

183

```java { .api }

184

// Axis-based selection

185

XPath childXPath = DocumentHelper.createXPath("/catalog/child::book");

186

XPath descendantXPath = DocumentHelper.createXPath("//descendant::title");

187

XPath followingXPath = DocumentHelper.createXPath("//book[1]/following-sibling::book");

188

XPath parentXPath = DocumentHelper.createXPath("//title/parent::book");

189

190

// Attribute selection

191

XPath allAttributesXPath = DocumentHelper.createXPath("//book/@*");

192

XPath specificAttrXPath = DocumentHelper.createXPath("//book/@isbn");

193

194

// Text node selection

195

XPath allTextXPath = DocumentHelper.createXPath("//text()");

196

XPath titleTextXPath = DocumentHelper.createXPath("//title/text()");

197

198

// Mixed content

199

XPath mixedXPath = DocumentHelper.createXPath("//description//text()");

200

```

201

202

### Predicates and Filtering

203

```java { .api }

204

// Position-based predicates

205

XPath firstXPath = DocumentHelper.createXPath("//book[1]");

206

XPath lastXPath = DocumentHelper.createXPath("//book[last()]");

207

XPath secondToLastXPath = DocumentHelper.createXPath("//book[last()-1]");

208

XPath positionRangeXPath = DocumentHelper.createXPath("//book[position() > 2 and position() < 5]");

209

210

// Attribute-based predicates

211

XPath hasAttributeXPath = DocumentHelper.createXPath("//book[@isbn]");

212

XPath attributeValueXPath = DocumentHelper.createXPath("//book[@category='fiction']");

213

XPath attributeComparisonXPath = DocumentHelper.createXPath("//book[@price > 25.00]");

214

215

// Element content predicates

216

XPath hasElementXPath = DocumentHelper.createXPath("//book[author]");

217

XPath elementValueXPath = DocumentHelper.createXPath("//book[author='John Doe']");

218

XPath elementComparisonXPath = DocumentHelper.createXPath("//book[price > 20]");

219

220

// Complex logical predicates

221

XPath complexXPath = DocumentHelper.createXPath(

222

"//book[@category='technical' and (author='Smith' or author='Jones') and @price < 50]");

223

```

224

225

### XPath Functions

226

```java { .api }

227

// String functions

228

XPath containsXPath = DocumentHelper.createXPath("//book[contains(title, 'XML')]");

229

XPath startsWithXPath = DocumentHelper.createXPath("//book[starts-with(title, 'Advanced')]");

230

XPath substringXPath = DocumentHelper.createXPath("//book[substring(isbn, 1, 3) = '978']");

231

XPath normalizeXPath = DocumentHelper.createXPath("//book[normalize-space(title) != '']");

232

233

// Numeric functions

234

XPath sumXPath = DocumentHelper.createXPath("sum(//book/@price)");

235

XPath countXPath = DocumentHelper.createXPath("count(//book[@category='fiction'])");

236

XPath avgXPath = DocumentHelper.createXPath("sum(//book/@price) div count(//book)");

237

XPath minMaxXPath = DocumentHelper.createXPath("//book[@price = //book/@price[not(. > //book/@price)]]");

238

239

// Boolean functions

240

XPath notXPath = DocumentHelper.createXPath("//book[not(@out-of-print)]");

241

XPath trueXPath = DocumentHelper.createXPath("//book[true()]");

242

243

// Node set functions

244

XPath localNameXPath = DocumentHelper.createXPath("//book[local-name() = 'book']");

245

XPath namespaceUriXPath = DocumentHelper.createXPath("//*[namespace-uri() = 'http://example.com/books']");

246

```

247

248

## Variable and Function Contexts

249

250

### Variable Context

251

```java { .api }

252

import org.jaxen.VariableContext;

253

import org.jaxen.SimpleVariableContext;

254

255

// Simple variable context

256

SimpleVariableContext variables = new SimpleVariableContext();

257

variables.setVariableValue("category", "fiction");

258

variables.setVariableValue("maxPrice", 30.0);

259

variables.setVariableValue("author", "Smith");

260

261

XPath variableXPath = DocumentHelper.createXPath(

262

"//book[@category=$category and @price <= $maxPrice and author=$author]");

263

variableXPath.setVariableContext(variables);

264

265

List<Node> results = variableXPath.selectNodes(document);

266

267

// Custom variable context

268

class CustomVariableContext implements VariableContext {

269

private final Map<String, Object> variables = new HashMap<>();

270

271

public CustomVariableContext() {

272

variables.put("today", LocalDate.now().toString());

273

variables.put("userId", getCurrentUserId());

274

}

275

276

@Override

277

public Object getVariableValue(String namespaceURI, String prefix, String localName) {

278

return variables.get(localName);

279

}

280

281

public void setVariable(String name, Object value) {

282

variables.put(name, value);

283

}

284

}

285

286

CustomVariableContext customVars = new CustomVariableContext();

287

customVars.setVariable("status", "active");

288

289

XPath customXPath = DocumentHelper.createXPath("//item[@status=$status and @lastUpdate >= $today]");

290

customXPath.setVariableContext(customVars);

291

```

292

293

### Function Context

294

```java { .api }

295

import org.jaxen.FunctionContext;

296

import org.jaxen.Function;

297

import org.jaxen.Context;

298

import org.jaxen.function.StringFunction;

299

300

// Custom function implementation

301

class UpperCaseFunction implements Function {

302

@Override

303

public Object call(Context context, List args) throws Exception {

304

if (args.size() != 1) {

305

throw new Exception("upper-case() requires exactly 1 argument");

306

}

307

308

String value = StringFunction.evaluate(args.get(0), context.getNavigator());

309

return value.toUpperCase();

310

}

311

}

312

313

// Register custom function

314

FunctionContext functionContext = XPathFunctionContext.getInstance();

315

functionContext = new CustomFunctionContext(functionContext);

316

((CustomFunctionContext) functionContext).registerFunction(null, "upper-case", new UpperCaseFunction());

317

318

// Use custom function in XPath

319

XPath functionXPath = DocumentHelper.createXPath("//book[upper-case(title) = 'XML GUIDE']");

320

functionXPath.setFunctionContext(functionContext);

321

322

// Custom function context class

323

class CustomFunctionContext implements FunctionContext {

324

private final FunctionContext delegate;

325

private final Map<String, Function> customFunctions = new HashMap<>();

326

327

public CustomFunctionContext(FunctionContext delegate) {

328

this.delegate = delegate;

329

}

330

331

public void registerFunction(String namespaceURI, String localName, Function function) {

332

String key = (namespaceURI != null ? namespaceURI + ":" : "") + localName;

333

customFunctions.put(key, function);

334

}

335

336

@Override

337

public Function getFunction(String namespaceURI, String prefix, String localName) throws UnresolvableException {

338

String key = (namespaceURI != null ? namespaceURI + ":" : "") + localName;

339

Function custom = customFunctions.get(key);

340

if (custom != null) {

341

return custom;

342

}

343

return delegate.getFunction(namespaceURI, prefix, localName);

344

}

345

}

346

```

347

348

## Pattern Matching and Filtering

349

350

### NodeFilter Interface

351

```java { .api }

352

import org.dom4j.NodeFilter;

353

354

// Create filters from XPath expressions

355

NodeFilter elementFilter = DocumentHelper.createXPathFilter("self::element");

356

NodeFilter attributeFilter = DocumentHelper.createXPathFilter("self::attribute()");

357

NodeFilter textFilter = DocumentHelper.createXPathFilter("self::text()");

358

359

// Custom node filter

360

NodeFilter customFilter = new NodeFilter() {

361

@Override

362

public boolean matches(Node node) {

363

if (node.getNodeType() == Node.ELEMENT_NODE) {

364

Element element = (Element) node;

365

return "book".equals(element.getName()) &&

366

element.attributeValue("category") != null;

367

}

368

return false;

369

}

370

};

371

372

// Use filters

373

List<Node> allNodes = document.selectNodes("//node()");

374

List<Node> filteredNodes = allNodes.stream()

375

.filter(elementFilter::matches)

376

.collect(Collectors.toList());

377

```

378

379

### Pattern-Based Selection

380

```java { .api }

381

import org.dom4j.rule.Pattern;

382

383

// Create patterns for XSLT-style matching

384

Pattern bookPattern = DocumentHelper.createPattern("book");

385

Pattern authorPattern = DocumentHelper.createPattern("book/author");

386

Pattern isbnPattern = DocumentHelper.createPattern("book/@isbn");

387

388

// Test pattern matching

389

List<Element> elements = document.selectNodes("//element");

390

for (Element element : elements) {

391

if (bookPattern.matches(element)) {

392

System.out.println("Found book element: " + element.getName());

393

}

394

}

395

396

// Complex patterns

397

Pattern complexPattern = DocumentHelper.createPattern("book[@category='fiction' and author]");

398

Pattern positionPattern = DocumentHelper.createPattern("book[position() = 1]");

399

```

400

401

## Advanced XPath Techniques

402

403

### Dynamic XPath Generation

404

```java { .api }

405

// Build XPath expressions dynamically

406

public class XPathBuilder {

407

private final StringBuilder xpath = new StringBuilder();

408

409

public XPathBuilder element(String name) {

410

if (xpath.length() > 0 && !xpath.toString().endsWith("/")) {

411

xpath.append("/");

412

}

413

xpath.append(name);

414

return this;

415

}

416

417

public XPathBuilder descendant(String name) {

418

xpath.append("//").append(name);

419

return this;

420

}

421

422

public XPathBuilder attribute(String name, String value) {

423

xpath.append("[@").append(name).append("='").append(value).append("']");

424

return this;

425

}

426

427

public XPathBuilder hasAttribute(String name) {

428

xpath.append("[@").append(name).append("]");

429

return this;

430

}

431

432

public XPathBuilder position(int pos) {

433

xpath.append("[").append(pos).append("]");

434

return this;

435

}

436

437

public XPathBuilder text(String text) {

438

xpath.append("[text()='").append(text).append("']");

439

return this;

440

}

441

442

public XPath build() throws InvalidXPathException {

443

return DocumentHelper.createXPath(xpath.toString());

444

}

445

446

@Override

447

public String toString() {

448

return xpath.toString();

449

}

450

}

451

452

// Use dynamic builder

453

XPath dynamicXPath = new XPathBuilder()

454

.descendant("book")

455

.attribute("category", "fiction")

456

.hasAttribute("isbn")

457

.build();

458

459

List<Node> books = dynamicXPath.selectNodes(document);

460

```

461

462

### XPath Performance Optimization

463

```java { .api }

464

// Cache compiled XPath expressions

465

public class XPathCache {

466

private final Map<String, XPath> cache = new ConcurrentHashMap<>();

467

private final Map<String, String> namespaces;

468

469

public XPathCache(Map<String, String> namespaces) {

470

this.namespaces = namespaces != null ? Map.copyOf(namespaces) : Map.of();

471

}

472

473

public XPath getXPath(String expression) throws InvalidXPathException {

474

return cache.computeIfAbsent(expression, expr -> {

475

try {

476

XPath xpath = DocumentHelper.createXPath(expr);

477

if (!namespaces.isEmpty()) {

478

xpath.setNamespaceURIs(namespaces);

479

}

480

return xpath;

481

} catch (InvalidXPathException e) {

482

throw new RuntimeException(e);

483

}

484

});

485

}

486

487

public List<Node> selectNodes(String expression, Object context) throws InvalidXPathException {

488

return getXPath(expression).selectNodes(context);

489

}

490

491

public Node selectSingleNode(String expression, Object context) throws InvalidXPathException {

492

return getXPath(expression).selectSingleNode(context);

493

}

494

495

public String valueOf(String expression, Object context) throws InvalidXPathException {

496

return getXPath(expression).valueOf(context);

497

}

498

}

499

500

// Use cached XPath

501

Map<String, String> namespaces = Map.of("ns", "http://example.com/");

502

XPathCache cache = new XPathCache(namespaces);

503

504

// These expressions are compiled once and cached

505

List<Node> books1 = cache.selectNodes("//ns:book[@category='fiction']", document);

506

List<Node> books2 = cache.selectNodes("//ns:book[@category='technical']", document);

507

List<Node> books3 = cache.selectNodes("//ns:book[@category='fiction']", document); // Uses cached version

508

```

509

510

### Sorting with XPath

511

```java { .api }

512

// Sort nodes using XPath expressions

513

XPath booksXPath = DocumentHelper.createXPath("//book");

514

List<Node> books = booksXPath.selectNodes(document);

515

516

// Sort by title

517

XPath titleSortXPath = DocumentHelper.createXPath("title");

518

titleSortXPath.sort(books);

519

520

// Sort by price (numeric)

521

XPath priceSortXPath = DocumentHelper.createXPath("number(@price)");

522

priceSortXPath.sort(books);

523

524

// Complex sorting with selectNodes

525

XPath sortedBooksXPath = DocumentHelper.createXPath("//book");

526

XPath sortKeyXPath = DocumentHelper.createXPath("concat(author, '|', title)");

527

List<Node> sortedBooks = sortedBooksXPath.selectNodes(document, sortKeyXPath);

528

529

// Remove duplicates while sorting

530

List<Node> uniqueSortedBooks = sortedBooksXPath.selectNodes(document, titleSortXPath, true);

531

```

532

533

### Error Handling in XPath

534

```java { .api }

535

// Handle XPath compilation errors

536

try {

537

XPath xpath = DocumentHelper.createXPath("//book[invalid syntax");

538

} catch (InvalidXPathException e) {

539

System.err.println("Invalid XPath syntax: " + e.getMessage());

540

// Provide user-friendly error message

541

String suggestion = suggestCorrection(e.getMessage());

542

System.err.println("Did you mean: " + suggestion);

543

}

544

545

// Handle runtime XPath errors

546

try {

547

XPath xpath = DocumentHelper.createXPath("//book/@price div //book/@quantity");

548

Number result = xpath.numberValueOf(document);

549

550

if (result.doubleValue() == Double.POSITIVE_INFINITY) {

551

System.err.println("Division by zero in XPath expression");

552

} else if (Double.isNaN(result.doubleValue())) {

553

System.err.println("Invalid numeric operation in XPath expression");

554

}

555

556

} catch (Exception e) {

557

System.err.println("XPath evaluation error: " + e.getMessage());

558

}

559

560

// Validate XPath expressions

561

public boolean isValidXPath(String expression) {

562

try {

563

DocumentHelper.createXPath(expression);

564

return true;

565

} catch (InvalidXPathException e) {

566

return false;

567

}

568

}

569

570

// Safe XPath execution with defaults

571

public String safeValueOf(String expression, Object context, String defaultValue) {

572

try {

573

XPath xpath = DocumentHelper.createXPath(expression);

574

String result = xpath.valueOf(context);

575

return result != null && !result.isEmpty() ? result : defaultValue;

576

} catch (Exception e) {

577

System.err.println("XPath error: " + e.getMessage());

578

return defaultValue;

579

}

580

}

581

```

582

583

## Integration with Document Navigation

584

585

### Combining XPath with DOM4J Navigation

586

```java { .api }

587

// Start with XPath, continue with DOM4J navigation

588

XPath authorXPath = DocumentHelper.createXPath("//author[1]");

589

Element firstAuthor = (Element) authorXPath.selectSingleNode(document);

590

591

// Continue with DOM4J navigation

592

Element book = firstAuthor.getParent();

593

String title = book.elementText("title");

594

String isbn = book.attributeValue("isbn");

595

596

// Navigate to siblings using DOM4J

597

Element nextBook = book.getParent().elements("book").stream()

598

.skip(book.getParent().elements("book").indexOf(book) + 1)

599

.findFirst()

600

.orElse(null);

601

602

// Use XPath on specific subtrees

603

if (nextBook != null) {

604

XPath relativeXPath = DocumentHelper.createXPath("./author");

605

Element nextAuthor = (Element) relativeXPath.selectSingleNode(nextBook);

606

}

607

```

608

609

DOM4J's XPath support provides powerful and flexible document querying capabilities. The integration with Jaxen enables full XPath 1.0 compliance while maintaining the performance and ease of use that DOM4J is known for. Whether for simple node selection or complex document analysis, XPath expressions provide a declarative and efficient way to work with XML content.