fontoxpath

A minimalistic XPath 3.1 and XQuery 3.1 engine for (XML) nodes with XQuery Update Facility 3.0 support.
Demo page
How to use
Querying XML
evaluateXPath(xpathExpression, contextNode, domFacade, variables, returnType, options);
The following are convenience functions for a specific returnType.
evaluateXPathToArray(xpathExpression, contextNode, domFacade, variables, options);
evaluateXPathToAsyncIterator(xpathExpression, contextNode, domFacade, variables, options);
evaluateXPathToBoolean(xpathExpression, contextNode, domFacade, variables, options);
evaluateXPathToFirstNode(xpathExpression, contextNode, domFacade, variables, options);
evaluateXPathToMap(xpathExpression, contextNode, domFacade, variables, options);
evaluateXPathToNodes(xpathExpression, contextNode, domFacade, variables, options);
evaluateXPathToNumber(xpathExpression, contextNode, domFacade, variables, options);
evaluateXPathToNumbers(xpathExpression, contextNode, domFacade, variables, options);
evaluateXPathToString(xpathExpression, contextNode, domFacade, variables, options);
evaluateXPathToStrings(xpathExpression, contextNode, domFacade, variables, options);
xpathExpression
<String>
The query to evaluate.contextNode
<Node>
The node in which context the xpathExpression
will be evaluated. Defaults to null
.domFacade
<IDomFacade>
An IDomFacade implementation which will be used for querying the DOM. Defaults to an implementation which uses properties and methods on the contextNode
as described in the DOM spec.variables
<Object>
The properties of variables
are available variables within the xpathExpression
. Defaults to an empty Object
.returnType
<number>
Determines the type of the result. Defaults to evaluateXPath.ANY_TYPE
. Possible values:
evaluateXPath.ANY_TYPE
Returns the result of the query, can be anything depending on the query. Note that the return type is determined dynamically, not statically: XPaths returning empty sequences will return empty arrays and not null, like one might expect.evaluateXPath.NUMBER_TYPE
Resolve to a number
, like count((1,2,3)) resolves to 3.evaluateXPath.STRING_TYPE
Resolve to a string
, like //someElement[1] resolves to the text content of the first someElement.evaluateXPath.BOOLEAN_TYPE
Resolves to a boolean
true or false, uses the effective boolean value to determine the result. count(1) resolves to true, count(()) resolves to false.evaluateXPath.NODES_TYPE
Resolve to all nodes Node[]
the XPath resolves to. Returns nodes in the order the XPath would. Meaning (//a, //b) resolves to all A nodes, followed by all B nodes. //*[self::a or self::b] resolves to A and B nodes in document order.evaluateXPath.FIRST_NODE_TYPE
Resolves to the first Node
node.NODES_TYPE would have resolved to.evaluateXPath.STRINGS_TYPE
Resolve to an array of strings string[]
.evaluateXPath.MAP_TYPE
Resolve to an Object
, as a map.evaluateXPath.ARRAY_TYPE
Resolve to an array []
.evaluateXPath.ASYNC_ITERATOR_TYPE
evaluateXPath.NUMBERS_TYPE
Resolve to an array of numbers number[]
.
options
<Object>
Options used to modify the behavior. The following options are available:
namespaceResolver
<function(string):string?>
nodesFactory
INodesFactory
A INodesFactory implementation which will be used for creating nodes.language
string
The query language to use. Defaults to evaluateXPath.XPATH_3_1_LANGUAGE
. Possible values:
evaluateXPath.XPATH_3_1_LANGUAGE
Evaluate xpathExpression
according the XPath spec.evaluateXPath.XQUERY_3_1_LANGUAGE
Evaluate xpathExpression
according the XQuery spec.
moduleImports
<Object<string, string>
debug
<boolean>
If a debug trace should be tracked, see debugging for more information.logger
<Object>
Object with functions used to override the standard logger.
trace: <function(string):void>
The logger for the trace()
function. The argument is the string of the original message.
Example
const {
evaluateXPath,
evaluateXPathToBoolean,
evaluateXPathToString,
evaluateXPathToFirstNode
} = require('fontoxpath');
const documentNode = new DOMParser().parseFromString('<xml/>', 'text/xml');
console.log(evaluateXPathToBoolean('/xml => exists()', documentNode));
console.log(evaluateXPathToString('$foo', null, null, {'foo': 'bar'}));
console.log(evaluateXPathToFirstNode('<foo>bar</foo>', documentNode, null, null, {language: evaluateXPath.XQUERY_3_1_LANGUAGE}).outerHTML);
Debugging
FontoXPath can output a basic trace for an error if the debug
option is set to true
. This is disabled by default because of
performance reasons.
evaluateXPathToBoolean(`
if (true()) then
zero-or-one((1, 2))
else
(1, 2, 3)
`, null, null, null, {debug: true});
1: if (true()) then
2: zero-or-one((1, 2))
^^^^^^^^^^^^^^^^^^^
3: else
4: (1, 2, 3)
Error: FORG0003: The argument passed to fn:zero-or-one contained more than one item.
at <functionCallExpr>:2:3 - 2:22
at <ifThenElseExpr>:1:1 - 4:12
Modifying XML
Note: the use of XQuery Update Facility 3.0 is in preview and subject to change.
To modify XML you can use XQuery Update Facility 3.0 as following
evaluateUpdatingExpression(xpathExpression, contextNode, domFacade, variables, options);
The arguments are the same as evaluateXPath
. This returns a Promise<Object>
, the object has a xdmValue
and pendingUpdateList
. The xdmValue
is the result of query as if it was run using evaluateXPath
with evaluateXPath.ANY_TYPE
as returnType
. The pendingUpdateList
is an <Object[]>
in which each entry represents an update primitive where the type
identifies the update primitive.
The pending update list can be executed using
executePendingUpdateList(pendingUpdateList, domFacade, nodesFactory, documentWriter);
pendingUpdateList
<Object[]>
The pending update list returned by evaluateUpdatingExpression
.domFacade
<IDomFacade>
See evaluateXPath
. The default will use nodes from the pendingUpdateList
.nodesFactory
INodesFactory
A INodesFactory implementation which will be used for creating nodes. Defaults to an implementation which uses properties and methods of nodes from the pendingUpdateList
.documentWriter
<IDocumentWriter>
An IDocumentWriter implementation which will be used for modifying a DOM. Defaults to an implementation which uses properties and methods of nodes from the pendingUpdateList
.
Example
const {
evaluateUpdatingExpression,
executePendingUpdateList
} = require('fontoxpath');
const documentNode = new DOMParser().parseFromString('<xml/>', 'text/xml');
evaluateUpdatingExpression('replace node /xml with <foo/>', documentNode)
.then(result => {
executePendingUpdateList(result.pendingUpdateList);
console.log(documentNode.documentElement.outerHTML);
});
Global functions
To register custom functions. They are registered globally.
registerCustomXPathFunction(name, signature, returnType, callback);
name
<string>
The function name.signature
string[]
The arguments of the function.returnType
string
The return type of the function.callback
function
The function itself.
Typescript
We support TypeScript; and expose a minimal Node type.
You can use generic types to get the type of the DOM implementation you are using without having to
cast it.
const myNodes = evaluateXPathToNodes<slimdom.Node>(
'<foo>bar</foo>',
null,
null,
null,
{language: evaluateXPath.XQUERY_3_1_LANGUAGE}
);
Features
Note that this engine assumes XPath 1.0 compatibility
mode turned off.
Not all XPath 3.1 functions are implemented yet. We
accept pull requests for missing features.
The following features are unavailable at this moment, but will be implemented at some point in time (and even
sooner if you can help!):
- Some DateTime related functions
- Collation related functions (
fn:compare#3
) - functions using patterns
- Some other miscellaneous functions
- The
?
lookup operator for maps and arrays - XML parsing
- The
treat as
operator
For all available features, see the unit tests, or just try it out on the Demo page.
Compatibility
This engine is pretty DOM-agnostic, it has a good track record with the browser DOM implementations
and slimdom.js. There are a number of known issues with
xmldom because it does not follow the DOM spec on some features
including namespaces.
Contribution
If you have any questions on how to use FontoXPath, or if you are running into problems, just file a
github issue! If you are looking to contribute, we have a Contribution Guide
that should help you in getting your development environment set up.