Skip to content

Commit 5aac9cd

Browse files
committed
XmlXPath to hold namespace information
1 parent 0c13897 commit 5aac9cd

File tree

5 files changed

+43
-22
lines changed

5 files changed

+43
-22
lines changed

src/document.mts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {
22
XmlError, xmlDocGetRootElement, xmlFreeDoc, xmlNewDoc,
33
} from './libxml2.mjs';
4-
import { NamespaceMap, XmlElement, XmlNode } from './nodes.mjs';
5-
import XmlXPath from './xpath.mjs';
4+
import { XmlElement, XmlNode } from './nodes.mjs';
5+
import { XmlXPath, NamespaceMap } from './xpath.mjs';
66

77
export default class XmlDocument {
88
/** @internal */
@@ -30,6 +30,8 @@ export default class XmlDocument {
3030
xmlFreeDoc(this._docPtr);
3131
}
3232

33+
get(xpath: XmlXPath): XmlNode | null;
34+
get(xpath: string, namespaces?: NamespaceMap): XmlNode | null;
3335
/**
3436
* Find the first descendant node of root element matching the given xpath selector.
3537
* @param xpath XPath selector
@@ -43,6 +45,8 @@ export default class XmlDocument {
4345
return this.root.get(xpath, namespaces);
4446
}
4547

48+
find(xpath: XmlXPath): XmlNode[];
49+
find(xpath: string, namespaces?: NamespaceMap): XmlNode[];
4650
/**
4751
* Find all the descendant nodes of root element matching the given xpath selector.
4852
* @param xpath XPath selector

src/index.mts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@ import {
99
} from './libxml2.mjs';
1010
import type { XmlDocPtr } from './libxml2raw';
1111

12-
export { default as XmlXPath } from './xpath.mjs';
12+
export { XmlXPath, NamespaceMap } from './xpath.mjs';
1313
export {
1414
XmlNode,
1515
XmlAttribute,
1616
XmlComment,
1717
XmlElement,
1818
XmlText,
1919
XmlCData,
20-
NamespaceMap,
2120
} from './nodes.mjs';
2221
export { default as XmlDocument } from './document.mjs';
2322
export { XmlParseError, XmlError } from './libxml2.mjs';

src/nodes.mts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,24 @@
11
import {
22
XmlError,
3-
xmlHasNsProp,
43
xmlGetNsList,
4+
xmlHasNsProp,
55
XmlNamedNodeStruct,
66
xmlNodeGetContent,
77
XmlNodeSetStruct,
88
XmlNodeStruct,
99
XmlNsStruct,
1010
xmlSearchNs,
11+
xmlXPathCompiledEval,
1112
xmlXPathFreeContext,
1213
xmlXPathFreeObject,
1314
xmlXPathNewContext,
1415
XmlXPathObjectStruct,
1516
xmlXPathRegisterNs,
16-
xmlXPathCompiledEval,
1717
xmlXPathSetContextNode,
1818
} from './libxml2.mjs';
1919
import type XmlDocument from './document.mjs';
2020
import type { XmlNodePtr } from './libxml2raw.js';
21-
import XmlXPath from './xpath.mjs';
22-
23-
/**
24-
* Map between the prefix and the uri of the namespace
25-
*/
26-
export interface NamespaceMap {
27-
[prefix: string]: string;
28-
}
21+
import { XmlXPath, NamespaceMap } from './xpath.mjs';
2922

3023
export abstract class XmlNode {
3124
protected _doc: XmlDocument;
@@ -156,6 +149,9 @@ export abstract class XmlNode {
156149
return ns ? XmlNsStruct.href(ns) : null;
157150
}
158151

152+
get(xpath: XmlXPath): XmlNode | null;
153+
get(xpath: string, namespaces?: NamespaceMap): XmlNode | null;
154+
get(xpath: string | XmlXPath, namespaces?: NamespaceMap): XmlNode | null;
159155
/**
160156
* Find the first descendant node matching the given xpath selector
161157
*
@@ -186,18 +182,18 @@ export abstract class XmlNode {
186182
}
187183

188184
private xpathEval(xpath: string | XmlXPath, namespaces?: NamespaceMap) {
189-
const xpathCompiled = xpath instanceof XmlXPath ? xpath : new XmlXPath(xpath);
190-
const ret = this.compiledXPathEval(xpathCompiled, namespaces);
185+
const xpathCompiled = xpath instanceof XmlXPath ? xpath : new XmlXPath(xpath, namespaces);
186+
const ret = this.compiledXPathEval(xpathCompiled);
191187
if (!(xpath instanceof XmlXPath)) {
192188
xpathCompiled.dispose();
193189
}
194190
return ret;
195191
}
196192

197-
private compiledXPathEval(xpath: XmlXPath, namespaces?: NamespaceMap) {
193+
private compiledXPathEval(xpath: XmlXPath) {
198194
const context = xmlXPathNewContext(this._doc._docPtr);
199-
if (namespaces) {
200-
Object.entries(namespaces)
195+
if (xpath._namespaces) {
196+
Object.entries(xpath._namespaces)
201197
.forEach(([prefix, uri]) => {
202198
xmlXPathRegisterNs(context, prefix, uri);
203199
});
@@ -208,6 +204,9 @@ export abstract class XmlNode {
208204
return xpathObj;
209205
}
210206

207+
find(xpath: XmlXPath): XmlNode[];
208+
find(xpath: string, namespaces?: NamespaceMap): XmlNode[];
209+
find(xpath: string | XmlXPath, namespaces?: NamespaceMap): XmlNode[];
211210
/**
212211
* Find all the descendant nodes matching the given xpath selector.
213212
* @param xpath XPath selector

src/xpath.mts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@ import {
44
} from './libxml2.mjs';
55
import type { XmlXPathCompExprPtr } from './libxml2raw.js';
66

7-
export default class XmlXPath {
7+
/**
8+
* Map between the prefix and the uri of the namespace
9+
*/
10+
export interface NamespaceMap {
11+
[prefix: string]: string;
12+
}
13+
14+
export class XmlXPath {
815
_xpath: XmlXPathCompExprPtr;
916

10-
constructor(xpath: string) {
17+
_namespaces: NamespaceMap | undefined;
18+
19+
constructor(xpath: string, namespaces?: NamespaceMap) {
1120
this._xpath = xmlXPathCtxtCompile(0, xpath);
21+
this._namespaces = namespaces;
1222
}
1323

1424
dispose() {

test/xpath.spec.mts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect } from 'chai';
22
import { parseXmlString } from '../lib/index.mjs';
3-
import XmlXPath from '../lib/xpath.mjs';
3+
import { XmlXPath } from '../lib/xpath.mjs';
44

55
describe('XPath', () => {
66
const doc1 = parseXmlString('<book><title>Harry Potter</title></book>');
@@ -24,4 +24,13 @@ describe('XPath', () => {
2424
const xpath = new XmlXPath(null!);
2525
xpath.dispose();
2626
});
27+
28+
it('handles namespace', () => {
29+
const doc = parseXmlString(
30+
'<book xmlns:t="http://foo"><t:title>Harry Potter</t:title></book>',
31+
);
32+
const xpath = new XmlXPath('/book/m:title', { m: 'http://foo' });
33+
expect(doc.get(xpath)?.content).to.equal('Harry Potter');
34+
expect(doc.find(xpath).map((node) => node.content)).to.deep.equal(['Harry Potter']);
35+
});
2736
});

0 commit comments

Comments
 (0)