xsl copy-of with substring - xslt

i have this function in xsl i took from this post
to replace "cr" with "line-break"
this is how i call it:
<xsl:variable name="breakText">
<xsl:call-template name="insertBreaks">
<xsl:with-param name="subject" select="PublicProfile/AboutMe"/>
</xsl:call-template>
</xsl:variable>
im doing like an article link to "read more" on text click
the first div of the short start of the article (550 chars of the long article)
display the text like i didnt use the "insertBreaks" function
<xsl:copy-of select="substring($breakText,0,550)"/>
but this line of the long description is working fine:
<xsl:copy-of select="$breakText"/>
where i was wrong?

You want to use xsl:value-of not xsl:copy-of. xsl:copy-of returns the elements and all their values of the selected tag. xsl:value-of returns the text associated with the selected tag. Since you're trying to get a substring, you want the text.

The solution used referred to in the question happens to be mine.
It replaces a newline character not with "line-break" but with a <br /> element. An element is a node -- not a string.
Thus, the $breakText as defined in this question is a temporary tree (in XSLT 2.0) or an RTF (Result Tree Fragment) in XSLT 1.0 -- not a string.
The problem is that (regardless of an accompanying minor error of using zero as index):
substring($breakText,0,550)
is a string (calculated from the string value of $breakText) and as such cannot include any <br/> element or any node at all
Remember: The string value of an element or of a tree is the concatenation of the values of all of its text-node descendents.
On the other side:
<xsl:copy-of select="$breakText"/>
copies the temporary tree (in XSLT 2.0) or produces the serialization of the RTF (in XSLT 1.0).
To put it more simply, xsl:copy-of preserves structure, string() or any conversion to string produces just a string -- and a string doesn't have structure (all nodes are gone).

Related

How would I get the last character of a string in XSLT in order to add it to my HTML output?

I am very new to XSLT and need to grab only the last letter of an XML value and add that character to my HTML output.
Use the XPath expression
substring(., string-length(.), 1)
or, in an XSLT way
<xsl:value-of select="substring(., string-length(.), 1)" />
This works, because XPath starts counting at 1 and not 0.

How to use a variable value

The variable behaviour here does not work as expected.
I have a variable named fonttag with a value that is an HTML line with both start and end tags and a divider.
<xsl:variable name="fonttag">
<font face="ANGSANA NEW" size="12">|</font>
</xsl:variable>
When I try to use it, to get part of the string back, I get an empty string:
<xsl:value-of select="substring-before($fonttag ,'|')"/>
Where I expected the substring :
<font face="ANGSANA NEW" size="12">
Similarly the
<xsl:value-of select="$fonttag"/>
returns nothing, although
<xsl:copy-of select="$fonttag"/>
return the whole string. Is there another way to achieve the expected result ?
A derived-question: Is it possible to nest xsl select tags like this (cannot get it to work either)
<xsl:copy-of select="substring-before( <xsl:copy-of select="$fonttag"/>,'|')"/>
?
thanks
I am afraid you misunderstand how XSLT works. Your variable does not contain the string "<font face="ANGSANA NEW" size="12">|</font>". It contains the element font, with two attributes, and the string value of "|". The xsl:value-of instruction, as well as any string function such as substring(), only address the string value of the given expression.

Storing html tags within an xsl variable

Sorry if this is a dumb question, but it is possible to store, and retrieve, a HTML snippet within an xsl 1.0 variable? EG:
<xsl:variable name="something"><p>Hi there</p><p>How are you today?</p></xsl:variable>
<xsl:value-of disable-output-escaping="yes" select="$something"/>
It just when I try, it seems to strip the HTML tags out. Thanks.
You need to use <xsl:copy-of select="$something"/> instead of xsl:value-of.
I'll add some explanation of what's happening :)
The reason you're not getting the html tags is that the $something variable contains a dom fragment, not a string: the value-of element extracts the content of the node(s) the same way as the string() function does, so does not serialize the nodes.
This would provide, instead, a string representation of the html string you have and you can then print it out with value-of and disable-output-escaping:
<xsl:variable name="something"><![CDATA[<p>Hi there</p><p>How are you today?</p>]]></xsl:variable>
(see https://msdn.microsoft.com/en-us/library/ms256181(v=vs.110).aspx "The results are converted to a string, as by a call to the string() function")

XSLT, finding out if last child node is a specific element

Look at the following two examples:
<foo>some text <bar/> and maybe some more</foo>
and
<foo>some text <bar/> and a last <bar/></foo>
Mixed text nodes and bar elements within the foo element. Now I am in foo, and want to find out if the last child is a bar. The first example should prove false, as there are text after the bar, but the second example should be true.
How can I accomplish this with XSLT?
Just select the last node of the <foo> element and then use self axis to resolve the node type.
/foo/node()[position()=last()]/self::bar
This XPath expression returns an empty set (which equates to boolean false) if the last node is not an element. If you want to specifically get value true or false, wrap this expression in the XPath function boolean(). Use self::* instead of self::bar to match any element as the last node.
Input XML document:
<root>
<foo>some text <bar/> and maybe some more</foo>
<foo>some text <bar/> and a last <bar/></foo>
</root>
XSLT document example:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="foo">
<xsl:choose>
<xsl:when test="node()[position()=last()]/self::bar">
<xsl:text>bar element at the end
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>text at the end
</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output of the stylesheet:
text at the end
bar element at the end
Now I am in foo, and want to find
out if the last child is a bar
Use:
node()[last()][self::bar]
The boolean value of any non-empty node-set is true() and it is false() for otherwise. You can use the above expression directly (unmodified) as the value of the test attribute of any <xsl:if> or <xsl:when>.
Better, use:
foo/node()[last()][self::bar]
as the match attribute of an <xsl:template> -- thus you write in pure "push" style.
Update: This answer addresses the requirement stated in the original question title, "finding out if last child node is a text node." But the question body suggests a different requirement, and it seems that the latter requirement was the one intended by the OP.
The previous two answers explicitly test whether the last child is a bar element, rather than directly testing whether it is a text node. This is correct if foo contains only "mixed text nodes and bar elements" and never has zero children.
But you may want to test directly whether the last child is a text node:
For readability of stylesheet logic
In case the element contains other children besides elements and text: e.g. comments or processing instructions
In case the element has no children
Maybe you know the latter two will never occur in your case (but from your question I would guess that #3 could). Or maybe you think so but aren't sure, or maybe you hadn't thought about it. In either case, it's safer to test directly for what you actually want to know:
test="node()[last()]/self::text()"
Thus, building on #Dimitre's example code and input, the following XML input:
<root>
<foo>some text <bar/> and maybe some more</foo>
<foo>some text <bar/> and a pi: <?foopi param=yes?></foo>
<foo>some text <bar/> and a comment: <!-- baz --></foo>
<foo>some text and an element: <bar /></foo>
<foo noChildren="true" />
</root>
With this XSLT template:
<xsl:template match="foo">
<xsl:choose>
<xsl:when test="node()[last()]/self::text()">
<xsl:text>text at the end;
</xsl:text>
</xsl:when>
<xsl:when test="node()[last()]/self::*">
<xsl:text>element at the end;
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>neither text nor element child at the end;
</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
yields:
text at the end;
neither text nor element child at the end;
neither text nor element child at the end;
element at the end;
neither text nor element child at the end;

xsl strange behaviour using variables

I've stored a file's tree into $onto
<xsl:variable name="onto" select="document('file.xml')"/>
In some places I can use this variable as espected:
<xsl:copy-of select="$onto/rdf:RDF"/>
But I'm having trouble in other places, strange chars are written on output:
<xsl:element name="autor">
<xsl:attribute name="rdf:resource">
<xsl:text>#</xsl:text> <xsl:value-of select="$onto"/>
</xsl:attribute>
</xsl:element>
This is the beginig of the output I've got:
<autor rdf:resource="#
What I'm missing? What's wrong?
If that's to much for an attribute, what can I do?
Thank you
When <xsl:value-of> is applied to a tree fragment, it takes the text content of that tree. In your case, it looks like your XML file doesn't contain any text (other than whitespace) which isn't in an attribute value. I suspect that you mean to select the value of a particular attribute node within the document, e.g.:
<xsl:value-of select="$onto//foo/#bar"/>
(Without knowing the structure of your XML and what you're trying to select, I don't know what the real path would be.)