Suppose I have this XML:
<div>
<div>
<div>
<div>
"Hello2"
</div>
</div>
</div>
</div>
But my templates might change, and I want to be flexible in the depth in which the div element is located. Example:
<div>
<div>
<div>
"Hello3"
</div>
</div>
</div>
So how I can get the innertext from the last element in the XML from this nested XML structure?
So how I can get the innertext from the last element in the XML from this nested XML structure?
It seems from the posted XML document that what is actually asked is:
How can I get the string value of the innermost element in the XML document
I. XPath 1.0 / XSLT 1.0 solution:
This XPath expression, when evaluated:
//*[not(*)]
selects all elements in the document that don't have another element as a child.
(//*[not(*)])[last()]
selects the last such inner-most node.
It is not possible to find the "deepest element" with a single XPath 1.0 expression -- one could do this with a simple XSLT 1.0 transformation. The XSLT 1.0 transformation below copies to the output the last inner-most element of the XML document:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kElemByDepth" match="*" use="count(ancestor::*)"/>
<xsl:template match="/">
<xsl:variable name="vMaxDepth">
<xsl:apply-templates select="//*[not(*)]" mode="getMax">
<xsl:sort select="count(ancestor::*)" data-type="number" order="descending"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:copy-of select="key('kElemByDepth', $vMaxDepth)[last()]"/>
</xsl:template>
<xsl:template match="*" mode="getMax">
<xsl:if test="position() = 1">
<xsl:value-of select="count(ancestor::*)"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<div>
<div>
<div>
<div>
"Hello2"
</div>
<div>
"Hello3"
</div>
</div>
</div>
<div>
"Hello1"
</div>
</div>
the wanted, correct result is produced:
<div>
"Hello3"
</div>
If you want just the string value of this element, simply replace:
<xsl:copy-of select="key('kElemByDepth', $vMaxDepth)[last()]"/>
with
<xsl:copy-of select="normalize-space(key('kElemByDepth', $vMaxDepth)[last()])"/>
II. Pure XPath 2.0 solution
Use this XPath 2.0 expression:
normalize-space(//*[not(*)]
[not(count(ancestor::*) < //*[not(*)]/count(ancestor::*))][last()])
XSLT 2.0 - based verification:
This transformation evaluates the above XPath 2.0 expression and copies to the output the result of this evaluation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:sequence select=
"normalize-space(//*[not(*)]
[not(count(ancestor::*) < //*[not(*)]/count(ancestor::*))][last()])"/>
</xsl:template>
</xsl:stylesheet>
when applied to the same XML document (above), again the same correct, wanted result is produced:
"Hello3"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With