XSLT 2.0 moving a node (created in first step of a multi-step transformation) - xslt

XML and the XSLT 2.0 files for this question are found at https://xsltfiddle.liberty-development.net/6qVRKwX/3
I am trying to 'move' an element ahead of outputting a section of HTML. This element was created during the first part of the transformation) using #mode to insert footnote numbers into the text. The first mode fn-add-marker creates <fn-marker/> to hold the footnote number. The second mode number then inserts incremented footnote numbers. All of this works fine (through to line 52 and then after 68 in the XSLT fiddle).
Now I need to 'move' an element into the sibling element that spawned it in the mode above. I've combined this with the HTML output: the final idea is that element <tei:seg> is transformed into HTML <p> such that this :
<seg type="dep_event">text</seg><fn-marker>incremented no.</fn-marker>
Now becomes this HTML (where seg = p, and fn-marker = sup:
<p>text<sup>incremented no.</sup></p>
ie. where a condition is met, the footnote is brought inside a sibling element to be contained in <p>.
The code I inserted (below) works for 3 of 4 needed steps to accomplish this move. It seems the code associated with step 3 does not locate a value in <fn-marker/>. But if I remove all this, the value is in fact there! It makes me think this is a problem of modes.
The code below does this:
output each instance of <tei:seg #type="dep_event>" into a <p> works
create the <sup>inside the <seg> that meets the sibling condition works
copy the text() content of <fn-marker> into the <sup> that meets the sibling condition does not work
destroy the old <fn-marker>1</fn-marker> works
Referring to line numbers at https://xsltfiddle.liberty-development.net/6qVRKwX/3:
line 56-63:
<xsl:template match="tei:seg[#type='dep_event']">
<p>
<xsl:apply-templates/>
<xsl:if test="following-sibling::node()[1][self::tei:fn-marker]">
<!-- next line of code does not find a value in /text() -->
<sup><xsl:value-of select="./following-sibling::node()[1][self::tei:fn-marker/]text()"/></sup>
</xsl:if>
</p>
</xsl:template>
line 66:
<xsl:template match="tei:fn-marker[preceding-sibling::node()[1][self::tei:seg[#type='dep_event']]]"/>
Thanks in advance.

Related

XSLT 1.0 - change the order of lines

I want to change the order of line cac:InvoiceLine depending on this node:
cac:AdditionalItemProperty/cbc:Value
All InvoiceLines that have Item type=RC must be gruop at the end of lines, and all that have CU must be on the top.
If the mentioned values are the only ones you are concerned about, then it seems like you could just sort alphabetically by that value; see xsl:sort. You could just put this inside the xsl:for-each or xsl:apply-templates where you process your invoice lines:
<xsl:sort select="cac:AdditionalItemProperty/cbc:Value" />
On the other hand, if you only want to output only the line items with the mentioned values, you could select them separately. For example, assuming you have a template which matches your invoice lines, you'd first apply it to the 'CU' ones and then to the 'RC' ones:
<xsl:apply-templates select="cac:InvoiceLine[cac:AdditionalItemProperty/cbc:Value='CU']" />
<xsl:apply-templates select="cac:InvoiceLine[cac:AdditionalItemProperty/cbc:Value='RC']" />

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.

How to apply-templates to all templates except a spefic one

In the code bellow I'm applying all the templates to the element chapter (in the end of the code) but I would like to know how it is possible to apply all the templates to this element except a spefic one. In this case it's the element title because I'm already selecting it in the line before and it appears repeated in the html file. Someone?
<xsl:template match="chapter">
<h3>
<a name="{#id}"><xsl:value-of select="title"/></a>
</h3>
<xsl:apply-templates/>
</xsl:template>
Output:
<h3>Title</h3>
Title<br>
Text.
A plain <xsl:apply-templates/> is equivalent to <xsl:apply-templates select="node()" />, i.e. all child nodes. You can exclude certain nodes by using the XPath 2.0 except operator, e.g.
<xsl:apply-templates select="node() except title" />
This would select all child nodes except those that are elements with the name title. If you are only interested in child elements (not text nodes etc.) then you could use * except title instead.
The except operator essentially implements set difference - you're not limited to simple element names on the right, you can use any expression that returns a sequence of nodes, e.g.
node() except (title | div[#class = 'heading'])
X except Y selects all nodes that are in the sequence selected by X and not also in the sequence selected by Y

Break the XSLT for-each loop when first match is found

I am having trouble to display the first matching value, like
<test>
<p>30</p>
<p>30{1{{23{45<p>
<p>23{34</p>
<p>30{1{98</p>
</test>
<test2>
<p1>text</p1>
</test2>
So i want to loop through the <test></test> and find the value of <p> node whose string length is greater than 2 and that contains 30. I want only the first value.
so i tired the following code
<xsl:variable name="var_test">
<xsl:for-each select="*/*/test()>
<xsl:if string-length(p/text())>2 and contains(p/text(),'30'))
<xsl:value-of select="xpath">
</xsl:variable>
the problem is the var_test is being null always.
if i try directly with out any variable
<xsl:for-each select="*/*/test()>
<xsl:if string-length(p/text())>2 and contains(p/text(),'30'))
<xsl:value-of select="xpath">
I am getting the following output
<p>30{1{23{4530{1{98</p>
but the desired output is
<p>0{1{23{45</p>
so how can i achieve this?
Instead of the for-each, use
<xsl:copy-of select="(*/*/test/p[string-length() > 2 and
contains(.,'30'))] )[1]" />
The [1] selects only the first matching <p>. (Updated: I changed the XPath above in response to #markusk's comment.)
The above will output that <p> element as well as its text content, as shown in your "desired output". If you actually want only the value of the <p>, that is, its text content, use <xsl:value-of> instead of <xsl:copy-of>.
Addendum:
The idea of breaking out of a loop does not apply to XSLT, because it is not a procedural language. In a <xsl:for-each> loop, the "first" instantiation (speaking in terms of document order, or sorted order) of the loop is not necessarily evaluated at a time chronologically before the "last" instantiation. They may be evaluated in any order, or in parallel, because they do not depend on each other. So trying to "break out of the loop", which is intended to cause "subsequent" instantiations of the loop not to be evaluated, cannot work: if it did, the outcome of later instantiations would be dependent on earlier instantiations, and parallel evaluation would be ruled out.

In what order do templates in an XSLT document execute, and do they match on the source XML or the buffered output?

Here is something that has always mystified me about XSLT:
In what order do the templates execute, and
When they execute, do they match on (a) the original source XML, or (b) the current output of the XSLT to that point?
Example:
<person>
<firstName>Deane</firstName>
<lastName>Barker</lastName>
</person>
Here is a fragment of XSLT:
<!-- Template #1 -->
<xsl:template match="/">
<xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
</xsl:template>
<!-- Template #2 -->
<xsl:template match="/person/firstName">
First Name: <xsl:value-of select="firstName"/>
</xsl:template>
Two questions about this:
I am assuming that Template #1 will execute first. I don't know why I assume this -- is it just because it appears first in the document?
Will Template #2 execute? It matches a node in the source XML, but by the time the we get to this template (assuming it runs second), the "firstName" node will not be in the output tree.
So, are "later" templates beholden to what has occurred in "earlier" templates, or do they operate on the source document, oblivious to what has been transformed "prior" to them? (All those words are in quotes, because I find it hard to discuss time-based issues when I really have little idea how template order is determined in the first place...)
In the above example, we have a template that matches on the root node ("/") that -- when it is done executing -- has essentially removed all nodes from the output. This being the case, would this pre-empt all other templates from executing since there is nothing to match on after that first template is complete?
To this point, I've been concerned with later templates not executing because the nodes they have operated on do not appear in the output, but what about the inverse? Can an "earlier" template create a node that a "later" template can do something with?
On the same XML as above, consider this XSL:
<!-- Template #1 -->
<xsl:template match="/">
<fullName>
<xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
</fullName>
</xsl:template>
<!-- Template #2 -->
<xsl:template match="//fullName">
Full Name: <xsl:value-of select="."/>
</xsl:template>
Template #1 creates a new node called "fullName". Template #2 matches on that same node. Will Template #2 execute because the "fullName" node exists in the output by the time we get around to Template #2?
I realize that I'm deeply ignorant about the "zen" of XSLT. To date, my stylesheets have consisted of a template matching the root node, then are completely procedural from there. I'm tired of doing this. I would rather actually understand XSLT correctly, hence my question.
I love your question. You're very articulate about what you do not yet understand. You just need something to tie things together. My recommendation is that you read "How XSLT Works", a chapter I wrote to address exactly the questions you're asking. I'd love to hear if it ties things together for you.
Less formally, I'll take a stab at answering each of your questions.
In what order do the templates execute, and
When they execute, do they match on (a) the original source XML, or (b)
the current output of the XSLT to that
point?
At any given point in XSLT processing, there are, in a sense, two contexts, which you identify as (a) and (b): where you are in the source tree, and where you are in the result tree. Where you are in the source tree is called the current node. It can change and jump all around the source tree, as you choose arbitrary sets of nodes to process using XPath. However, conceptually, you never "jump around" the result tree in the same way. The XSLT processor constructs it in an orderly fashion; first it creates the root node of the result tree; then it adds children, building the result in document order (depth-first). [Your post motivates me to pick up my software visualization for XSLT experiments again...]
The order of template rules in a stylesheet never matters. You can't tell, just by looking at the stylesheet, in what order the template rules will be instantiated, how many times a rule will be instantiated, or even whether it will be at all. (match="/" is an exception; you can always know that it will get triggered.)
I am assuming that Template #1 will
execute first. I don't know why I
assume this -- is it just because it
appears first in the document?
Nope. It would be called first even if you put it last in the document. Template rule order never matters (except under an error condition when you have more than one template rule with the same priority matching the same node; even then, it's optional for the implementor and you should never rely on such behavior). It gets called first because the first thing that always happens whenever you run an XSLT processor is a virtual call to <xsl:apply-templates select="/"/> . The one virtual call constructs the entire result tree. Nothing happens outside it. You get to customize, or "configure", the behavior of that instruction by defining template rules.
Will Template #2 execute? It matches a node in the source XML, but
by the time the we get to this
template (assuming it runs second),
the "firstName" node will not be in
the output tree.
Template #2 (nor any other template rules) will never get triggered unless you have an <xsl:apply-templates/> call somewhere in the match="/" rule. If you don't have any, then no template rules other than match="/" will get triggered. Think of it this way: for a template rule to get triggered, it can't just match a node in the input. It has to match a node that you elect to process (using <xsl:apply-templates/>). Conversely, it will continue to match the node as many times as you choose to process it.
Would [the match="/"
template] pre-empt all other templates
from executing since there is nothing
to match on after that first template
is complete?
That rule preempts the rest by nowhere including <xsl:apply-templates/> in it. There are still plenty of nodes that could be processed in the source tree. They're always all there, ripe for the picking; process each one as many times as you want. But the only way to process them using template rules is to call <xsl:apply-templates/>.
To this point, I've been concerned
with later templates not executing
because the nodes they have operated
on do not appear in the output, but
what about the inverse? Can an
"earlier" template create a node that
a "later" template can do something
with?
It's not that an "earlier" template creates a new node to be processed; it's that an "earlier" template in turn processes more nodes from the source tree, using that same instruction (<xsl:apply-templates). You can think of it as calling the same "function" recursively, with different parameters each time (the nodes to process as determined by the context and the select attribute).
In the end, what you get is a tree-structured stack of recursive calls to the same "function" (<xsl:apply-templates>). And this tree structure is isomorphic to your actual result. Not everyone realizes this or has thought about it this way; that's because we don't have any effective visualization tools...yet.
Template #1 creates a new node called
"fullName". Template #2 matches on
that same node. Will Template #2
execute because the "fullName" node
exists in the output by the time we
get around to Template #2?
Nope. The only way to do a chain of processing is to explicitly set it up that way. Create a variable, e.g., $tempTree, that contains the new <fullName> element and then process it, like this <xsl:apply-templates select="$tempTree">. To do this in XSLT 1.0, you need to wrap the variable reference with an extension function (e.g., exsl:node-set()), but in XSLT 2.0 it will work just as is.
Whether you're processing nodes from the original source tree or in a temporary tree that you construct, either way you need to explicitly say what nodes you want to process.
What we haven't covered is how XSLT gets all its implicit behavior. You must also understand the built-in template rules. I write stylesheets all the time that don't even include an explicit rule for the root node (match="/"). Instead, I rely on the built-in rule for root nodes (apply templates to children), which is the same as the built-in rule for element nodes. Thus I can ignore large parts of the input, let the XSLT processor automatically traverse it, and only when it comes across a node I'm interested in will I do something special. Or I could write a single rule that copies everything recursively (called the identity transform), overriding it only where necessary, to make incremental changes to the input. After you've read "How XSLT Works", your next assignment is to look up the "identity transform".
I realize that I'm deeply ignorant
about the "zen" of XSLT. To date, my
stylesheets have consisted of a
template matching the root node, then
are completely procedural from there.
I'm tired of doing this. I would
rather actually understand XSLT
correctly, hence my question.
I applaud you. Now it's time to take the "red pill": read "How XSLT Works"
Templates always match in the source XML. So the order doesn't really matter, unless 2 or more templates match the same node(s). In that case, somewhat counter-intuitively, the rule with the last matching template is triggered.
In your 1st example Template #1 runs because when you start processing the input xml it begins at the root and that is the only template in your stylesheet that matches the root element. Even if it was 2nd in the stylesheet it would still run 1st.
In this example template 2 will not run as you have already processed the root element using template 1 and there are no more elements to process after the root. If you did want to process other elements using additional templates you should change it to.
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
This then allows you to define a template for each element you are interested in and process the xml in a more logical way, rather than doing it procedurally.
Also note that this example will not output anything as at the current context (the root) there is no firstName element, only a person element so it should be:
<xsl:template match="/">
<xsl:value-of select="person/firstName"/> <xsl:value-of select="person/lastName"/>
</xsl:template>
I find it easier to think that you are stepping through the xml, starting at the root and looking for the template that matches that element then following those instructions to generate teh output. The XSLT transforms the input document to the output so the output doucument is empty at the start of the transformation. The output is not used as part of the transformation it is just the output from it.
In your 2nd example Template #2 will not execute because the template is run against the input xml not the output.
Evan's answer is basically a good one.
However one thing which does seem to be lacking is the ability to "call" up chunks of code without doing any matching. This would - at least in some people's opinion - enable much better structuring.
I have made a small example in an attempt to show what I mean.
<xsl:template match="/" name="dotable">
<!-- Surely the common html part could be placed somewhere else -->
<!-- the head and the opening body -->
<html>
<head><title>Salary table details</title></head>
<body>
<!-- Comments are better than nothing -->
<!-- but that part should really have been somewhere else ... -->
<!-- Now do what we really want here ... this really is making the table! -->
<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
<tr>
<td><xsl:value-of select="name" /></td>
<td><xsl:value-of select="firstname" /></td>
<td><xsl:value-of select="age" /></td>
<td><xsl:value-of select="salary" /></td>
</tr>
</xsl:for-each>
</table>
<!-- Now close out the html -->
</body>
</html>
<!-- this should also really be somewhere else -->
<!-- This approach works, but leads to horribly monolithic code -->
<!-- Further - it leads to templates including code which is strictly -->
<!-- not relevant to them. I've not found a way round this yet -->
</xsl:template>
However, after fiddling around a bit, and at first making use of the hint that if there are two matching templates the last one in the code will be selected, and then restructuring my code (not all shown here), I achieved this which seems to work, and hopefully generates the correct code, as well as displaying the wanted data -
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- <?xml version="1.0"?>-->
<xsl:template name="dohtml">
<html>
<xsl:call-template name="dohead" />
<xsl:call-template name="dobody" />
</html>
</xsl:template>
<xsl:template name="dohead">
<head>
<title>Salary details</title>
</head>
</xsl:template>
<xsl:template name="dobody">
<body>
<xsl:call-template name="dotable" />
</body>
</xsl:template>
<xsl:template match="/entries" name="dotable">
<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
<tr>
<td><xsl:value-of select="name" /></td>
<td><xsl:value-of select="firstname" /></td>
<td><xsl:value-of select="age" /></td>
<td><xsl:value-of select="salary" /></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template match="/" name="main">
<xsl:call-template name="dohtml" />
</xsl:template>
[Scroll the code above up-down if you can't see it all]
The way this works is the main template always matches - matches on /
This has the chunks of code - templates - which are called.
This now means that it is not possible to match another template on / but it is possible to match
explicitly on a named node, which in this case is the highest level node in the xml - called entries.
A small modification to the code produced the example given above.