Skip to content

Commit 363ea0a

Browse files
committed
Move global parsing functions to static member of XmlDocument class
1 parent 3637c61 commit 363ea0a

File tree

9 files changed

+136
-132
lines changed

9 files changed

+136
-132
lines changed

benchmark/index.mts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ import * as libxmljs2 from 'libxmljs2';
55
import { XMLParser } from 'fast-xml-parser';
66
import { XmlDocument } from 'xmldoc';
77
import { parseXml } from '@rgrove/parse-xml';
8-
import { parseXmlBuffer, parseXmlString } from 'libxml2-wasm';
8+
import { XmlDocument as WasmXmlDocument} from 'libxml2-wasm';
99

10-
console.log(`Environment: NodeJs ${process.version} on ${os.type()} ${os.arch()} ${os.cpus()[0].model}\n`);
10+
console.log(`\nEnvironment: NodeJs ${process.version} on ${os.type()} ${os.arch()} ${os.cpus()[0].model}\n`);
1111
for (const fixture of ['fixtures/small.xml', 'fixtures/medium.xml', 'fixtures/large.xml']) {
1212
const xmlString = readFileSync(fixture, 'utf-8');
1313
const xmlBuffer = readFileSync(fixture);
1414
benny.suite(
1515
`${fixture}: ${xmlBuffer.length} bytes`,
1616
benny.add('libxml2-wasm', () => {
17-
const doc = parseXmlString(xmlString);
17+
const doc = WasmXmlDocument.fromString(xmlString);
1818
doc.dispose();
1919
}),
2020
benny.add('libxml2-wasm(buffer api)', () => {
21-
const doc = parseXmlBuffer(xmlBuffer);
21+
const doc = WasmXmlDocument.fromBuffer(xmlBuffer);
2222
doc.dispose();
2323
}),
2424
benny.add('libxmljs2', () => {

src/document.mts

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,89 @@
11
import {
2-
XmlError, xmlDocGetRootElement, xmlFreeDoc, xmlNewDoc,
2+
XmlError,
3+
xmlDocGetRootElement,
4+
xmlFreeDoc,
5+
xmlNewDoc,
6+
xmlResetLastError,
7+
xmlGetLastError,
8+
XmlErrorStruct, xmlReadString, XmlParseError, xmlReadMemory,
39
} from './libxml2.mjs';
410
import { XmlElement, XmlNode } from './nodes.mjs';
511
import { XmlXPath, NamespaceMap } from './xpath.mjs';
12+
import type { XmlDocPtr } from './libxml2raw';
613

7-
export default class XmlDocument {
14+
export enum ParseOption {
15+
XML_PARSE_DEFAULT = 0,
16+
/** recover on errors */
17+
XML_PARSE_RECOVER = 1 << 0,
18+
/** substitute entities */
19+
XML_PARSE_NOENT = 1 << 1,
20+
/** load the external subset */
21+
XML_PARSE_DTDLOAD = 1 << 2,
22+
/** default DTD attributes */
23+
XML_PARSE_DTDATTR = 1 << 3,
24+
/** validate with the DTD */
25+
XML_PARSE_DTDVALID = 1 << 4,
26+
/** suppress error reports */
27+
XML_PARSE_NOERROR = 1 << 5,
28+
/** suppress warning reports */
29+
XML_PARSE_NOWARNING = 1 << 6,
30+
/** pedantic error reporting */
31+
XML_PARSE_PEDANTIC = 1 << 7,
32+
/** remove blank nodes */
33+
XML_PARSE_NOBLANKS = 1 << 8,
34+
/** use the SAX1 interface internally */
35+
XML_PARSE_SAX1 = 1 << 9,
36+
/** Implement XInclude substitution */
37+
XML_PARSE_XINCLUDE = 1 << 10,
38+
/** Forbid network access */
39+
XML_PARSE_NONET = 1 << 11,
40+
/** Do not reuse the context dictionary */
41+
XML_PARSE_NODICT = 1 << 12,
42+
/** remove redundant namespaces declarations */
43+
XML_PARSE_NSCLEAN = 1 << 13,
44+
/** merge CDATA as text nodes */
45+
XML_PARSE_NOCDATA = 1 << 14,
46+
/** do not generate XINCLUDE START/END nodes */
47+
XML_PARSE_NOXINCNODE = 1 << 15,
48+
/** compact small text nodes;
49+
* no modification of the tree allowed afterward
50+
* (will possibly crash if you try to modify the tree)
51+
*/
52+
XML_PARSE_COMPACT = 1 << 16,
53+
/** parse using XML-1.0 before update 5 */
54+
XML_PARSE_OLD10 = 1 << 17,
55+
/** do not fixup XINCLUDE xml:base uris */
56+
XML_PARSE_NOBASEFIX = 1 << 18,
57+
/** relax any hardcoded limit from the parser */
58+
XML_PARSE_HUGE = 1 << 19,
59+
/* parse using SAX2 interface before 2.7.0 */
60+
XML_PARSE_OLDSAX = 1 << 20,
61+
/** ignore internal document encoding hint */
62+
XML_PARSE_IGNORE_ENC = 1 << 21,
63+
/** Store big lines numbers in text PSVI field */
64+
XML_PARSE_BIG_LINES = 1 << 22,
65+
}
66+
67+
export interface ParseOptions {
68+
url?: string; // reserved
69+
encoding?: string; // reserved
70+
option?: ParseOption;
71+
}
72+
73+
export class XmlDocument {
874
/** @internal */
975
_docPtr: number;
1076

11-
/** Create a new document.
12-
* To parse an existing xml, use {@link parseXmlString}.
13-
*/
14-
constructor();
1577
/** Create a document object wrapping document parsed by libxml2.
1678
* @see {@link parseXmlString}
1779
* @internal
1880
*/
19-
constructor(xmlDocPtr: number);
20-
constructor(xmlDocPtr?: number) {
21-
this._docPtr = xmlDocPtr ?? xmlNewDoc();
81+
private constructor(xmlDocPtr: XmlDocPtr) {
82+
if (!xmlDocPtr) {
83+
const err = xmlGetLastError();
84+
throw new XmlParseError(XmlErrorStruct.message(err));
85+
}
86+
this._docPtr = xmlDocPtr;
2287
}
2388

2489
/**
@@ -30,6 +95,43 @@ export default class XmlDocument {
3095
xmlFreeDoc(this._docPtr);
3196
}
3297

98+
/** Create a new document from scratch.
99+
* To parse an existing xml, use {@link fromBuffer} or {@link fromString}.
100+
*/
101+
static create(): XmlDocument {
102+
return new XmlDocument(xmlNewDoc());
103+
}
104+
105+
/**
106+
* Parse and create an {@link XmlDocument} from an XML string.
107+
* @param source The XML string
108+
* @param options Parsing options
109+
*/
110+
static fromString(
111+
source: string,
112+
options: ParseOptions = {},
113+
): XmlDocument {
114+
xmlResetLastError();
115+
return new XmlDocument(
116+
xmlReadString(source, '', '', options.option ?? ParseOption.XML_PARSE_DEFAULT),
117+
);
118+
}
119+
120+
/**
121+
* Parse and create an {@link XmlDocument} from an XML buffer.
122+
* @param source The XML buffer
123+
* @param options Parsing options
124+
*/
125+
static fromBuffer(
126+
source: Uint8Array,
127+
options: ParseOptions = {},
128+
): XmlDocument {
129+
xmlResetLastError();
130+
return new XmlDocument(
131+
xmlReadMemory(source, '', '', options.option ?? ParseOption.XML_PARSE_DEFAULT),
132+
);
133+
}
134+
33135
get(xpath: XmlXPath): XmlNode | null;
34136
get(xpath: string, namespaces?: NamespaceMap): XmlNode | null;
35137
/**

src/index.mts

Lines changed: 1 addition & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,3 @@
1-
import XmlDocument from './document.mjs';
2-
import {
3-
XmlErrorStruct,
4-
xmlGetLastError,
5-
XmlParseError,
6-
xmlReadMemory,
7-
xmlReadString,
8-
xmlResetLastError,
9-
} from './libxml2.mjs';
10-
import type { XmlDocPtr } from './libxml2raw';
11-
121
export { XmlXPath, NamespaceMap } from './xpath.mjs';
132
export {
143
XmlNode,
@@ -18,92 +7,5 @@ export {
187
XmlText,
198
XmlCData,
209
} from './nodes.mjs';
21-
export { default as XmlDocument } from './document.mjs';
10+
export { XmlDocument, ParseOption, ParseOptions } from './document.mjs';
2211
export { XmlParseError, XmlError } from './libxml2.mjs';
23-
24-
export enum ParseOption {
25-
XML_PARSE_DEFAULT = 0,
26-
/** recover on errors */
27-
XML_PARSE_RECOVER = 1 << 0,
28-
/** substitute entities */
29-
XML_PARSE_NOENT = 1 << 1,
30-
/** load the external subset */
31-
XML_PARSE_DTDLOAD = 1 << 2,
32-
/** default DTD attributes */
33-
XML_PARSE_DTDATTR = 1 << 3,
34-
/** validate with the DTD */
35-
XML_PARSE_DTDVALID = 1 << 4,
36-
/** suppress error reports */
37-
XML_PARSE_NOERROR = 1 << 5,
38-
/** suppress warning reports */
39-
XML_PARSE_NOWARNING = 1 << 6,
40-
/** pedantic error reporting */
41-
XML_PARSE_PEDANTIC = 1 << 7,
42-
/** remove blank nodes */
43-
XML_PARSE_NOBLANKS = 1 << 8,
44-
/** use the SAX1 interface internally */
45-
XML_PARSE_SAX1 = 1 << 9,
46-
/** Implement XInclude substitution */
47-
XML_PARSE_XINCLUDE = 1 << 10,
48-
/** Forbid network access */
49-
XML_PARSE_NONET = 1 << 11,
50-
/** Do not reuse the context dictionary */
51-
XML_PARSE_NODICT = 1 << 12,
52-
/** remove redundant namespaces declarations */
53-
XML_PARSE_NSCLEAN = 1 << 13,
54-
/** merge CDATA as text nodes */
55-
XML_PARSE_NOCDATA = 1 << 14,
56-
/** do not generate XINCLUDE START/END nodes */
57-
XML_PARSE_NOXINCNODE = 1 << 15,
58-
/** compact small text nodes;
59-
* no modification of the tree allowed afterward
60-
* (will possibly crash if you try to modify the tree)
61-
*/
62-
XML_PARSE_COMPACT = 1 << 16,
63-
/** parse using XML-1.0 before update 5 */
64-
XML_PARSE_OLD10 = 1 << 17,
65-
/** do not fixup XINCLUDE xml:base uris */
66-
XML_PARSE_NOBASEFIX = 1 << 18,
67-
/** relax any hardcoded limit from the parser */
68-
XML_PARSE_HUGE = 1 << 19,
69-
/* parse using SAX2 interface before 2.7.0 */
70-
XML_PARSE_OLDSAX = 1 << 20,
71-
/** ignore internal document encoding hint */
72-
XML_PARSE_IGNORE_ENC = 1 << 21,
73-
/** Store big lines numbers in text PSVI field */
74-
XML_PARSE_BIG_LINES = 1 << 22,
75-
}
76-
77-
export interface ParseOptions {
78-
url?: string; // reserved
79-
encoding?: string; // reserved
80-
option?: ParseOption;
81-
}
82-
83-
function toXmlDocument(docPtr: XmlDocPtr): XmlDocument {
84-
if (!docPtr) {
85-
const err = xmlGetLastError();
86-
throw new XmlParseError(XmlErrorStruct.message(err));
87-
}
88-
return new XmlDocument(docPtr);
89-
}
90-
91-
export function parseXmlString(
92-
source: string,
93-
options: ParseOptions = {},
94-
): XmlDocument {
95-
xmlResetLastError();
96-
return toXmlDocument(
97-
xmlReadString(source, '', '', options.option ?? ParseOption.XML_PARSE_DEFAULT),
98-
);
99-
}
100-
101-
export function parseXmlBuffer(
102-
source: Uint8Array,
103-
options: ParseOptions = {},
104-
): XmlDocument {
105-
xmlResetLastError();
106-
return toXmlDocument(
107-
xmlReadMemory(source, '', '', options.option ?? ParseOption.XML_PARSE_DEFAULT),
108-
);
109-
}

src/nodes.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
xmlXPathRegisterNs,
1717
xmlXPathSetContextNode,
1818
} from './libxml2.mjs';
19-
import type XmlDocument from './document.mjs';
19+
import type { XmlDocument } from './document.mjs';
2020
import type { XmlNodePtr } from './libxml2raw.js';
2121
import { XmlXPath, NamespaceMap } from './xpath.mjs';
2222

src/xpath.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface NamespaceMap {
2020
* Note: This object requires to be {@link dispose}d explicitly.
2121
*/
2222
export class XmlXPath {
23+
/** @internal */
2324
_xpath: XmlXPathCompExprPtr;
2425

2526
private readonly _xpathSource;

test/document.spec.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { expect } from 'chai';
22
import { XmlElement } from '../lib/nodes.mjs';
3-
import { XmlDocument, XmlError, parseXmlString } from '../lib/index.mjs';
3+
import { XmlDocument, XmlError } from '../lib/index.mjs';
44

55
describe('XmlDocument', () => {
6-
const doc = parseXmlString('<docs><doc></doc></docs>');
6+
const doc = XmlDocument.fromString('<docs><doc></doc></docs>');
77
after(() => doc.dispose());
88

99
describe('root property', () => {
@@ -14,7 +14,7 @@ describe('XmlDocument', () => {
1414
});
1515

1616
it('return null if root doesn\'t exist', () => {
17-
const d = new XmlDocument();
17+
const d = XmlDocument.create();
1818
expect(() => d.root).to.throw(XmlError);
1919
d.dispose();
2020
});

test/nodes.spec.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { expect } from 'chai';
2-
import { parseXmlString } from '../lib/index.mjs';
2+
import { XmlDocument } from '../lib/index.mjs';
33
import {
44
XmlAttribute, XmlCData,
55
XmlComment,
66
XmlElement,
77
XmlText,
88
} from '../lib/nodes.mjs';
99

10-
const doc = parseXmlString(`<?xml version="1.0" encoding="UTF-8"?>
10+
const doc = XmlDocument.fromString(`<?xml version="1.0" encoding="UTF-8"?>
1111
<bookstore xmlns:m="http://www.federalreserve.gov"><!--comment1-->
1212
<book><title lang="en" author="J.K. Rowling">Harry Potter</title><price m:currency="USD"><![CDATA[29.99]]></price></book>
1313
<book><title lang="en" author="Erik Ray">Learning XML</title><price m:currency="USD">39.95</price></book>
@@ -240,7 +240,7 @@ describe('XmlNode', () => {
240240
});
241241

242242
it('should return empty if element has no namespace definition', () => {
243-
const document = parseXmlString('<doc/>');
243+
const document = XmlDocument.fromString('<doc/>');
244244
expect(document.root.namespaces).to.be.empty;
245245
});
246246

0 commit comments

Comments
 (0)