Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Evaluating xpath with context in a sequence #317

Open
egh opened this issue Apr 8, 2021 · 5 comments
Open

Evaluating xpath with context in a sequence #317

egh opened this issue Apr 8, 2021 · 5 comments

Comments

@egh
Copy link
Contributor

egh commented Apr 8, 2021

Hi,

I am not an xpath expert by any means, so please bear with me.

I am not sure how to get this expected result:

import { evaluateXPathToNodes, evaluateXPathToNumber } from "fontoxpath";
import { sync } from "slimdom-sax-parser";
test("node list context", () => {
  const xml = "<doc><item>item1</item><item>item2</item><item>item3</item></doc>";
  const dom = sync(xml);
  const nodeList = evaluateXPathToNodes("/doc/item", dom);
  expect(evaluateXPathToNumber("position()", nodeList[1])).toEqual(2);
});

Is there something that I am missing in understanding how xpath is expected to work? I would expect that there is some way to get position() to evaluate to 2 in this case, since it is the second item in the result set. I tried implementing a IDomFacade but that didn't seem to be used.

Is there any advice you can provide here? The code seems to include the idea of a contextSequence in the DynamicContext but I can't seem to pass that in to the evaluate xpath functions.

@DrRataplan
Copy link
Collaborator

Hello there!

Indeed, to make the XPath APIs easy to use, you can only input a singleton sequence as the start (parameter called contextItem). That is usually exactly what you want. In most cases, you have an element and you want to traverse a path or determine some property of that element. In your case, making this a single XPath query just works: /doc/item/position()[2] or something.

I think you have a question behind that question. Are you executing XPaths in multiple phases with the same context? Are you implementing some kind of spec? I think your question makes sense, I would love some more context so I can think along!

If you want to override the semantics of the position() function, we might have something to achieve that.

Regards,

Martin

@egh
Copy link
Contributor Author

egh commented Apr 9, 2021

@DrRataplan Thanks for getting back to me! Indeed, there is some background: I was implementing a proof of concept XSLT processor in TypeScript (https://github.com/egh/xjslt).

So in this case, we might be iterating through a node set with for-each or apply-templates, and the template might be checking if this node was the last item in the set, for example.

Hope that helps! Thanks for the work.

@DrRataplan
Copy link
Collaborator

Awesome! I was wondering when someone would be brave enough to implement XSLT with FontoXPath.

When I tried to hack an XSLT together, I ran into a number of 'quirks'. For example, XSLT uses XPaths like <xslt:template match="p"> to match any paragraphs. Blindly compiling this to XPath would result in an XPath that traverses the child axis. Turns out XSLT does not really use XPath as the value of a match attribute, rather a syntax called XSL Patterns: https://www.w3.org/TR/xslt-30/#patterns. This is also why we use vanilla XPaths in Fonto instead: our template engine basically matches nodes to selectors that look like self::p.

For the semantics of a position() function, I think we could consider some new API that just gives you access to way lower level APIs. Something like evaluateTodoSomeLogicalName(xpath: IAST|string, contextSequence: {contextItems: Value[], contextItemIndex: number}, additionalOptions...) as Value[]. With Value being {type: typeString, value: Node|number|string|boolean} or something.

This way, you can just input a whole sequence into FontoXPath. We will take care of the proper results of any functions that use the context.
I bet you're also going to be needing direct access to the actual values XPath resolves to, not the JavaScript lookalikes. In other words, I bet you're going to need to know something is an xs:decimal as opposed to some numeric type. Same with array() and map(). That's why I added a Value type there.

I think I'm OK with building such an API, and I am of course even more willing to accept such a PR 😉, but before I want to give the go-ahead, I'd like to know your thoughts on the Patterns issue I outlined above.

Regards,

Martin

@egh
Copy link
Contributor Author

egh commented Apr 12, 2021

Hi Martin - Excellent, thanks for all the info. As this is an experimental implementation, the XSLT version has been a moving target. I was originally implementing against XSLT 1, which uses a simpler algorithm to determine a match. I am now trying to target XSLT 2, which uses a more complex algorithm which is nonetheless slightly different from XSLT 3.

As far as I can tell, however, all of these can be implemented according to spec with (a) some compile-time syntax checking and processing to generate the xpath and then interpreting the results of evaluating the xpath appropriately. But I am not sure, I've only implemented the easier algorithm of XSLT 1 and I haven't yet implemented, e.g. evaluating an xpath with variables containing node sets, or sorting, or anything other than just evaluating an xpath against the document.

In summary, I think that you are correct that a lower-level API is probably the appropriate solution, but at this stage of my implementation I can't be sure. I'd be happy to circle back later with a PR implementing this low-level API call. And yes, the actual values is something else I'm going to need to be implementing: I've just implemented variable but without bothering types or the as attribute.

@DrRataplan
Copy link
Collaborator

I think the PR #411 might solve a lot of these issues. I just ran into the ANY type atomizing attributes. This PR will bring a new return type that just is the (value of the) result you return, close to zero changes regarding attributes, sometimes returning arrays, etcetera.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants