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

<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.)

Related

XSLT select unknown nodes

I have some trouble with a special XML-document.
The XML has only 3 nodes like the below the example:
<solidName>
<unknownNodeName>
<everytimeTheSame>blablabla</everytimeTheSame>
<everytimeTheSame2>blablabla</everytimeTheSame2>
<unknownChildNodeName>
<everytimeTheSame>blablabla</everytimeTheSame>
<everytimeTheSame2>blablabla</everytimeTheSame2>
</unknownChildNodeName>
</unknownNodeName>
</solidName>
I need to select the unknownNodeName and the unknownChildNodeName to use the function
<xsl:value-of select="everytimeTheSame"/> and so on. I tried to use a for-each select function, but I found no way to get the unknow Name of the node.
Are there any possibilities for my problem? Is it possible to say <xsl:for-each select ="NodeNumberX"> or things like that <xsl:for-each select ="/*/*">
to use the function and so on
is not a good description of what you want to do.
Selecting "unknown nodes" (i.e. nodes whose name you do not know, but you do know their position in the document's tree) is trivial by using the * symbol in an XPath expression, possibly combined with a positional predicate.
For example:
/*/*/*[2]
will select the <everytimeTheSame2> element in your example.

ignore a case and apply templates

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)]" />

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.

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.

XSLT: attribute value used as numeric predicate

Given
<xsl:variable name="datePrecision" as="element()*">
<p>Year</p>
<p>Month</p>
<p>Day</p>
<p>Time</p>
<p>Timestamp</p>
</xsl:variable>
The expression
$datePrecision[5]
returns a nodeSet containing one text node with value "Timestamp", as expected.
Later in a template, with a context element having an attribute
#precision="5"
I try the following expressions but all return an empty string:
$datePrecision[#precision]
$datePrecision[number(#precision)]
$datePrecision[xs:decimal(#precision)]
However, the following sequence does what I want
<xsl:variable name="prec" select="number(#precision)"/>
... $datePrecision[$prec] ...
Using Oxygen/XML's debugger I've stepped to the point where the expression is about to be evaluated and display the following in the watch window:
Expression Value Nodes/Values Set
-------------------------- --------------- -----------------------
$datePrecision[5] Node Set(1) #text Timestamp
#precision Node Set(1) precision 5
$datePrecision[#precision]
number(#precision) 5
$datePrecision[number(#precision)]
$prec 5
$datePrecision[$prec] Node Set(1) #text Timestamp
Obviously I've missed something fundamental about how attribute nodes are atomized for use in a predicate, but can't find anything in the docs (Michael Kay's XSLT/XPATH 2.0, 4th ed) that would explain this difference.
Can someone explain why this is occurring, and point me to where, in either the XSLT 2.0 spec or Michael Kay's book, where this is described?
(the XSLT processor is Saxon-PE 9.2.0.3)
Obviously I've missed something
fundamental
Yes. The XPath expression:
$datePrecision[#precision]
means: all elements in $datePrecision that have an attribute named precision.
But you want #precision to mean the attribute named precision of the currnet node that is matched by the template.
XSLT provides the current() function exactly for this purpose. Use:
$datePrecision[current()/#precision]
UPDATE: As Martin Honnen hinted, the OP probably wants to get the 5th element out of $datePrecision -- something not immediately visible from the description of the problem. In this case, it may be necessary to use:
$datePrecision[position() = current()/#precision]