javascriptxquery

Specifying a node in a document to insert something using fontoxpath


So i am new to both fontoxpath and Xquery.

I would like to select a node in a document, then do an insert.

declare %public %updating function xyz:change($bc as document-node(), $ch as element()) {
  let $contextNode := evaluateXPath($ch/@contextNode, $bc)
  return insert node $ch/element() as last into $contextNode
};

So $ch/@contextNode is an XPath.

When i change the last bit of the insert line into as last into $bc/changes/*[Last()}, i do get a result.

The problem is I don't know how to get evaluateXPath to work, so $contextNode actually point to a node. I keep getting this error when trying several namespaces:

XPST0017: Function evaluateXPath with arity of 2 not registered.

So how can I do this using fontoxpath? Without adding extra dependencies.

Or is this just not yet implemented in fontoxpath?


Solution

  • There is a function evaluateXPath in the JavaScript API of Fontoxpath, if you wanted to use that from XQuery or XPath you would need to use registerCustomXPathFunction (the example just shows how to register and use the extension function with normal XPath, I haven't tried with XQuery update):

    <script type=module>
    // See https://www.npmjs.com/package/fontoxpath documentation.
    import * as fontoxpath from 'https://esm.run/fontoxpath';
    
    const namespaceBindings = { 'mf' : 'http://example.com/mf' };
    
    // Register a function called 'evaluateXPath' in the 'http://example.com/mf' namespace:
    fontoxpath.registerCustomXPathFunction(
        { namespaceURI: 'http://example.com/mf', localName: 'evaluateXPath' },
        ['xs:string', 'item()?'],
        'item()*',
        (_, xpath, contextItem) => fontoxpath.evaluateXPath(xpath, contextItem, null, null, fontoxpath.evaluateXPath.ALL_RESULTS_TYPE, { language: fontoxpath.evaluateXPath.XPATH_3_1_LANGUAGE} )
    );
    
    const exampleXML1 = `<root>
      <item xpath="/root/data/value"/>
      <data>
        <value>foo</value>
        <value>bar</value>
      </data>
    </root>`;
    
    
    const exampleXML1Doc = new DOMParser().parseFromString(exampleXML1, 'application/xml');
    
    console.log(fontoxpath.evaluateXPath('root/item/mf:evaluateXPath(@xpath, .)', exampleXML1Doc, null, null, fontoxpath.evaluateXPath.ALL_RESULTS_TYPE, { namespaceResolver: (prefix) => namespaceBindings[prefix]} ));
    
    </script>

    There also seems a predefined function in the namespace http://fontoxml.com/fontoxpath of the name evaluate that can be used as follows:

    <script type=module>
    // See https://www.npmjs.com/package/fontoxpath documentation.
    import * as fontoxpath from 'https://esm.run/fontoxpath';
    
    const namespaceBindings = { 'fonto' : 'http://fontoxml.com/fontoxpath' };
    
    const exampleXML1 = `<root>
      <item xpath="/root/data/value"/>
      <data>
        <value>foo</value>
        <value>bar</value>
      </data>
    </root>`;
    
    
    const exampleXML1Doc = new DOMParser().parseFromString(exampleXML1, 'application/xml');
    
    console.log(fontoxpath.evaluateXPath(` 
    root/item/fonto:evaluate(string(@xpath), map { '.' : / })`, 
    exampleXML1Doc, null, null, fontoxpath.evaluateXPath.ALL_RESULTS_TYPE, { namespaceResolver: (prefix) => namespaceBindings[prefix]}));
    </script>

    To test with XQuery update try e.g.

    <script type=module>
    // See https://www.npmjs.com/package/fontoxpath documentation.
    import * as fontoxpath from 'https://esm.run/fontoxpath';
    
    const namespaceBindings = { 'fonto' : 'http://fontoxml.com/fontoxpath' };
    
    const exampleXML1 = `<root>
      <item contextNode="/root/data">
        <value>new 1</value>
        <value>new 2</value>
      </item>
      <data>
        <value>foo</value>
        <value>bar</value>
      </data>
    </root>`;
    
    
    const exampleXML1Doc = new DOMParser().parseFromString(exampleXML1, 'application/xml');
    
    const result = fontoxpath.evaluateUpdatingExpressionSync(`
    declare %updating function local:change($bc as document-node(), $ch as element()) {
      let $contextNode := fonto:evaluate($ch/@contextNode/string(), map { '.' : $bc })
      return insert node $ch/element() as last into $contextNode
    };
    local:change(/, root/item)`, 
    exampleXML1Doc, 
    null, 
    null, 
    { namespaceResolver: (prefix) => namespaceBindings[prefix]}
    );
    
    
    fontoxpath.executePendingUpdateList(result.pendingUpdateList);
    console.log(exampleXML1Doc.documentElement.outerHTML);
    
    </script>