Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A minimalistic XPath 3.1 implementation in pure JavaScript

License

NotificationsYou must be signed in to change notification settings

FontoXML/fontoxpath

Repository files navigation

A minimalisticXPath 3.1 andXQuery3.1 engine for (XML) nodes withXQuery Update Facility3.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 thexpathExpression will be evaluated. Defaultstonull.
  • domFacade<IDomFacade> AnIDomFacade implementation which willbe used for querying the DOM. Defaults to an implementation which uses properties and methods onthecontextNode as described in theDOM spec.
  • variables<Object> The properties ofvariables are available variables within thexpathExpression. Defaults to an emptyObject. Can only be used to set variables in theglobal namespace.
  • returnType<number> Determines the type of the result. Defaults toevaluateXPath.ANY_TYPE. Possible values:
    • evaluateXPath.ALL_RESULTS_TYPE Returns the result of the query, can be anything dependingon the query. This will always be an array, and the result can be mixed: contain both nodesand strings for example.
    • evaluateXPath.NUMBER_TYPE Resolve to anumber, like count((1,2,3)) resolves to 3.
    • evaluateXPath.STRING_TYPE Resolve to astring, like //someElement[1] resolves to the textcontent of the first someElement.
    • evaluateXPath.BOOLEAN_TYPE Resolves to aboolean true or false, uses the effective booleanvalue to determine the result. count(1) resolves to true, count(()) resolves to false.
    • evaluateXPath.NODES_TYPE Resolve to all nodesNode[] the XPath resolves to. Returns nodes inthe order the XPath would. Meaning (//a, //b) resolves to all A nodes, followed by all Bnodes. //*[self::a or self::b] resolves to A and B nodes in document order.
    • evaluateXPath.FIRST_NODE_TYPE Resolves to the firstNode node.NODES_TYPE would have resolvedto.
    • evaluateXPath.STRINGS_TYPE Resolve to an array of stringsstring[].
    • evaluateXPath.MAP_TYPE Resolve to anObject, as a map.
    • evaluateXPath.ARRAY_TYPE Resolve to an array[].
    • evaluateXPath.ASYNC_ITERATOR_TYPE
    • evaluateXPath.NUMBERS_TYPE Resolve to an array of numbersnumber[].
    • evaluateXPath.ANY_TYPE Returns the result of the query, can be anything depending on thequery. Note that the return type is determined dynamically, not statically: XPaths returningempty sequences will return empty arrays and not null, like one might expect.This is deprecated, useevaluateXPath.ALL_RESULTS_TYPE instead, since that is more predictable.
  • options<Object> Options used to modify the behavior. The following options are available:
    • namespaceResolver<function(string):string?> By default, the namespaces in scope of thecontext item (if it is a node) are used. This is fine for most queries if you can assume howyour XML uses prefixes. Use this function to override those namespaces to remove thatassumption. This function will be called with a prefix (the empty string for the defaultnamespaceURI) and should return a namespaceURI (or null for the null namespace).
    • nodesFactoryINodesFactory AINodesFactoryimplementation which will be used for creating nodes.
    • languagestring The query language to use. Defaults toevaluateXPath.XPATH_3_1_LANGUAGE. Possible values:
      • evaluateXPath.XPATH_3_1_LANGUAGE EvaluatexpathExpression according theXPathspec.
      • evaluateXPath.XQUERY_3_1_LANGUAGE EvaluatexpathExpression according theXQueryspec.
    • moduleImports<Object<string, string>
    • debug<boolean> If a debug trace should be tracked, seedebugging for moreinformation.
    • logger<Object> Object with functions used to override the standard logger.
      • trace: <function(string):void> The logger for thetrace() function. The argument is thestring of the original message.
    • defaultFunctionNamespaceURI<string> To modify or change the default function namespaceURI. Defaults tohttp://www.w3.org/2005/xpath-functions. Defining the default function namespaceURI in the xpath expression overwrites this option.
    • functionNameResolver<({prefix, localName}, arity) => {namespaceURI, localName}> To influence the function name resolving algorithm. Useful to extend the protected namespaces, such as thefn namespace.

Example

const{evaluateXPath,evaluateXPathToBoolean,evaluateXPathToString,evaluateXPathToFirstNode,evaluateXPathToNumber,}=require('fontoxpath');constdocumentNode=newDOMParser().parseFromString('<xml/>','text/xml');console.log(evaluateXPathToBoolean('/xml => exists()',documentNode));// Outputs: trueconsole.log(evaluateXPathToString('$foo',null,null,{foo:'bar'}));// Outputs: "bar"// We pass the documentNode so the default INodesFactory can be used.console.log(evaluateXPathToFirstNode('<foo>bar</foo>',documentNode,null,null,{language:evaluateXPath.XQUERY_3_1_LANGUAGE,}).outerHTML);// Outputs: "<foo>bar</foo>"// We pass the Math namespaceURI for the pi() function to be usedconsole.log(evaluateXPathToNumber('pi()',documentNode,undefined,{},{language:evaluateXPath.XQUERY_3_1_LANGUAGE,defaultFunctionNamespaceURI:'http://www.w3.org/2005/xpath-functions/math',}));// Outputs: Math.PI (3.14...)

Creating typed values

When having to pass JavaScript values as variables to anevaluateXPath call you can create a typedvalue of it to ensure it will be used as that specific type.

If you do not do this and instead pass a plain JavaScript value as variable it will get convertedautomatically into a type which fits but you will not be able to control the exact type.

constintegerValueFactory=createTypedValueFactory('xs:integer');constintegerValue=integerValueFactory(123,domFacade);// Will return true as we specified it to be an xs:integerevaluateXPathToBoolean('$value instance of xs:integer',null,null,{value:typedValue,}),// Will return false as JavaScript numbers are by default converted to an xs:doubleevaluateXPathToBoolean('$value instance of xs:integer',null,null,{value:123,}),

Debugging

FontoXPath can output a basic trace for an error if thedebug option is set totrue. This isdisabled by default because of performance reasons.

evaluateXPathToBoolean(`if (true()) then  zero-or-one((1, 2))else  (1, 2, 3)`,null,null,null,{debug:true});// Throws:1:if(true())then2:zero-or-one((1,2))^^^^^^^^^^^^^^^^^^^3:else4:(1,2,3)Error: FORG0003:Theargumentpassedto fn:zero-or-onecontainedmorethanoneitem.at<functionCallExpr>:2:3-2:22at<ifThenElseExpr>:1:1-4:12

Besides errors, thefn:trace function can be used to output information to the developer console.

Performance

FontoXPath can use the Performance API to provide some insight in the speed of XPaths. To use it,first give FontoXPath an implementation of the Performance interface:

import{profiler}from'fontoxpath';profiler.setPerformanceImplementation(window.performance);// or global.performance or self.performance, depending on you surroundings// And start profiling all XPath / XQuery usageprofiler.startProfiling();

At some point, you may want to get a summary of all evaluated XPaths:

constsummary=profiler.getPerformanceSummary();

This summary contains an array of XPaths, their execution times, their total runtime and theiraverage runtime. Starting a performance profile will also output measurements on the timeline of theperformance profiler of the browser.

Modifying XML

To modify XML you can useXQuery Update Facility 3.0 asfollowing

evaluateUpdatingExpressionSync(xpathExpression,contextNode,domFacade,variables,options);

The arguments are the same asevaluateXPath. This returns anObject, the object has axdmValue andpendingUpdateList. ThexdmValue is the result of query as if it was run usingevaluateXPath withevaluateXPath.ANY_TYPE asreturnType. ThependingUpdateList is an<Object[]> in which each entry represents anupdateprimitive where thetype identifiesthe update primitive.

The pending update list can be executed using

executePendingUpdateList(pendingUpdateList,domFacade,nodesFactory,documentWriter);
  • pendingUpdateList<Object[]> The pending update list returned byevaluateUpdatingExpression.
  • domFacade<IDomFacade> SeeevaluateXPath. The default will use nodes from thependingUpdateList.
  • nodesFactoryINodesFactory AINodesFactory implementationwhich will be used for creating nodes. Defaults to an implementation which uses properties andmethods of nodes from thependingUpdateList.
  • documentWriter<IDocumentWriter> AnIDocumentWriterimplementation which will be used for modifying a DOM. Defaults to an implementation which usesproperties and methods of nodes from thependingUpdateList.

Example

const{ evaluateUpdatingExpression, executePendingUpdateList}=require('fontoxpath');constdocumentNode=newDOMParser().parseFromString('<xml/>','text/xml');constresult=evaluateUpdatingExpressionSync('replace node /xml with <foo/>',documentNode)executePendingUpdateList(result.pendingUpdateList);console.log(documentNode.documentElement.outerHTML);// Outputs: "<foo/>";

An example of using XQUF with XQuery modules:

registerXQueryModule(`module namespace my-custom-namespace = "my-custom-uri";(:~Insert attribute somewhere~:)declare %public %updating function my-custom-namespace:do-something ($ele as element()) as xs:boolean {if ($ele/@done) then false() else(insert nodeattribute done {"true"}into $ele, true())};`);// At some point:constcontextNode=null;constpendingUpdatesAndXdmValue=evaluateUpdatingExpressionSync('ns:do-something(.)',contextNode,null,null,{moduleImports:{ns:'my-custom-uri'}});console.log(pendingUpdatesAndXdmValue.xdmValue);// this is true or false, see functionexecutePendingUpdateList(pendingUpdatesAndXdmValue.pendingUpdateList,null,null,null);// At this point the context node will have its attribute set

Global functions

To register custom functions. They are registered globally.

registerCustomXPathFunction(name,signature,returnType,callback);
  • name{namespaceURI: string, localName: string} The function name.
  • signaturestring[] The arguments of the function.
  • returnTypestring The return type of the function.
  • callbackfunction The function itself.

Example:

constfontoxpath=require('fontoxpath');// Register a function called 'there' in the 'hello' namespace:fontoxpath.registerCustomXPathFunction({namespaceURI:'hello',localName:'there'},['xs:string'],'xs:string',(_,str)=>`Hello there,${str}`);// and call it, using the BracedUriLiteral syntax (Q{})constout=fontoxpath.evaluateXPathToString('Q{hello}there("General Kenobi")');// Or by using a prefix instead:constURI_BY_PREFIX={hi:'hello'};constout2=fontoxpath.evaluateXPathToString('hi:there("General Kenobi")',null,null,null,{namespaceResolver:(prefix)=>URI_BY_PREFIX[prefix],});

Including modules

Use theregisterXQueryModule function to register an XQuery module. Registered modules will beglobally available, but will have to be imported before they can be used.

Example:

constfontoxpath=require('fontoxpath');fontoxpath.registerXQueryModule(`module namespace test = "https://www.example.org/test1";declare %public function test:hello($a) {"Hello " || $a};`);// Import the module using the XQuery way:fontoxpath.evaluateXPathToString(`import module namespace test = "https://www.example.org/test1";(: Invoke the test:hello function :)test:hello('there')`,null,null,null,{language:fontoxpath.evaluateXPath.XQUERY_3_1_LANGUAGE});// Or by using the moduleImports API, which can be used in XPath contexts as wellfontoxpath.evaluateXPathToString(`(: Invoke the test:hello function :)test:hello('there')`,null,null,null,{moduleImports:{test:'https://www.example.org/test1'}});

Typescript

We supportTypeScript; and expose a minimal Node type.You can use generic types to get the type of the DOM implementation you are using without having tocast it.

constmyNodes=evaluateXPathToNodes<slimdom.Node>('<foo>bar</foo>',null,null,null,{language:evaluateXPath.XQUERY_3_1_LANGUAGE,});// Type of myNodes is: slimdom.Node[] .

Compiling queries to JavaScript for better execution performance

⚠️ Warning: this functionality considered experimental.⚠️

FontoXPath supports compiling a small but useful subset of XPath 3.1 to pure JavaScript code. Queryexecution performance benefits from this: execution speed can be 2 to 7 times higher than when usingevaluateXPath, according toour benchmarks.

Two API's provide this functionality:

  • compileXPathToJavaScript Compiles a query and its return type to JavaScript code. This resultshould be evaluated to a function, for example withnew Function.
  • executeJavaScriptCompiledXPath Evaluates a to a function evaluated compiled query (see theexample below) and applies it to the given context node, returning its resulting value.

Supported functionality

Here is a list of supported functionality so you can determine if compiling to JavaScript issuitable for your project. These functionalities are supported:

  • Absolute and relative path expressions, including an arbitrary amount of steps.
  • child,self,parent andattribute axes.
  • NodeTests: NameTest, ElementTest, Wildcard and TextTest.
  • Predicates (the[ and] in/xml[child::title]).
  • Logical operators (and andor).
  • Compares (compare string to string and node to string).
  • Return typesevaluateXPath.NODES_TYPE,evaluateXPath.BOOLEAN_TYPE,evaluateXPath.FIRST_NODE_TYPE,evaluateXPath.STRING,evaluateXPath.ANY.

Functions, XQuery and other more advanced features arenot supported (yet).

Example usage:

import{compileXPathToJavaScript,CompiledXPathFunction,evaluateXPath,executeJavaScriptCompiledXPath,}from'fontoxpath';constdocumentNode=newDOMParser().parseFromString('<p>Beep beep.</p>','text/xml');constcompiledXPathResult=compileXPathToJavaScript('/child::p/text()',evaluateXPath.BOOLEAN_TYPE);if(compiledXPathResult.isAstAccepted===true){// Query is compiled succesfully, it can be evaluated.constevalFunction=newFunction(compiledXPathResult.code)asCompiledXPathFunction;console.log(executeJavaScriptCompiledXPath(evalFunction,documentNode));// Outputs: true}else{// Not supported by JS codegen (yet).}
Ideas to improve the example to better fit your project:
  • If a query could not be compiled to JavaScript, fall back on the stableevaluateXPath function.
  • Add caching so compiling andnew Function does not have happen more than once per unique query.
  • Store compiled code to disk.

Features

Note that this engine assumesXPath 1.0 compatibilitymode turned off.

Not allXPath 3.1 functions are implemented yet. Weaccept pull requests for missing features. A full list of supported queries can be found ontheplayground. Select the 'Report on which functions areimplemented' example to get a full dynamic report!

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)
  • Some other miscellaneous functions
  • XML parsing
  • Thetreat as operator
  • Some parts of FLWOR expressions

For all available features, see the unit tests, or just try it out on theDemopage.

Extensions to the spec

FontoXPath implements a single function that is public API:fontoxpath:version() as xs:string. Itresides in the 'http://fontoxml.com/fontoxpath' namespace. Call it to check what version ofFontoXPath you are running.

Compatibility

This engine is pretty DOM-agnostic, it has a good track record with the browser DOM implementationsandslimdom.js. There are a number of known issues with otherDOM implementations such asxmldom because it does not follow theDOM spec on some features including namespaces.

When using namespaces in general, be sure to not use the HTML DOM since it does not always implementnamespaces how you'd expect!

Contribution

If you have any questions on how to use FontoXPath, or if you are running into problems, just file agithub issue! If you are looking to contribute, we have aContribution Guidethat should help you in getting your development environment set up.

About

A minimalistic XPath 3.1 implementation in pure JavaScript

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors31

Languages


[8]ページ先頭

©2009-2025 Movatter.jp