Why do I have to assign this node to a variable in my stylesheet? - xslt

I'm working on an XSLT stylesheet. I have a a node (node A) with a bunch of children, and I'm looping through another node's (node B) children. I'm trying to do something each time a child of node B is also a child of node A, so I have this code:
<xsl:if test="$prodbins/bin[./text()=/root/Line[1]/Element[6]/text()]">
But that doesn't work (the test fails; the expression returns false) even though the left and right side of the expression, when evaluated separately, are equal.
But when I do this:
<xsl:variable name="curbin" select="/root/Line[1]/Element[6]/text()"/>
<xsl:if test="$prodbins/bin[./text()=$curbin]">
The expression evaluates to true. Why do I have to use the $curbin variable to get the result I'm expecting?

Can you try <xsl:if test="$prodbins/bin[./text()=current()/root/Line[1]/Element[6]/text()]"> (notice current() function). The reason why it does not work in your original expression is that because you query a variable and / looks up the root node of the content of the variable and not the source document you are transforming. current() should return the context element for the template you are in.

My guess is that $prodbins/bin is a node(-set) belonging to a different document than the document that contains the nodes that are being compared to.
In the expression:
$prodbins/bin[./text()=/root/Line[1]/Element[6]/text()]
the subexpression
/root/Line[1]/Element[6]/text()
selects from the same document as the one that is the document from which the $prodbins/bin nodes are selected.
One way to specify successfully the wanted comparisson is:
<xsl:variable name="vDoc" select="/"/>
<xsl:if test="$prodbins/bin[./text()=$vDoc/root/Line[1]/Element[6]/text()]">

Related

XSLT 2.0 using key with except returns unexpected result

NB: title changed to reflect the problem better.
My xml documents contain an element <tei:seg #type #xml:id #corresp> which wrap little 'stories'. The attribute #corresp allows me to connect these stories to a master story. For example, these seg are all connected by their #corresp:
doc1.xml//seg[#type='dep_event' #corresp='#JKL' #xml:id='doc1-05']
doc2.xml//seg[#type='dep_event' #corresp='#JKL' #xml:id='doc2-06']
doc6.xml//seg[#type='dep_event' #corresp='#JKL' #xml:id='doc6-03']
My objective is: when the XSLT template finds a #corresp, find other seg in other documents with the same #corresp and output their respective `#xml:id``
So, in the above example, if the current seg was #xml:id='doc1-05', the template outputs a list: Corresponds to doc2-06, doc6-03
Until I can solve the current problems with XSLT collection() in eXist-DB, I'm falling back on my previous solution: a 'TEI corpus' xml document which maintains a master list of all related tei-xml documents via xi:include. This way I provide a single document node whereby the processor can access and search all the xml documents.
So, I declare the corpus document:
<xsl:variable name="corpus" select="doc('ms609_corpus.xml')"/>
Then create a key for the #corresp:
<xsl:key name="correspkey" match="//tei:seg[#type='dep_event' and #corresp]" use="#corresp"/>
Then I use the key with the doc() to search:
<xsl:when test="tei:seg[#type='dep_event' and #corresp]">
<xsl:variable name="correspvar"
select="data(self::seg[#type='dep_event' and #corresp]/#corresp)"/>
<xsl:text>Corresponds to </xsl:text>
<xsl:value-of select="data($corpus/(key('correspkey',$correspvar) except $correspvar)/#xml:id)" separator=", "/>
</xsl:when>
It returns the results, but the except should exclude the current #corresp. Yet it is included in the results.
The except operator works on sequences of nodes based on node identity, see https://www.w3.org/TR/xpath20/#combining_seq defining
The except operator takes two node sequences as operands and returns a
sequence containing all the nodes that occur in the first operand but
not in the second operand ... All these operators eliminate duplicate
nodes from their result sequences based on node identity
Based on that I think you simply want
<xsl:value-of select="$corpus/(key('correspkey', current()/#corresp) except current())/#xml:id)" separator=", "/>
Using data on nodes which atomizes nodes to values and then trying to use except which works on nodes doesn't seem to make sense to me.

Xpath - Select everything that is not the first p

I am trying to select every node in an XHTML file that is not the first P tag.
I'm modifying an existing stylesheet (h2d.xsl, for those who might look for it later) and currently the expression is:
<xsl:apply-templates select="(body/*|body/text()|body/comment())[1]" mode="creating-content-before-section"/>
And essentially I'd like to modify to skip p[1]. I tried things like
<xsl:apply-templates select="(body/*[not(self::p[1])]|body/text()|body/comment())[1]" mode="creating-content-before-section"/>
But that does not work...
Any idea XSLT gurus?
The following XPath should return any child of body element except the first p child element :
body/*[not(self::p and count(preceding-sibling::p)=0)]

xsl:if at least one child node exists

<node>
<node1><node11/></node1>
<node2/>
</node>
I want my XSLT to check
<xsl:if test="If at least 1 child node exists">
Only node1 can pass the if condition
</xsl:if>
Thanks for any reply.
Firstly, be careful with your terminology here. Do you mean "node" or do you mean "element". A node can be an element, comment, text or processing-instruction.
Anyway, if you do mean element here, to check at least one child element exists, you can just do this (assuming you are positioned on the node element in this case.
<xsl:if test="*">
Your comment suggests only "node1" can pass the if condition, so to check the existence of a specific element, do this
<xsl:if test="node1">
In the context of the node you are testing, this should work to test whether a node has child elements:
<xsl:if test="*">
Only node1 can pass the if condition
</xsl:if>
If you actually meant nodes (which would include text nodes), then this would work to include text nodes:
<xsl:if test="node()">
Only node1 can pass the if condition
</xsl:if>
But <node> would also pass this test (<node2> wouldn't). I assumed you were only speaking in the context of <node>'s child nodes, but perhaps not?
Expressions that match a node are truthy, whilst expressions that don't match anything are falsy, so:
<xsl:if test="node()">
...
</xsl:if>
However, your question and the implied condition "Only node1 can pass the if condition" are at odds with the example. Both node and node1 have child nodes, so both would pass this if condition.
To limit it strictly to node1, you have to either ensure that the template context is appropriate, or check that the node in question is not the documentElement.
The wording of the question is unclear but I think you just want to process child nodes that have themselves got children (ie grandchildren of the current node)
<xsl:template match="node">
do stuff for node
<xsl:apply-templates select="*[*]"/>
</xsl:template>
will just apply templates to node1 as it has a child node, it will not apply templates to node2.

Saxon error "XPTY0019: Required item type of first operand of '/' is node(); supplied value has item type xs:string"

I have an XSL program which in turn generates an XSL program, which depending on the input might look like this:
<xsl:variable name="patterns"/> <!--empty in this particular case-->
<xsl:template name="token">
<xsl:for-each select="$patterns/pattern">
...
When I then run the generated stylesheet, Saxon, bless its heart, is apparently doing some kind of static analysis and complains:
XPTY0019: Required item type of first operand of '/' is node(); supplied value has item type xs:string
and won't even compile the stylesheet.
My workaround was to generate a dummy element in the $patterns nodeset, but is there any cleaner approach here, or way to suppress the compile error?
According to http://www.w3.org/TR/xslt20/#variable-values, "If the variable-binding element has empty content and has neither a select attribute nor an as attribute, then the supplied value of the variable is a zero-length string.".
So you need to change that, for instance by doing <xsl:variable name="patterns" select="()"/> to bind an empty sequence as the variable value.
In XSLT 1.0 (the same would work also with XSLT 2.0) use:
<xsl:variable name="patterns" select="/.."/>
This provides to the XSLT processor the information, necessary to conclude that the type of the $patterns variable is node-set.

Go up one node and see if the parent node equates a particular node in XSLT

I want to see if the parent node of the current node I am referring to is equal to a particular value. I did it as follows but no use.
eg.
<v:name>
<v:age>
when at "age" I tried <xsl:if test='.. = v:name'>. But it is not correct. What is the correct way? Can someone help?
I am inside a template which is true for both v:name and v:age. There are v:age's which are not children of v:name. I want to ensure that v:age I am referring to is a child of a v:name. That is what I want inside the test attribute.
From a comment by the OP:
I am inside a template which is true for both v:name and v:age.
There are v:age's which are not children of v:name. I want to
ensure that v:age I am referring to is a child of a v:name. That
is what I want inside the test attribute
Use:
parent::v:name
And this in a xsl:if becomes:
<xsl:if test="parent::v:name">
<!-- Whatever processing is necessary. -->
</xsl:if>
Use <xsl:if test="../v:name = 'somevalue'></xsl:if> or <xsl:if test="parent::v:name = 'somevalue'></xsl:if>
I suspect that when you say you want to know if the parent node is "equal" to a particular value, you really mean that you want to know whether its name is equal to a particular value (this would be immediately clear if you gave examples of your input and output).
If my conjecture is correct, use <xsl:if test="parent::xyz">