ignore a case and apply templates - xslt

I've the below sample line of XML.
Case1:
<para><content-style font-style="bold">1.54</content-style> For the purposes of this book, the only authorities that are strictly speaking decisive are cases decided by the Singapore courts and their predecessors, and the earlier binding decisions of the Privy Council. This relative freedom from authority has its good and bad points. On the minus side, there is often a penumbra of uncertainty surrounding a proposition based upon a foreign case; until our courts have actually accepted the proposition, it can only be treated as tentative. On the plus side, we are not bound to follow a case that is wrong in principle or weak in reasoning. Our courts are at liberty to develop and interpret the law in a manner that is suitable to Singapore’s needs.<page num="17"/></para>
Case 2:
<para><page num="5"/><content-style font-style="bold">1.12</content-style> In the context of the PA, the term ‘firm’ refers collectively to those who entered into partnership with one another and the name under which partners carry on their business (i.e. name of their partnership) is referred to as the
Case3:
<para><page num="5"/><content-style font-style="bold">1.12</content-style> In the context of the PA, the term ‘firm’ refers collectively to those who entered into partnership with one another and the name under which partners carry on their business (i.e. name of their partnership) is referred to as the <page num="6"/>
and i'm using the below XSLT line to apply-templates.
<xsl:apply-templates select="child::node()[not(self::content-style[1] and self::content-style[1]/preceding::page)]"/>
here what i'm trying to achieve is, to apply templates to para content leaving the first page that is first child node of para preceded by content-style and apart from this, though there are any other page the template should work fine. but here in my case, the page, first child of para preceding content-style is also getting caught.
please let me know where am i going wrong.
here in the cases, the output for case one should be catching the page and in t he second case the page should not be caught and int he case 3, page num="5" should be ignored and page num="6" should be caught
Thanks

Ignoring the criteria for page initially, your current condition to check for the first content-style is not going to work...
<xsl:apply-templates select="child::node()[not(self::content-style[1])]" />
This will be true for all content-style elements. The [1] condition in this case is not the position of the node within its parent, but relates to the node just selected, and will be evaluated for each content-style separately. Therefore, the above code simply won't do what you expect.
To test for equality of nodes, consider first setting a variable to hold the unique id of the first content-style
<xsl:variable name="content" select="generate-id(content-style[1])" />
Then, your xsl:apply-templates would initially look like this
<xsl:apply-templates select="child::node()[not(generate-id() = $content)]" />
And to expand this to cope with the page element, check the first following content-style also does not have the same id..
<xsl:apply-templates select="child::node()
[not(generate-id() = $content or self::page[generate-id(following-sibling::content-style[1]) = $content])]"/>
An alternative approach is also possible. Rather than actively select only the nodes you want, just select all nodes, but have template matches to exclude the nodes you don't want. Replace you xsl:apply-tempates with just this...
<xsl:apply-templates />
Then add the following two templates in your code:
<xsl:template match="content-style[not(preceding-sibling::content-style)]" />
<xsl:template match="page[following-sibling::content-style and not(preceding-sibling::content-style)]" />

Related

XSLT - Key() function

I'm a bit confused about this key function:
<xsl:for-each select="article[count(. | key('idkey', #id)[1]) = 1]>
Is there anyone that can briefly explain whats happening in this for-each loop?
The key is: <xsl:key name="idkey" match="/newspapers/newspaper" use="#id"/>
#id is an attribute in newspaper.
Thanks.
The expression key('idkey', #id)[1] selects the first element whose idkey is equal to #id.
The expression count(A|B) = 1 is an insane XSLT 1.0 workaround for testing whether A and B are the same node. (You will also see people using generate-id(A)=generate-id(B) for this.)
Put these together and you are asking whether the current element is the first one in the document that has a particular id value.
This is the basis of the technique called Muenchian Grouping (which becomes redundant in XSLT 2.0).
There is something fishy about the code because the key seems to be matching newspaper id's, not article id's. But perhaps they are related in some way.
In this for-each element
<xsl:for-each select="article[count(. | key('idkey', #id)[1]) = 1]">
The for-each is being applied to the first article element for each #id attribute.
The call key('idkey', #id) is selecting all article elements with the same #id attribute as the current one.
key('idkey', #id)[1] selects the first of all article elements with the same #id.
Because a node cannot appear in a node set more than once, the union . | key('idkey', #id)[1] will contain one node if the current article is the same node as the first article with the same #id. Otherwise it will contain two.
Checking that the value of count() is one selects only the elements that are the first with any #id.
An alternative way of doing this, and the one I prefer, is to use generate-id like this
select="article[generate-id() = generate-id(key('idkey', #id)[1])]"
which checks directly whether the current element is the same one as the first element in the set by comparing their generated IDs.

Idiom for templating similar to other templating engines like Velocity?

I have an XSLT(2.0) file; which takes an input XML data file and creates DDL/SQL Statements.
It works just fine. But it is a bit difficult to maintain, as it contains a lot of formatting information in 'concat' statements like this:
<xsl:value-of select="concat('CREATE USER ',$username,' IDENTIFIED BY ',$password,';',$nl)"/>
What I would prefer to do would be to encode my SQL Statements in a manner like this instead:
<some-enclosing-elements>[...]CREATE USER <username/>, identified by <password/>; [literally a newline here][...]</some-enclosing-elements>
I would perhaps keep this format above in the XML data file itself in a 'lookup' table at the top of the either the XSLT or the data document iself (I can't work out which yet).
Is there a standard idiom that would allow this kind of templating ?
Any ideas ?
By the way; the data document contains many different users to create of course
The AVT approach is just a little bit too devious for my taste. I tend to rely on the implicit concatenation done (in 2.0) by xsl:value-of:
<xsl:value-of select="'CREATE USER', $username, 'identified by', $password"/>
Another approach which I have used in applications where this kind of text templating is significant is to essentially write my own templating engine within XSLT; have a "message file" containing message templates in the form
<message nr="1">CREATE USER <p:user/> IDENTIFIED BY <p:password/></message>
and then write template rules to expand the messages by substituting the parameters.
#xiaoyi is right, showing the main alternative to using concat(). However that's even more notation-heavy than the concat(), since you have to keep repeating <xsl:value-of select="..." />.
A nice alternative would be to use attribute value templates (AVTs):
[...]CREATE USER {username}, identified by {password};
[...]
But ATVs are only available for (certain) attributes, not for text nodes (directly). How do you use them for this purpose?
One way in XSLT 2.0 would be to use an AVT to create a new literal result element with an attribute; specify the value of that attribute using an AVT; and then select the value of the new attribute:
<xsl:variable name="query">
<dummy val="[...]CREATE USER {username}, identified by {password};
[...]" />
</xsl:variable>
<xsl:value-of select="$query//#val" />
Yes that's some significant overhead per formatted string, but there's very little overhead per field within the string. You could do several strings together like this:
<xsl:variable name="queries">
<q val="[...]CREATE USER {username}, identified by {password};
[...]" />
<q val="[...]CREATE TABLE {tablename}, blah blah;
[...]" />
</xsl:variable>
<xsl:value-of select="$queries/q[1]/#val" />
<xsl:value-of select="$queries/q[2]/#val" />
You could use position indices as above, or use an id attribute to identify each string.
I have not seen this method advocated elsewhere, so I'd be curious to hear what others think about it.
Never mind, except...
Given the simpler approach shown by Michael Kay's answer, I don't think there's any point in doing it this way. I guess that explains why others haven't advocated this method. :-)
The only situation I can think of where this approach might still be of use is if you can't use XSLT 2.0, but you do have access to the nodeset() extension function (e.g. in IE or .NET environment). In that case you would need to wrap nodeset( ) around $queries wherever you used it in an XPath expression before /.

how to use two conditions in select conditions in xslt when using Apply template

<xsl:apply-templates mode="block2sequence" select="NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][position()=1] and NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType][position()=1]"/>
why cant i use two conditions in above select condition, can any one suggest me
<xsl:apply-templates mode="block2"
select="NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][position()=1] "/>
why cant i use two conditions in above select condition
I guess this is to mean, "why can't the two conditions be specified in the same predicate?"
The answer is that the expression:
NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId and position() = 1]
isn't equivalent at all to the 1st expression above.
The first expression selects the first Table child of NewDataSet such that the string value of its CTD_CTD_PKG_ID child is equal to the string value of $PackageId. In this case we don't know which child (at which position) of NewDataSet will be selected -- any child that happens to be the first with the specified properties, will be selected.
On the other side, the latter expression selects the first Table child of NewDataSet only if the string value of its CTD_CTD_PKG_ID child is equal to the string value of $PackageId. In this case, if anything is selected, it would be the first Table child.
If you want an equivalent expression to the first one, that has only one predicate, one such expression is:
NewDataSet/Table
[CTD_CTD_PKG_ID =$PackageId
and
not(preceding-sibling::Table[CTD_CTD_PKG_ID =$PackageId ])
]
Update: The OP has published a code snippet:
<xsl:apply-templates mode="block2sequence" select=
"NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][position()=1]
and
NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType][position()=1]"/>
This code will cause an error thrown at compile time by the XSLT processor.
The value of the select attribute is a boolean (expr1 and expr2), however templates in XSLT 1.0 and XSLT 2.0 can only be applied on nodes. A boolean isn't a node -- hence the error.
Solution:
My first guess is that you want templates to be applied on both nodes. If this is so, then use:
<xsl:apply-templates mode="block2sequence" select=
"NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][1]
|
NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType][1]"/>
My second guess is that you want templates applied only on the first of the two nodes. If this is so, then use:
<xsl:apply-templates mode="block2sequence" select=
"(NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId]
|
NewDataSet/Table[CTD_SEQ_NUM =$strXSLMsgType]
)
[1]
"/>
Notes:
Please, learn how to ask a question -- provide all relevant data and explain -- in the question, not in subsequent comments.
Did you know that [1] is equivalent to [position()=1] and is shorter?
You can use two conditions and your expression looks perfectly correct. If it is failing with an error, please tell us the error. If it is not selecting what you want, then (a) show us your source document, and (b) tell us what you want to be selected.
(You know, your question gives so little information, you don't give the impression that you really want an answer.)

XSL beginner question

I want to understand, in general, what this means:
<xsl:template match="foo:barLists[#mode = 'Dummy Filter']"
mode="dummy-filter-cache" priority="2">
I'm looking for some insight to what this does so I may learn a bit about XSL
<xsl:template
This element defines a template. We'll give it data later with an apply-template element
match="foo:barLists[#mode = 'Dummy Filter']"
This template uses the element barLists in the namespace foo which has an attribute of mode which is set to "Dummy Filter". i.e. <foo:barList mode="Dummy Filter"> .... </foo:barList>
mode="dummy-filter-cache"
This tempalte has a mode of "dummy-filter-cache". I have no idea what that means. w3schools.com only says about mode: "Optional. Specifies a mode for this template"
priority="2">
This tempate has a priority of 2. If there's another template which also matches that element with a priority of 1, that one wins.
In general, you are matching specific nodes with specific attributes in an XML file.
I suggest you look at a tutorial on XSL.
mode attribute of xsl:template allows you to create several templates that have same match attribute. With mode you can choose which one of these templates gets applied in different cases. This might be useful if you need to apply the same content several times with different formatting at some of these times.
A template with a mode will be instantiated only when you have set the same mode on an xsl:apply-templates element whose select attribute matches the match attribute on the xsl:template element.
Let's suppose you have templates
<xsl:template match="foo">
and
<xsl:template match="foo" mode="bar">
Then <xsl:apply-templates select="foo" mode="bar"/> will match the template #2 while
<xsl:apply-templates select="foo"/> and <xsl:apply-templates/> will match the template #1.

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.