Xpath - Select everything that is not the first p - xslt

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

Related

How can I use the concat() function on multiple children within an XPath expression

something like this works fine "concat('a', 'b')" but i can't it to work as part of an existing expression, i.e.
<div class='xyz'>
<strong>the</strong>
<sub> one</sub>
</div>
//div[class='xyz']/concat(/strong/text(), /sub/text())
i'd like it to return 'the one'
i'd like it to return 'the one'
In XPath 1.0, you'd have to do:
concat(div[#class='xyz']/strong, div[#class='xyz']/sub)
In XSLT 1.0, you could do:
<xsl:template match="/">
<xsl:for-each select="div[#class='xyz']/*">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
I hope this is XPath 2.0? Otherwise, concat() cannot appear on the rh-side of the / (or either side of |) in an expression, because it returns an atomic value, not a node. This limitation was removed in XPath 2.0 and up.
XPath 2.0
Your expression is almost correct, the difference being the root node. You started your concat-expressions with /strong and /sub, but these elements are not rooted at the document root. The rh-side of the expression takes the focus of the lh-side of the expression, that is, its focus is on div:
//div[#class='xyz']/concat(strong/text(), sub/text())
In addition (thanks to Rubens), you want the attribute class, not the child element class.
If your elements do not contain mixed content and you are interested on all text data inside it, you can simplify as follows:
//div[#class='xyz']/concat(strong, sub)
XPath 1.0
If you are stuck with XPath 1.0, you can do the following, but it is not (quite) the same:
concat(//div[#class='xyz']/strong, //div[#class='xyz']/sub)
If you want to apply this to all divs (as indicated by //) you should resort to XSLT instead.

XSLT: xpath to get parent element on matched child element name

I am new to xslt.
I want to get the parent node of matched child node(ends with)
Sample XML:
can have elements before and after parent tag
Here the result should be "parent"
Query: find first parent element name of the child element ends with smile ("endwithsmile")
I could able to get the element which contains ("endswithsmile"), but not ends with .
ends-with is not working.
<xsl:value-of select="(//*[contains(local-name(), 'Identifier')])"/>
I also tried using "../" to get parent but didnt work.
Sample xml for above question :::
<parent>
<childendwithsmile>
<test></test>
</childendwithsmile>
</parent>
<parent1>
<childendwithsmile>
<test></test>
</childendwithsmile>
</parent1>
Try this XPath:
//*[substring(name(), string-length(name()) - string-length('smile') + 1) = 'smile']/..
#Kiril's answer should work, but I prefer this approach that selects a parent based on its child, rather than selecting the child and going back up the tree:
//*[*[substring(name(), string-length(name()) - string-length('smile') + 1) = 'smile']]

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.

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

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

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