Removing white space inside quotes in XSLT - xslt

I'm trying to format a table from XML. Lets say I have this line in the XML
<country>Dominican Republic</country>
I would like to get my table to look like this
<td class="country DominicanRepublic">Dominican Republic</td>
I've tried this:
<td class="country {country}"><xsl:value-of select="country"/></td>
then this:
<xsl:element name="td">
<xsl:attribute name="class">
<xsl:text>country </xsl:text>
<xsl:value-of select="normalize-space(country)"/>
</xsl:attribute>
<xsl:value-of select="country"/>
</xsl:element>
The normalize-space() doesn't remove the space between the two parts of the name and I can't use <xsl:strip-space elements="country"/> because I need the space when I display the name inside the table cell.
How can I strip the space from the value inside the class, but not the text in the cell?

Use the translate() function to replace spaces ' ' with nothing '':
<xsl:element name="td">
<xsl:attribute name="class">
<xsl:text>country </xsl:text>
<xsl:value-of select="translate(country,' ','')"/>
</xsl:attribute>
<xsl:value-of select="country"/>
</xsl:element>
You can use normalize-space(), which will remove any leading and trailing white space and convert multiple spaces between characters into a single space. Then, send the results through translate() to replace any remaining spaces:
<xsl:element name="td">
<xsl:attribute name="class">
<xsl:text>country </xsl:text>
<xsl:value-of select="translate(normalize-space(country),' ','')"/>
</xsl:attribute>
<xsl:value-of select="normalize-space(country)"/>
</xsl:element>

You will need to split your string by whitespaces recursively, have a look at this topic:
Does XSLT have a Split() function?
Or you can try this replace function implementation: http://geekswithblogs.net/Erik/archive/2008/04/01/120915.aspx

Related

confusing error thown while trying to match templates

I've the below XML line of code.
<entry colname="col3" align="left" valign="top"><para>grandchild, cousin, <content-style font-style="italic">etc</content-style>., shall be described as “lawful” and “one of the next-of-kin” or “only next-of-kin”.</para></entry>
and below XSL
<xsl:template match="entry" name="entry">
<xsl:choose>
<xsl:when test="./#namest">
<xsl:variable name="namest" select="#namest" />
<xsl:variable name="nameend" select="#nameend" />
<xsl:variable name="namestPos" select="count(ancestor::tgroup/colspec[#colname=$namest]/preceding-sibling::colspec)" />
<xsl:variable name="nameendPos" select="count(ancestor::tgroup/colspec[#colname=$nameend]/preceding-sibling::colspec)" />
<td colspan="{$nameendPos - $namestPos + 1}" align="{#align}">
<xsl:apply-templates select="child::node()[not(self::page)]" />
</td>
</xsl:when>
<xsl:otherwise>
<td>
<xsl:if test="./#morerows">
<xsl:attribute name="rowspan">
<xsl:value-of select="number(./#morerows)+1" />
</xsl:attribute>
</xsl:if>
<xsl:if test="./#align">
<xsl:attribute name="align">
<xsl:value-of select="#align" />
</xsl:attribute>
</xsl:if>
<xsl:if test="./#valign">
<xsl:attribute name="valign">
<xsl:value-of select="#valign" />
</xsl:attribute>
</xsl:if>
<xsl:for-each select="para">
<div class="para">
<xsl:choose>
<xsl:when test="../#colname='col3' and contains(./text(),'.')">
<xsl:variable name="strl">
<xsl:value-of select="fn:string-length(fn:substring-before(.,'.'))" />
</xsl:variable>
<xsl:choose>
<xsl:when test="$strl < '6'">
<a href="{concat('er:#SCP_ORD_',//chapter/#num,'/','P',translate(./text(),'.','-'))}">
<xsl:value-of select="./text()" />
</a>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates />
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates />
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:for-each>
</td>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
and when i run this on my XML in the above mentioned line(the XML given in the sample), it is throwing me an error. and the error is as below.
Wrong occurrence to match required sequence type - Details: - XPTY0004: The supplied sequence ('2' item(s)) has the wrong occurrence to match the sequence type xs:string ('zero or one')
here what i actually was trying to achieve is, i have another table(which is basically a TOC), where in there is some linking needed, and below is such sample entries.
<entry colname="col3" align="right" valign="top"><para>A.1</para></entry>
<entry colname="col3" align="right" valign="top"><para>A.2</para></entry>
here i'm searching if the colname is col3 and if this has a . in it, and the above two cases mentioned are passing and are getting linked successfully, where in the case mentioned in the top is throwing the error, can anyone please suggest some better method to differentiate these two cases, and i use XSLT 2.0.
Thanks
The problem is
contains(./text(),'.')
./text() is not "the text of the current element" but rather the sequence of all text node children of the current element. In the case of
<para>grandchild, cousin, <content-style font-style="italic">etc</content-style>., shall be described as “lawful” and “one of the next-of-kin” or “only next-of-kin”.</para>
there are two such nodes, one containing everything between the <para> and <content-style> tags ("grandchild, cousin, " including the trailing space) and the other containing everything between the </content-style> and the </para>. But the contains function expects its first argument to be a single string, not a sequence of two nodes.
Instead of testing ./text() you probably just need to test .:
contains(., '.')
which is interpreted as the string value of the whole para element, i.e. a single string consisting of the concatenation of all the descendant text nodes (so the whole text "grandchild, cousin, etc., shall be described...").

substring XSLT over multiple value lines for an atttribute

I know the following xslt will work:
<xsl:attribute name="test">
<xsl:value-of select="substring(title, 1, 4000)"/>
</xsl:attribute>
But not sure what to do if there is something like the following and you want the substring over the whole attribute value not just the title or the substitle.
<xsl:attribute name="test">
<xsl:value-of select="title"/>
<xsl:if test="../../sub_title != ''">
<xsl:text> </xsl:text>
<xsl:value-of select="../sub_title"/>
</xsl:if>
</xsl:attribute>
Is it even possible to apply a substring function over multiple lines that define an attribute?
I think what you are saying is that you want to build up a long string, consisting of the values of a number of other elements, and then truncate the result.
What you could do, is use the concat function to build the attribute value, and then do a substring on that.
<xsl:attribute name="test">
<xsl:value-of select="substring(concat(title, ' ', ../sub_title), 1, 4000)" />
</xsl:attribute>
In this case, if sub_title was empty, you would end up with a space at the end of the test attribute, so you might want to add a normalize-space to this expression
<xsl:value-of select="normalize-space(substring(concat(title, ' ', ../sub_title), 1, 4000))" />
An alternate approach, if you did want to use a more complicated expression, is to do the string calculation in a variable first
<xsl:variable name="test">
<xsl:value-of select="title"/>
<xsl:if test="../../sub_title != ''">
<xsl:text> </xsl:text>
<xsl:value-of select="../sub_title"/>
</xsl:if>
</xsl:variable>
<xsl:attribute name="test">
<xsl:value-of select="substring($test, 1, 4000)" />
</xsl:attribute>
As an aside, you can simplify your code by using "Attribute Value Templates" here, instead of using the more verbose xsl:attribute command. Simply do this..
<myElement test="{substring($test, 1, 4000)}">
Here, the curly braces indicate an expression to be evaluated, rather than output literally.

Split and concatenate a string in XSLT

Hi I have the below line in by XML and also I need a hyperlink for the number. I want this output to be shown in HTML format.
<main>
<alph>a b 2,3</alph>
</main>
I want an XSLT that gives output as:
a b 2, a b 3
I have tried the below XSLT:
<xsl:template match="alph">
<xsl:variable name="link" select="normalize-space(translate(
normalize-space(current()),abcdefghijklmnopqrstuvwxyz,''))"/>
<xsl:value-of select="substring-before(normalize-space(.),$link)"/>
<xsl:variable name="tex">
<xsl:value-of select="text()"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($link,',')">
<xsl:variable name="new">
<xsl:value-of select="tokenize($link,',')"/>
</xsl:variable>
<xsl:value-of select="concat($new,$tex)"/>
</xsl:when>
<xsl:when test="contains($link,'-')">
<xsl:value-of select="tokenize($link,'-')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$link"/>
</xsl:otherwise>
</xsl:choose>
But it is giving me output as:
a b 2 3a b 2,3
Thanks
One problem you have is with the variable link
<xsl:variable name="link" select="normalize-space(translate(
normalize-space(current()),abcdefghijklmnopqrstuvwxyz,''))"/>
It looks like you are trying removing all alphabetic characters from the string, so that you are left with just 2,3. However, for this to work the abc...xyz needs to be enclosed in apostrophes, otherwise it will be looking for an element named abc...xyz. Having said that, you say you are using XSLT2.0, so you can make use of the replace function here, which takes a regular expression as a parameter
<xsl:variable name="link" select="normalize-space(replace(current(),'[a-z]',''))"/>
Next, you can get the text before this link, like so
<xsl:variable name="text" select="normalize-space(substring-before(current(), $link))"/>
This will give you your a b
Finally, you can use the tokenize function to split up the 2,3. In your XSLT you seem to be looking for hyphens too, but the tokenize function also uses regular expressions, so this is not a problem. What you can do is just tokenize the string, and re-join it using the text variable as a separator
<xsl:value-of select="concat($text, ' ')"/>
<xsl:value-of select="tokenize($link,',|-')" separator="{concat(', ', $text, ' ')}"/>
Here is the full XSLT
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="alph">
<xsl:variable name="link" select="normalize-space(replace(current(),'[a-z]',''))"/>
<xsl:variable name="text" select="normalize-space(substring-before(current(), $link))"/>
<xsl:value-of select="concat($text, ' ')"/>
<xsl:value-of select="tokenize($link,',|-')" separator="{concat(', ', $text, ' ')}"/>
</xsl:template>
</xsl:stylesheet>
When applied on your XML, the following is output
a b 2, a b 3

XSL for each loop selecting position of first occurrence

In the following code snippet, I'm trying to get position of EMP_ID field from the available fields. This works fine if there's just one occurrence of EMP_ID.
But if there are more than one occurences then variable 'empid_field' will have positions of all the occurrences appended one after the other. i.e if EMP_ID is at postions 1, 8, and 11, then 'empid_field' would be '1811'.
Is there any way I get position of first occurrence only? Or Can I get comma separated positions atleast? (Code sample would be highly appreciated as I'm new to XSL programming)
<xsl:variable name="empid_field">
<xsl:for-each select="$fields">
<xsl:if test="internalName='EMP_ID'">
<xsl:value-of select="position()"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
The easiest solution which is in my mind is to extend this. But I think there are also solutions which look more pretty.
<xsl:variable name="empid_field">
<xsl:for-each select="$fields">
<xsl:if test="internalName='EMP_ID'">
<xsl:value-of select="position()"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="first_empid_field">
<xsl:value-of select="$empid_field[1]"/>
</xsl:variable>
The variable $first_empid_field will only have the first position value.
Ok got something ...
Created a comma separated string and picked the part before the delimiter.
<xsl:variable name="empid_fields" >
<xsl:for-each select="$fields">
<xsl:if test="internalName='EMP_ID'">
<xsl:value-of select="position()" />
<xsl:text >, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="empid_field" >
<xsl:value-of select="substring-before($empid_fields, ', ')" />
</xsl:variable>

How to implement Carriage return in XSLT

I want to implement carriage return within xslt.
The problem is I have a varible:
Step 1 = Value 1 breaktag Step 2 = Value 2 as a string and would like to appear as
Step 1 = Value 1
Step 2 = Value 2
in the HTML form but I am getting the br tag on the page.Any good ways of implementing a line feed/carriage return in xsl would be appreciated
As an alternative to
<xsl:text>
</xsl:text>
you could use
<xsl:text>
</xsl:text> <!-- newline character -->
or
<xsl:text>
</xsl:text> <!-- carriage return character -->
in case you don't want to mess up your indentation
This works for me, as carriage-return + life feed.
<xsl:text>
</xsl:text>
The "
" string does not work.
The cleanest way I've found is to insert !ENTITY declarations at the top of the stylesheet for newline, tab, and other common text constructs. When having to insert a slew of formatting elements into your output this makes the transform sheet look much cleaner.
For example:
<?xml version="1.0"?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY nl "<xsl:text>
</xsl:text>">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="step">
&nl;&nl;
<xsl:apply-templates />
</xsl:template>
...
</xsl:stylesheet>
use a simple carriage return in a xsl:text element
<xsl:text>
</xsl:text>
Try this at the end of the line where you want the carriage return. It worked for me.
<xsl:text><![CDATA[<br />]]></xsl:text>
I was looking for a nice solution to this, as many would prefer, without embedding escape sequences directly in the expressions, or having weird line breaks inside of a variable. I found a hybrid of both this approaches actually works, by embedding a text node inside a variable like this:
<xsl:variable name="newline"><xsl:text>
</xsl:text></xsl:variable>
<xsl:value select="concat(some_element, $newline)" />
Another nice side-affect of this is that you can pass in whatever newline you want, be it just LF, CR, or both CRLF.
--Daniel
Here is an approach that uses a recursive template, which looks for
in the string from the database and then outputs the substring before.
If there is a substring after
remaining, then the template calls itself until there is nothing left.
In case
is not present then the text is simply output.
Here is the template call (just replace #ActivityExtDescription with your database field):
<xsl:call-template name="MultilineTextOutput">
<xsl:with-param name="text" select="#ActivityExtDescription" />
</xsl:call-template>
and here is the code for the template itself:
<xsl:template name="MultilineTextOutput">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '
')">
<xsl:variable name="text-before-first-break">
<xsl:value-of select="substring-before($text, '
')" />
</xsl:variable>
<xsl:variable name="text-after-first-break">
<xsl:value-of select="substring-after($text, '
')" />
</xsl:variable>
<xsl:if test="not($text-before-first-break = '')">
<xsl:value-of select="$text-before-first-break" /><br />
</xsl:if>
<xsl:if test="not($text-after-first-break = '')">
<xsl:call-template name="MultilineTextOutput">
<xsl:with-param name="text" select="$text-after-first-break" />
</xsl:call-template>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" /><br />
</xsl:otherwise>
</xsl:choose>
Works like a charm!!!
I believe that you can use the xsl:text tag for this, as in
<xsl:text>
</xsl:text>
Chances are that by putting the closing tag on a line of its own, the newline is part of the literal text and outputted as such.
I separated the values by Environment.NewLine and then used a pre tag in html to emulate the effect I was looking for
This is the only solution that worked for me. Except I was replacing
with \r\n