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

[BUG] fn:transform not evaluating global params correctly #4609

Closed
yamahito opened this issue Nov 7, 2022 · 14 comments · Fixed by #4883
Closed

[BUG] fn:transform not evaluating global params correctly #4609

yamahito opened this issue Nov 7, 2022 · 14 comments · Fixed by #4883

Comments

@yamahito
Copy link

yamahito commented Nov 7, 2022

Describe the bug
When running a transform using fn:transform(), parameters set with a default value (e.g. @select='/*/@xml:id') return an error. Possibly this is because the context for the transformation?

err:XTTE0590 Could not transform input: An empty sequence is not allowed as the value of parameter $documentID

Additionally, when running the transformation using the stylesheet-location option, I get the following error (not sure whether or not it's related):

Failed to invoke method queryPT in class org.exist.xmlrpc.RpcConnection: org.exist.dom.persistent.NodeProxy cannot be cast to org.w3c.dom.Node

Expected behavior
I expect a successful transformation: here is the output when run with Saxon PE (some whitespace added):

map{
    "output":
        <div class="toc">
            <div class="toc__header">
                <h4 class="title">Contents</h4>
            </div>
            <nav aria-label="Side navigation,,," class="toc__chapters">
                <ul class="chapters js-smoothscroll">
                    <li data-tei-id="pressrelease">
                        <a href="/output/pressrelease">Press Release</a>
                    </li>
                    <li data-tei-id="comp">
                        <a href="/output/comp">Main Book</a> (Documents 1 - 28)
                        <ul class="chapters__nested">
                            <li data-tei-documents="d1 d2 d3 d4 d5 d6 d7" data-tei-id="ch1">
                                <a href="/output/ch1">Chapter One</a> (Documents 1 - 7)
                            </li>
                            <li data-tei-documents="d8 d9 d10 d11 d12 d13 d14 d15" data-tei-id="ch2">
                                <a href="/output/ch2">Chapter Two</a> (Documents 8 - 15)
                            </li>
                            <li data-tei-documents="d16 d17 d18 d19 d20 d21" data-tei-id="ch3">
                                <a href="/output/ch3">Chapter Three</a> (Documents 16 - 21)
                            </li>
                            <li data-tei-documents="d22 d23 d24 d25 d26 d27 d28" data-tei-id="ch4">
                                <a href="/output/ch4">Chapter Four</a> (Documents 22 - 28)
                            </li>
                        </ul>
                    </li>
                </ul>
            </nav>
        </div>
}

To Reproduce

I've attached a sample collection that can be loaded to exist to replicate.
fn_transform.zip

xquery version "3.0";

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";

declare option output:indent "yes";

declare option output:method "adaptive";

(: Failed to invoke method queryPT in class org.exist.xmlrpc.RpcConnection: org.exist.dom.persistent.NodeProxy cannot be cast to org.w3c.dom.Node

transform(map{
    "stylesheet-location":  '/db/apps/fn_transform/tei-toc.xsl',
    "source-node":          doc('input.xml')
}):)

(: err:XTTE0590 Could not transform input: An empty sequence is not allowed as the value of parameter $documentID  :)

transform(map{
    "stylesheet-node":  doc('tei-toc.xsl'),
    "source-node":      doc('input.xml')
})

Context (please always complete the following information):

  • eXist-db version: 6.10-SNAPSHOT

Additional context

  • How is eXist-db installed? docker image
  • Any custom changes in e.g. conf.xml? None relevant
@yamahito
Copy link
Author

yamahito commented Nov 7, 2022

@alanpaxton ;)

@dizzzz
Copy link
Member

dizzzz commented Nov 7, 2022

Java Version: 18
please use java11 latest ... a newer version is is not guaranteed to work. we are working on it for exist 7 (a lot of changes needed)

@yamahito
Copy link
Author

java version is a red herring, sorry (I'll remove it): it's running in docker.

@PieterLamers
Copy link

PieterLamers commented Nov 21, 2022

@yamahito The error you are reporting is a transform error. So it is not eXist-db complaining but the stylesheet.

The stylesheet says:

<xsl:param name="documentID" as="xs:string" select="/tei:TEI/@xml:id"/>

So if you do not actively supply a documentID, then my take is that the document returns an empty sequence for /tei:TEI/@xml:id, as the error says.
Maybe the document does not resolve?

I'd suggest making the type a bit looser, to avoid the error, e.g. as="xs:string?". Further (and maybe more the root of the problem), you are calling fn:transform with a map as its argument. I do not see support for this function. I see transform:transform($node-tree as node()*, $stylesheet as item(), $parameters as node()?) in http://exist-db.org/exist/apps/fundocs/index.html. Hope this helps.

@yamahito
Copy link
Author

yamahito commented Nov 28, 2022

@PieterLamers, I think you are confusing fn:transform() with transform:transform(). As you can see from the first link, fn:transform actually does take a map as the argument.

It is a transform error that arises because the @select statement of that parameter is not being properly evaluated. The same stylesheet evaluates /tei:TEI/@xml:id correctly when run using either transform:transform in the eXist-db context, or with Saxon outside of it.

if you do not actively supply a documentID, then my take is that the document returns an empty sequence for /tei:TEI/@xml:id

The meaning of the syntax here is that if I do not actively supply a documentID, then it should retrieve the value using the @select statement from the input tree. See "default values" in the XSLT spec, where it explicitly mentions that the default value can depend on the evaluation context.

So either there is something wrong with how I'm invoking the stylesheet transformation (please tell me if so), or else it is an issue with fn:transform specifically.

@yamahito
Copy link
Author

yamahito commented Nov 28, 2022

See also #4386 for background on fn:transform in eXist-db.

@yamahito yamahito changed the title [BUG] fn:transform problems [BUG] fn:transform not evaluating global params correctly Nov 28, 2022
@PieterLamers
Copy link

@yamahito I was not aware that fn:transform is now in the process of being supported, and I overlooked that you were specifically reporting a bug in a develop version. Still it seems the document is not available when the parameter is evaluated. If you'd relax the parameter type you could see whether you in fact have a document (an input tree, as you call it) (but probably you are already testing that).

@PieterLamers
Copy link

PieterLamers commented Nov 28, 2022

@yamahito while trying to simplify your issue I find a number of strange things, one being that an XSL is always cached: When I change it it will not change its behavior. Update: I see in the XPath 3.1 spec that this is default behavior, to be changed with cache : false() Another is that it is not possible to supply a stylesheet-location, be it with a relative or an absolute path. I can confirm the bug, in that the input tree is not available to the select attribute of the xsl:param.

@yamahito
Copy link
Author

Thanks @PieterLamers : I wonder if the part you can't reproduce is due to the caching behaviour you point out?

I'll try to replicate when I get a chance.

@PieterLamers
Copy link

PieterLamers commented Nov 28, 2022

@yamahito I am not saying (I hope) that I cannot reproduce, I can.

I had some problems setting up the transformation because my iterative changes were not picked up because of the default XSL caching behavior.

I wanted my own document, because I was seeing nodes with namespaces and thought it might have anything to do with a mismatch. But as I said: /*/local-name() is also empty.
Interestingly, <xsl:param name="test" as="xs:string" select="/local-name()"/> returns an empty string, where /*/local-name() returns an empty-sequence. I provided a simple structure with <root> as the top node.

@PieterLamers
Copy link

PieterLamers commented Nov 28, 2022

@yamahito I cannot assign an input-tree-based value to a global variable either. so it appears that there is no context outside of the initial match template.<xsl:variable name="root-id" as="xs:string" select="/root/@id/string()"/> returns the same error, where /root/@id is clearly available in the transformation later on.

@alanpaxton
Copy link
Collaborator

@yamahito So, after lots of experimenting in the source,

  • The "org.exist.dom.persistent.NodeProxy cannot be cast to org.w3c.dom.Node" can be easily fixed, per a suggestion of @adamretter and I've allowed myself to use that in experiments.
    Now, then
  • I can make it work (no err:XTTE0590, correct output) with the attached xqm test, and
  • I can make it err:XTTE0590 if I replace document { with <doc>, so

Question for you (and remember I'm just a Java hacker, fairly ignorant of XSLT and XML)

  • How do you load the input.xml file into eXist ? I think that it is ending up with tags around it, so that when it is being used to set up the transformer's global context, the <TEI ... xml:id> is not at top level and is not read by Saxon; NOTE that we use Saxon underneath to do the transformation..
  • When my .xqm script saves the input as a document { it gets saved without any <doc> elements around it.

Depending on whether <doc> elements are special and may be validly wrapping document content we can decide to special-case them when we set up the context.
fnTransformIssue4609.xqm.txt

@adamretter
Copy link
Member

adamretter commented Mar 31, 2023

I can make it err:XTTE0590 if I replace document { with

The document {} expression constructs an "XML Document Node", whereas the <doc> expression constructs an "XML Element" named doc.

Regardless, as you are storing it into the database (as an XML Document), when it is retrieved from the database using doc('/db/fn_transform_issue_4609/input.xml') it will always have a Document Node, the difference is in the "Document Element" (if you want to see the Java parlance for that, you can look here: https://docs.oracle.com/javase/7/docs/api/org/w3c/dom/Document.html#getDocumentElement()).

  1. The first variant will effectively look like:
document {
   <TEI xmlns="http://www.tei-c.org/ns/1.0" xml:id="output">
   
   ...
   
   </TEI>
}
  1. the second variant with <doc> will look like:
document {
   <doc>
       <TEI xmlns="http://www.tei-c.org/ns/1.0" xml:id="output">
   
       ...
   
       </TEI>
    </doc>
}

@adamretter
Copy link
Member

Closed by #4883

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

Successfully merging a pull request may close this issue.

5 participants