XSLT Get the first occurrence of a specific tag - xslt

Let's say i have a full html-document as XML-input.
How would the XSLT-file look if i only want to output the first (or any) image from the html?

One XPath expression that selects the first <img> element in a document is:
(//img)[1]
Do note that a frequent mistake -- as made by #Oded in his answer is to suggest the following XPath expression -- in general it may select more than one element:
//img[1] (: WRONG !!! :)
This selects all <img> elements in the document, each one of which is the first <img> child of its parent.
Here is the exact explanation of this frequent mistake -- in the W3C XPath 1.0 Recommendation:
NOTE: The location path //para[1] does not mean the same as the location path /descendant::para[1]. The latter selects the first descendant para element; the former selects all descendant para elements that are the first para children of their parents.
A further problem exists if the document has defined a default namespace, which must be the case with XHTML. XPath treats any unprefixed name as belonging to no namespace and the expression (//img)[1] selects no node, because there is no element in the document that belongs to no namespace and has name img.
In this case there are two ways to specify the wanted XPath expression:
(//x:img)[1] -- where the prefix x is associated (by the hosting language) with the specific default namespcae (in this case this is the XHTML namespace).
(//*[name()='img'])[1]

The XPath expression the will retrieve the first image from an HTML page: (//img)[1].
See the answer from #Dimitre Novatchev for more information on problems with it.

Related

Wildcard at the beginning of an XPath for XSLT (over multiple articles)

I have a working scenario but don't know why a particular XPath is working. I iterate over three articles from a database, they are TEI XML. For all of them, I need to put endnotes at the end of a particular article (third in this case).
<xsl:for-each select="//tei:text">
<xsl:apply-templates select="tei:body"/>
<xsl:apply-templates select="*//tei:note"/>
</xsl:for-each>
If I use just //tei:note, notes from the third article are present in all articles. If I use *//tei:note, it works as expected. Am I anchoring the notes to some context or so?
//tei:note returns all note elements within current XML document, ignoring the context element. Commonly, you put a dot (.) at the beginning to make it relative to the context element.
Regarding your working XPath, basically * gets direct child elements, of any name, from current context element. So yes, you can say that you're 'anchoring' subsequent XPath, //tei:note, to direct child of current contex element by saying *//tei:note.

What does `descendant` mean in XPATHNavigator's Compile function?

I've googled for few pages but failed to find the meaning of descendant word as typed out in the code below.
System::Xml::XPath::XPathExpression^ expr = nav->Compile("descendant::delivery[stockcode='ZMY201HR6US-A']");
Can someone tell me what descendant means?Thanks to anyone's helps.
descendant means:
Selects all descendants (children, grandchildren, etc.) of the current node
It comes from the XPath "axes" syntax.
http://www.w3schools.com/xpath/xpath_axes.asp
In your example, the qualifiers following it mean that the statement itself will select any delivery nodes with stockcode of ZMY201HR6US-A that are sub-nodes from the current node.

find xpath element using a variable

im looking to find an element in a schema based on the value of a variable (that changes each time i iterate). the catch is the element could be anywhere inside the schema.
for instance:
<...
<foo>
<bar>
<bar1>BB</bar1>
<bar2>CC</bar2>
</bar>
<rab>
<rab1>DD</rab1>
</rab>
</foo>
/...>
$attribute = bar1
(then the next iteration, $attribute may equal rab1)
how would i write an expression that could find me: .../foo/bar/$attribute
the closest thing i can find is ...//*[name()=$attribute] but it doesn't work. is there any other way?
Thanks for your help!
Although the question leaves out a lot of details that may be important, you could try changing name() to local-name():
...//*[local-name()='bar1']
and see if that fixes the problem. The return value of name() includes any prefix the element name has, which could cause it not to match the value of $attribute. (#Kirill was hinting at this.)
If that doesn't solve the problem, provide more context: What is the full XPath expression? How is it being used in XSLT? How do you know it "doesn't work"? (Give expected results and actual results.)

Difference between content node and document root

I have recently read about XSLT and Xpath. But i came across document root and content node, so many times. Are these are same? Or different?
Correct me if am wrong.
The root node is the node that is the ancestor of all other nodes in the XML document. It is not an element. It has no markup: you cannot see it in a serialized document. It is the parent of the outermost element (and of any siblings that element may have, such as comments or processing instructions). The root node is matched by the XPath expression /.
Confusingly, the outermost element is sometimes called the "root element," and is referred to that way in the XML specification; but in the XSLT specification it is only called the document element. This element is matched by the XPath expression /*.
The context node (if that's what you wanted -- I'm looking at your comment about a confusion) is whatever node the processor considers to be the current node to focus on, when evaluating a particular (piece of an) XPath expression or XSLT instruction, as it occurs in the context of an XSLT stylesheet or other execution environment. See current-node in the XSLT spec. The context node is the meaning of the XPath expression ..

How to check if an apply-template filled variable is a string or (possibly) empty node in XSLT?

I need a test for a variable which would evaluate to true in two cases:
There is a string inside which contains any non white space characters
There is any node (which can be possibly empty)
and the variable is filled with apply-template call result.
I tried
test="normalize-space($var)"
but this doesn't cover the empty tag possibility. I also tried simply this:
test="$var"
but this evaluate to true even for white space only strings.
By the way "$var/*" produces an error "Expression ...something I don't remember... node-set" which is I think because of apply-template variable instantiation.
Is there any (which means even multi level decision) solution for this?
EDIT: I forgot to say that it's for XSLT 1.0 and preferably without any exslt extensions or similar.
If you want to check whether a result tree fragment contains any child nodes then you need exsl:node-set e.g.
<xsl:if test="exsl:node-set($variable)/node()"
xmlns:exsl="http://exslt.org/common">
Without that extension function (or a similar one your particular XSLT 1.0 processor offers) you can't perform that check.
Hope this helps:
"string($variable)" will test true if the string has any characters in it, false if it equals ''.
http://www.dpawson.co.uk/xsl/sect2/N3328.html#d4474e64
Contains no child nodes: not(node())
Contains no text content: not(string(.))
Contains no text other than whitespace: not(normalize-space(.))
Contains nothing except comments: not(node()[not(self::comment())])