how to set newline and tab in paragraph using xslt - xslt

I want to set newline and tab in paragraph using XSLT values from xml retrieve using XML Parsing.Following XSLT code separates each word in paragraph.But,I want to separate newline in paragraph wherever necessary and also I want to set tab before starting a paragraph...
sample.xml
<item>
<id>0</id>
<desc>Review your resume, and make sure that you can explain everything on it. Arrive at the interview ten minutes early to give yourself an opportunity to collect your thoughts and relax. Be aware that many employers will have their receptionist’s record the time you came in. If you rush in at the last minute, an employer may have serious concerns about your ability to arrive on time for a normal day at work.</desc>
</item>
xslt:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="t">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="text()" name="insertBreaks">
<xsl:param name="pText" select="."/>
<xsl:choose>
<xsl:when test="not(contains($pText, '
'))">
<xsl:copy-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($pText, '
')"/>
<br />
<xsl:call-template name="insertBreaks">
<xsl:with-param name="pText" select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

To set a newline you can use:
<xsl:text>
</xsl:text>

Related

Unable to pass variable value in XSLT within a template

The below is my sgml file structure:
<em>
<pgblk revdate="20130901">
<task revdate='20140901'>
<p>random texts and few more inner tags</p>
</task>
<task revdate='20150901'>
<p>random texts and few more inner tags</p>
</task>
<task revdate='20160901'>
<p>random texts and few more inner tags</p>
</task>
</pgblk>
I have many tags with same name (task tag) and am trying to get the greatest revdate here. My XSLT is as follows:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:template match="/pgblk">
<xsl:variable name="updatedrevdate">
<xsl:for-each select="task">
<xsl:sort select="#revdate" data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="#revdate" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
</xsl:template>
</xsl:stylesheet>
Now i am getting the greatest task tag value..but when i use the condition inside the same template to compare the greatest tag and the pageblock value
"Operations to PRINT THE GREATEST REVDATE"
The comparison is always getting the first revdate from task and comparing with pageblock revdate. It is not getting the greatest from the task.
Any help is much appreciated. Thanks.
Please check if the following XSLT code helps you make changes to your code. Need to change the template matching to
<xsl:template match="pgblk">
The solution does the comparison and outputs the dates.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:strip-space elements="*"/>
<xsl:template match="pgblk">
<xsl:variable name="updatedrevdate">
<xsl:for-each select="task">
<xsl:sort select="#revdate" data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="#revdate" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- output the dates -->
<xsl:value-of select="concat('Page Block Date: ', #revdate)" />
<xsl:text>
</xsl:text>
<xsl:value-of select="concat('Max Task Date: ', $updatedrevdate)" />
<xsl:text>
</xsl:text>
<xsl:choose>
<xsl:when test="#revdate > $updatedrevdate">
<xsl:value-of select="concat('Max date after comparison: ', #revdate)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('Max date after comparison: ', $updatedrevdate)" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output
Page Block Date: 20130901
Max Task Date: 20160901
Max date after comparison: 20160901

replacing copyright symbol and other symbols with elements in xslt

Input:
<text>
Please see the registered mark® .
Please see the copy right ©.
Please see the Trade mark™.
</text>
Output:
<text>
Please see the registered mark<registeredTrademark></registeredTrademark>.
Please see the copy right <copyright></copyright>.
Please see the Trade mark <trademark></trademark>.
</text>
I need to replace all special symbols with the elements as shown above
Can any one help.
Thanks
As this is XSLT 1.0, you are going to have to use a recursive named template to check each character in turn.
Firstly, it may be more flexible to create a sort of 'look-up' in your XSLT where you can specify a list of symbols and the required element name to replace them with
<lookup:codes>
<code symbol="®">registeredTrademark</code>
<code symbol="©">copyright</code>
<code symbol="™">trademark</code>
</lookup:codes>
(The 'lookup' namespace could actually be named anything, just as long as it is declard in the XSLT).
Then, to access this, you could define a variable to access this look-up
<xsl:variable name="lookup" select="document('')/*/lookup:codes"/>
And, to look-up an actually code based on a symbol would do something like this (where $text) is a variable that contains the text you are checking.
<xsl:variable name="char" select="substring($text, 1, 1)"/>
<xsl:variable name="code" select="$lookup/code[#symbol = $char]"/>
All the named template would do, is check the first character of the text, replacing it with an element if it exists in the lookup, and then recursively call the template with the remaining part of the text.
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:lookup="lookup" exclude-result-prefixes="lookup">
<xsl:output method="xml" indent="no"/>
<lookup:codes>
<code symbol="®">registeredTrademark</code>
<code symbol="©">copyright</code>
<code symbol="™">trademark</code>
</lookup:codes>
<xsl:variable name="lookup" select="document('')/*/lookup:codes"/>
<xsl:template match="text[text()]">
<text>
<xsl:call-template name="text"/>
</text>
</xsl:template>
<xsl:template name="text">
<xsl:param name="text" select="text()"/>
<xsl:variable name="char" select="substring($text, 1, 1)"/>
<xsl:variable name="code" select="$lookup/code[#symbol = $char]"/>
<xsl:choose>
<xsl:when test="$code"><xsl:element name="{$code}" /></xsl:when>
<xsl:otherwise>
<xsl:value-of select="$char"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="string-length($text) > 1">
<xsl:call-template name="text">
<xsl:with-param name="text" select="substring($text, 2, string-length($text) - 1)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<text>
Please see the registered mark<registeredTrademark /> .
Please see the copy right <copyright />.
Please see the Trade mark<trademark />.
</text>
This transformation is more efficient by avoiding char-by-char recursion and using "biggest-possible-step" recursion:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<my:reps>
<r char="®">registeredTrademark</r>
<r char="©">copyright</r>
<r char="™">trademark</r>
</my:reps>
<xsl:variable name="vReps" select="document('')/*/my:reps/*"/>
<xsl:template match="text()" name="multReplace">
<xsl:param name="pText" select="."/>
<xsl:param name="pReps" select="$vReps"/>
<xsl:if test="$pText">
<xsl:variable name="vTarget" select="$pReps[1]/#char"/>
<xsl:choose>
<xsl:when test="not($vTarget)">
<xsl:value-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vReplacement" select="$pReps[1]"/>
<xsl:call-template name="multReplace">
<xsl:with-param name="pText" select=
"substring-before(concat($pText, $vTarget), $vTarget)"/>
<xsl:with-param name="pReps" select="$pReps[position() >1]"/>
</xsl:call-template>
<xsl:if test="contains($pText, $vTarget)">
<xsl:element name="{$vReplacement}"/>
<xsl:call-template name="multReplace">
<xsl:with-param name="pText" select="substring-after($pText, $vTarget)"/>
<xsl:with-param name="pReps" select="$pReps"/>
</xsl:call-template>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When applied to the provided XML document:
<text>
Please see the registered mark® .
Please see the copy right ©.
Please see the Trade mark™.
</text>
the correctly-replaced text is produced:
Please see the registered mark<registeredTrademark/> .
Please see the copy right <copyright/>.
Please see the Trade mark<trademark/>.

How to find word with hyphen?

Is it possible to find words separated by a hyphen and surround them with some tag?
input
<root>
text text text-with-hyphen text text
</root>
required output
<outroot>
text text <sometag>text-with-hyphen</sometag> text text
</outroot>
This XSLT 2.0 transformation:
<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="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/text()">
<xsl:analyze-string select="." regex="([^ ]*\-[^ ]*)+">
<xsl:matching-substring>
<sometag><xsl:value-of select="."/></sometag>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<root>
text text text-with-hyphen text text
</root>
produces the wanted, correct result:
<root>
text text <sometag>text-with-hyphen</sometag> text text
</root>
Explanation:
Proper use of the XSLT 2.0 <xsl:analyze-string> instruction and its allowed children-instructions.
Just checked, it works. So idea behind is create recursive iteration over entire text. And inside recursion step using XPath function contains detect if word (see usage of $word) contains hyphen:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/root">
<outroot>
<xsl:call-template name="split-by-space">
<xsl:with-param name="str" select="text()"/>
</xsl:call-template>
</outroot>
</xsl:template>
<xsl:template name="split-by-space"> <!-- mode allows distinguish another tag 'step'-->
<xsl:param name="str"/>
<xsl:if test="string-length($str)"><!-- declare condition of recursion exit-->
<xsl:variable name="word"> <!-- select next word -->
<xsl:value-of select="substring-before($str, ' ')"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($word, '-')"> <!-- when word contains hyphen -->
<sometag>
<xsl:value-of select='concat(" ", $word)'/><!-- need add space-->
</sometag>
</xsl:when>
<xsl:otherwise>
<!-- produce normal output -->
<xsl:value-of select='concat(" ", $word)'/><!-- need add space-->
</xsl:otherwise>
</xsl:choose>
<!-- enter to recursion to proceed rest of str-->
<xsl:call-template name="split-by-space">
<xsl:with-param name="str"><xsl:value-of select="substring-after($str, ' ')"/></xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

How to import an image based on a variable value or value-of result

i'm new to xsl-fo.
In my problem there are 3 sibling tags, one of them has an attribute. I have to print the one with the attribute first and then the other two.
My problem is that my results aren't showing and i've tried when and if.
Heres my code:
<fo:block>
<xsl:for-each select="platforms/platform">
<xsl:choose>
<xsl:when test="#highestRated">
<xsl:value-of select="platform"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
! Also available on
<xsl:for-each select="platforms/platform">
<xsl:choose>
<xsl:when test="not(#*)">
<xsl:value-of select="platform"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</fo:block>
and heres an example of the siblings:
<platforms>
<platform>PC</platform>
<platform highestRated="true">PS3</platform>
<platform>X360</platform>
</platforms>
i can't just use them in the order they appear here because each set of siblings are in a different order.
I also get no errors and the rest of the document displays perfectly, they just won't show the results.
Thank you
As others have said, your main problem is that you have select="platform" which is looking for an element /platforms/platform/platform which doesn't exist. Also an xsl:choose with only one xsl:when is the same as an xsl:if. (It is useful to use xsl:when if you want a else condition, which xsl:if doesn't provide. Use xsl:choose / xsl:when / xsl:otherwise instead.) But in this case you may as well select only the elements you want using a predicate; then there is no need for a conditional.
Here is some code that does what you need.
<?xml version="1.0" encoding="UTF-8" ?>
<!-- New document created with EditiX at Wed Mar 21 21:51:52 GMT 2012 -->
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:variable name="nl">
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<fo:block>
<xsl:value-of select="$nl"/>
<xsl:for-each select="platforms/platform[#highestRated]">
<xsl:value-of select="."/>
<xsl:value-of select="$nl"/>
</xsl:for-each>
<xsl:text>! Also available on</xsl:text>
<xsl:value-of select="$nl"/>
<xsl:for-each select="platforms/platform[not(#highestRated)]">
<xsl:value-of select="."/>
<xsl:value-of select="$nl"/>
</xsl:for-each>
</fo:block>
</xsl:template>
</xsl:stylesheet>
output
<?xml version="1.0" encoding="utf-8"?>
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format">
PS3
! Also available on
PC
X360
</fo:block>
Try changing
<xsl:value-of select="platform"/>
to
<xsl:value-of select="."/>
Hope that helps

Counting distinct items and parsing comma-delimited values using XSLT

Suppose I have XML like this:
<child_metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem3]"/>
</attributes>
</metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem1]"/>
</attributes>
</metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem1, SampleItem2]"/>
</attributes>
</metadata>
</child_metadata>
What I want to do is count the number of distinct values that are in the metadata_valuelists. There are the following distinct values: SampleItem1, SampleItem2, and SampleItem3. So, I want to get a value of 3. (Although SampleItem1 occurs twice, I only count it once.)
How can I do this in XSLT?
I realize there are two problems here: First, separating the comma-delimited values in the lists, and, second, counting the number of unique values. However, I'm not certain that I could combine solutions to the two problems, which is why I'm asking it as one question.
Another way without extension:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="all-value" select="/*/*/*/*/#value"/>
<xsl:template match="/">
<xsl:variable name="count">
<xsl:apply-templates select="$all-value"/>
</xsl:variable>
<xsl:value-of select="string-length($count)"/>
</xsl:template>
<xsl:template match="#value" name="value">
<xsl:param name="meta" select="translate(.,'[] ','')"/>
<xsl:choose>
<xsl:when test="contains($meta,',')">
<xsl:call-template name="value">
<xsl:with-param name="meta" select="substring-before($meta,',')"/>
</xsl:call-template>
<xsl:call-template name="value">
<xsl:with-param name="meta" select="substring-after($meta,',')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:if test="count(.|$all-value[contains(translate(.,'[] ','
'),
concat('
',$meta,'
'))][1])=1">
<xsl:value-of select="1"/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Note: maybe can be optimize with xsl:key instead of xsl:variable
Edit: Match tricky metadata.
This (note: just a single) transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
>
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kValue" match="value" use="."/>
<xsl:template match="/">
<xsl:variable name="vRTFPass1">
<values>
<xsl:apply-templates/>
</values>
</xsl:variable>
<xsl:variable name="vPass1"
select="msxsl:node-set($vRTFPass1)"/>
<xsl:for-each select="$vPass1">
<xsl:value-of select=
"count(*/value[generate-id()
=
generate-id(key('kValue', .)[1])
]
)
"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="metadata_valuelist">
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select="translate(#value, '[],', '')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="pText" />
<xsl:choose>
<xsl:when test="not(contains($pText, ' '))">
<value><xsl:value-of select="$pText"/></value>
</xsl:when>
<xsl:otherwise>
<value>
<xsl:value-of select="substring-before($pText, ' ')"/>
</value>
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select=
"substring-after($pText, ' ')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<child_metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem3]"/>
</attributes>
</metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem1]"/>
</attributes>
</metadata>
<metadata>
<attributes>
<metadata_valuelist value="[SampleItem1, SampleItem2]"/>
</attributes>
</metadata>
</child_metadata>
produces the wanted, correct result:
3
Do note: Because this is an XSLT 1.0 solution, it is necessary to convert the results of the first pass from the infamous RTF type to a regular tree. This is done using your XSLT 1.0 processor's xxx:node-set() function -- in my case I used msxsl:node-set().
You probably want to think about doing this in two stages; first, do a transform that breaks down these value attributes, then it's fairly trivial to count them.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#value">
<xsl:call-template name="breakdown">
<xsl:with-param name="itemlist" select="substring-before(substring-after(.,'['),']')" />
</xsl:call-template>
</xsl:template>
<xsl:template name="breakdown">
<xsl:param name="itemlist" />
<xsl:choose>
<xsl:when test="contains($itemlist,',')">
<xsl:element name="value">
<xsl:value-of select="normalize-space(substring-before($itemlist,','))" />
</xsl:element>
<xsl:call-template name="breakdown">
<xsl:with-param name="itemlist" select="substring-after($itemlist,',')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="value">
<xsl:value-of select="normalize-space($itemlist)" />
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Aside from the 'catch all' template at the bottom, this picks up any value attributes in the format you gave, and breaks them down into separate elements (as sub-elements of the 'metadata_valuelist' element) like this:
...
<metadata_valuelist>
<value>SampleItem1</value>
<value>SampleItem2</value>
</metadata_valuelist>
...
The 'substring-before/substring-after select you see near the top strips off the '[' and ']' before passing it to the 'breakdown' template. This template will check if there's a comma in it's 'itemlist' parameter, and if there is it spits out the text before it as the content of a 'value' element, before recursively calling itself with the rest of the list. If there was no comma in the parameter, it just outputs the entire content of the parameter as a 'value' element.
Then just run this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:key name="itemvalue" match="value" use="text()" />
<xsl:template match="/">
<xsl:value-of select="count(//value[generate-id(.) = generate-id(key('itemvalue',.)[1])])" />
</xsl:template>
</xsl:stylesheet>
on the XML you get from the first transform, and it'll just spit out a single value as text output that tells you how many distinct values you have.
EDIT: I should probably point out, this solution makes a few assumptions about your input:
There are no attributes named 'value' anywhere else in the document; if there are, you can modify the #value match to pick out these ones specifically.
There are no elements named 'value' anywhere else in the document; as the first transform creates them, the second will not be able to distinguish between the two. If there are, you can replace the two <xsl:element name="value"> lines with an element name that's not already used.
The content of the #value attribute always begins with '[' and ends with ']', and there are no ']' characters within the list; if there are, the 'substring-before' function will drop everything after the first ']', rather than just the ']' at the end.
There are no commas in the names of the items you want to count, e.g. [SampleItem1, "Sample2,3"]. If there are, '"Sample2' and '3"' would be treated as separate items.