I want to usw the XSLT replace function to replace words in a text with
<strong>word</strong>.
I wrote the following template:
<xsl:template name="make-bold">
<xsl:param name="text"/>
<xsl:param name="word"/>
<xsl:variable name="replacement">
<strong><xsl:value-of select="$word"/></strong>
</xsl:variable>
<xsl:value-of select="replace($text, $word, $replacement )" />
</xsl:template>
Unfortunately, and are not rendered, althoug the rest works.
Could anyone help me?
Best, Suidu
Well the replace function http://www.w3.org/TR/xpath-functions/#func-replace takes a string and returns a string. You seem to want to create an element node, not a simple string. In that case using analyze-string http://www.w3.org/TR/xslt20/#analyze-string instead of replace could help.
Here is a sample XSLT 2.0 stylesheet:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="html" indent="no"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="text()" mode="wrap">
<xsl:with-param name="words" as="xs:string+" select="('foo', 'bar')"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="text()" mode="wrap">
<xsl:param name="words" as="xs:string+"/>
<xsl:param name="wrapper-name" as="xs:string" select="'strong'"/>
<xsl:analyze-string select="." regex="{string-join($words, '|')}">
<xsl:matching-substring>
<xsl:element name="{$wrapper-name}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
When you run that with an XSLT 2.0 processor like Saxon 9 against the following input sample
<html>
<body>
<p>This is an example with foo and bar words.</p>
</body>
</html>
the output is as follows:
<html>
<body>
<p>This is an example with <strong>foo</strong> and <strong>bar</strong> words.</p>
</body>
</html>
hmm is is because here it its the string value that is replaced, you might try to use the node set?
i cannot test as i dont use xslt 2.0 but you might try a recursive template ie
<xsl:template match="yourtextelement">
<xsl:call-template name="MaketextStrong">
</xsl:template>
<xsl:template name="MaketextStrong">
<xsl:param name="text" select="."/>
<xsl:choose>
<xsl:when test="contains($text, 'texttomakestrong')">
<xsl:value-of select="substring-before($text, 'texttomakestrong')"/>
<strong>texttomakestrong</strong>
<xsl:call-template name="break">
<xsl:with-param name="text" select="substring-after($text,
'texttomakestrong')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Related
I'm working on a xsl template and one specific string is giving me a hard time.
<prixmenu>29.00-90.00</prixmenu>
Desired output :
<prixmenu>29€-90€</prixmenu>
The whole code I have is :
XML (input) :
<?xml version="1.0" encoding="UTF-8"?>
<data>
<etablissement not="15.0" etoile="2" >
<nom>L'Auberge Asterix</nom>
<index>AUBERGE ASTERIX (L')</index>
<id>0123456789</id>
<regionindex>Paris</regionindex>
<equipe>
<chef>Ratatouille</chef>
</equipe>
<pictoPratique cave_remarquable="0" coeur="0" ></pictoPratique>
<coordonnees>
<adresse>Random adress</adresse>
<tel>01 23 45 67 89/tel>
<fermetures>mam,maa,</fermetures>
<pratique terrasse="0"
voiturier="0"
parcPrive="1"
handicap="0"
airConditione="0"
piscine="0"
tennis="0"
chien="1"
relaischat="0"
delivery="0"
clickAndCollect="0"
itineraireGourmand="0"
menu_kids="1"
hebergement="0"
></pratique>
</coordonnees>
<texte>Lorem ipsum</texte>
<prixrestau>
<prixcarte>0</prixcarte>
<prixmenu>29.00-90.00</prixmenu>
</prixrestau>
</etablissement>
</data>
XSL stylesheet :
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="data | node()[not(self::coordonnees)]">
<xsl:copy>
<xsl:apply-templates select="nom" />
<xsl:choose>
<xsl:when test="#etoile = 1">
<etoile>1 étoile</etoile>
</xsl:when>
<xsl:when test="#etoile > 1">
<etoile><xsl:value-of select="#etoile" /> étoiles</etoile>
</xsl:when>
</xsl:choose>
<xsl:apply-templates select="index" />
<xsl:apply-templates select="id" />
<xsl:apply-templates
select="#*[not(.='0')][name( ) != 'etoile'] | node()[not(self::index)][not(self::id)][not(self::prixrestau)][not(self::nom)][not(self::etoile)]" />
</xsl:copy>
</xsl:template>
<xsl:template match="#*[name( ) != 'etoile']">
<xsl:element name="{name()}">
<xsl:value-of select="number(.)" />
</xsl:element>
</xsl:template>
<xsl:template match="prixrestau/prixmenu" name="split">
<xsl:param name="pText" select="."/>
<xsl:if test="$pText">
<xsl:value-of select="number(substring-before(concat($pText, '-'), '-'))"/>
<xsl:call-template name="split">
<xsl:with-param name="pText" select="substring-after($pText, '-')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="coordonnees">
<xsl:copy>
<xsl:apply-templates select="adresse" />
<xsl:apply-templates select="tel" />
<xsl:element name="prixrestau">
<xsl:value-of select="translate(../prixrestau/prixmenu,'.00.','€')"/>
<xsl:apply-templates select="../prixrestau/prixcarte[not(.='0')]" />
<xsl:element name="prixmenu">
<xsl:apply-templates select="../prixrestau/prixmenu" />
</xsl:element>
</xsl:element>
<xsl:apply-templates select="fermetures" />
<xsl:apply-templates
select="#*[not(.='0')] | node()[not(self::adresse)][not(self::tel)][not(self::fermetures)]" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Desired output :
<?xml version="1.0"?>
<data>
<etablissement>
<nom>L'Auberge Asterix</nom>
<etoile>2 étoiles</etoile>
<index>AUBERGE ASTERIX (L')</index>
<id>0123456789</id>
<not>13</not>
<regionindex>Paris</regionindex>
<equipe>
<chef>Ratatouille</chef>
</equipe>
<pictoPratique/>
<coordonnees>
<adresse>Random adress</adresse>
<tel>01 23 45 67 89</tel>
<prixrestau><prixmenu>29€-90€</prixmenu></prixrestau>
<fermetures>mam,maa,</fermetures>
<pratique>
<parcPrive>1</parcPrive>
<chien>1</chien>
<menu_kids>1</menu_kids>
</pratique>
</coordonnees>
<texte>Lorem ipsum</texte>
</etablissement>
</data>
Any suggestions on how to proceed ? Should I import FXSL or switch to XSLT 2.0 as it probably be less of a pain to get to the desired result ?
I've tried splitting it with the following code (I belive it's called a 'recursive template" :
<xsl:template match="prixrestau/prixmenu" name="split">
<xsl:param name="pText" select="."/>
<xsl:if test="$pText">
<xsl:value-of select="number(substring-before(concat($pText, '-'), '-'))"/>
<xsl:call-template name="split">
<xsl:with-param name="pText" select="substring-after($pText, '-')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
This outputs
<prixmenu>2990</prixmenu>
And i've also tried
<xsl:value-of select="translate(../prixrestau/prixmenu,'.00.','€')"/>
and the output is :
29€-9€
Because '.00.' and '€'don't have the same length (MDN reference)...
How about:
<xsl:template match="prixmenu">
<xsl:copy>
<xsl:value-of select="format-number(substring-before(., '-'), '0€')"/>
<xsl:text>-</xsl:text>
<xsl:value-of select="format-number(substring-after(., '-'), '0€')"/>
</xsl:copy>
</xsl:template>
If you like, you could change the first xsl:value-of instruction to:
<xsl:value-of select="format-number(substring-before(., '-'), '0€-')"/>
and get rid of the xsl:text part - but I think it's better to keep it to make the code more readable.
I have to remove leading and trailing spaces using XSL 1.0
cannot use normalize-space for this .
and tried the below code
<xsl:template match="text()">
<xsl:value-of select="replace(.,'^\s+|\s+$','')"/>
</xsl:template>
before commands to start the actual mapping
but does not help
how to achieve this ?
Well, in XSLT 1.0, you can use recursive template (NOT preferable) method to remove leading and trailing spaces (In case you don't want to use normalize-space())
Let's assume your input as following:
<?xml version="1.0" encoding="UTF-8"?>
<body>
<h1> A text having so many leading and trailing spaces! </h1>
</body>
Note: The below solution uses xmlns:str="xalan://org.apache.commons.lang.StringUtils" to use it's two functions ends-with and substringBeforeLast
An XSLT 1.0 solution to achieve the task:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="xalan://org.apache.commons.lang.StringUtils"
exclude-result-prefixes="str">
<xsl:output method="xml" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="h1">
<h1>
<xsl:variable name="leadingSpaceRemoved">
<xsl:call-template name="removeLeadingSpace">
<xsl:with-param name="text" select="." />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="trailingSpaceRemoved">
<xsl:call-template name="removeTrailingSpace">
<xsl:with-param name="text" select="$leadingSpaceRemoved" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$trailingSpaceRemoved" />
</h1>
</xsl:template>
<xsl:template name="removeLeadingSpace">
<xsl:param name="text" />
<xsl:variable name="h1" select="$text" />
<xsl:choose>
<xsl:when test="starts-with($h1,' ')">
<xsl:call-template name="removeLeadingSpace">
<xsl:with-param name="text" select="substring-after($h1,' ')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$h1" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="removeTrailingSpace">
<xsl:param name="text" />
<xsl:variable name="h1" select="$text" />
<xsl:choose>
<xsl:when test="str:ends-with($h1,' ')">
<xsl:call-template name="removeTrailingSpace">
<xsl:with-param name="text" select="str:substringBeforeLast($h1,' ')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$h1" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Also, if you can call a java library/class in your environment, it will be more easier to achieve the same as below:
Your XSLT would be:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:stringparser="xalan://com.example.commons.xsl.StringUtil"
exclude-result-prefixes="stringparser">
<xsl:output method="xml" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="h1">
<h1>
<xsl:value-of select="stringparser:trim(.)" />
</h1>
</xsl:template>
</xsl:stylesheet>
And the StringUtil.java would be:
import org.apache.commons.lang.StringUtils;
public class StringUtil {
/**
*
* #param input
* #return remove leading and trailing spaces.
*/
public static String trim(final String input) {
if (StringUtils.isNotBlank(input)) {
return input.trim();
}
return input;
}
}
One of possible solutions is to use normalize-space()
function (works even in XSLT 1.0).
It does even more, i.e. it replaces multiple white chars inside
with a single space.
To apply it to all text nodes, add such a template:
<xsl:template match="text()">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
But if you have also the identity template, the above template
must be in your script after the identity template.
I would like to print path of element and attributes if any along with values using XSLT. e.g
XML :
<root>
<node attr='abc' module='try'>
<subnode>Roshan</subnode>
<subnode>Chetan</subnode>
<subnode>Jennifer</subnode>
</node>
</root>
Output :
/root##
/root/node##
/root/node/#attr##abc
/root/node/#module##try
/root/node/subnode[1]##Roshan
/root/node/subnode[2]##Chetan
/root/node/subnode[3]##Jennifer
I am trying with below snippet, but could only print path of element and it's value
<xsl:template match="*">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="concat('/',local-name())" />
<xsl:if
test="(preceding-sibling::*|following-sibling::*)[local-name()=local-name(current())]">
<xsl:value-of
select="concat('[',count(preceding-sibling::*[local-name()=local-name(current())])+1,']')" />
</xsl:if>
<!-- <xsl:call-template name="attrData"></xsl:call-template> -->
</xsl:for-each>
<xsl:text>##</xsl:text>
<xsl:apply-templates select="node()" />
</xsl:template>
I am new to XSLT. Please help!!!!
I made the following XSLT and added also the [position] to the output. You can remove that if you need.
This gives this output:
/root[1]
/root[1]/node[1]
/root[1]/node[1]/#attr[1]##abc
/root[1]/node[1]/#module[1]##try
/root[1]/node[1]/subnode[1]##Roshan
/root[1]/node[1]/subnode[2]##Chetan
/root[1]/node[1]/subnode[3]##Jennifer
With this XSLT. With the two output template you can choose how to print the Xpath.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="" version="2.0">
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="/">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="*">
<xsl:call-template name="generateXPath">
<xsl:with-param name="previous" select="''"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="generateXPath">
<xsl:param name="previous" as="xs:string"/>
<xsl:variable name="this" select="." as="node()"/>
<xsl:if test="not(empty(.))">
<xsl:variable name="thisXPath" select="concat($previous, '/', name(.),'[', count(preceding-sibling::*[name() = name($this)])+1,']')"></xsl:variable>
<xsl:apply-templates select="." mode="output">
<xsl:with-param name="previous" select="$previous"/>
</xsl:apply-templates>
<xsl:text>
</xsl:text>
<xsl:for-each select="*|#*">
<xsl:call-template name="generateXPath">
<xsl:with-param name="previous" select="$thisXPath"/>
</xsl:call-template>
</xsl:for-each>
</xsl:if>
</xsl:template>
<xsl:template match="*" mode="output">
<xsl:param name="previous" as="xs:string"/>
<xsl:variable name="this" select="." as="node()"/>
<xsl:variable name="thisXPath">
<xsl:value-of select="concat($previous, '/', name(.),'[', count(preceding-sibling::*[name() = name($this)])+1,']')"></xsl:value-of>
<xsl:if test="not(*)">
<xsl:value-of select="concat('##',text())"></xsl:value-of>
</xsl:if>
</xsl:variable>
<xsl:value-of select="$thisXPath" />
</xsl:template>
<xsl:template match="#*" mode="output">
<xsl:param name="previous" as="xs:string"/>
<xsl:variable name="this" select="." as="node()"/>
<xsl:variable name="thisXPath" select="concat($previous, '/#', name(.),'[', count(preceding-sibling::*[name() = name($this)])+1,']','##',.)"></xsl:variable>
<xsl:value-of select="$thisXPath" />
</xsl:template>
</xsl:stylesheet>
I've a challenging problem and so far I wasn't able to solve.
Within my xlst I have variable which contains a string.
I need to add the following sequence [eol] to this string.
On a fix position namely every 65 characters
I thought to use a function or template to recursive add this charackter.
The reason is that the string length can variate in length.
<xsl:function name="funct:insert-eol" as="xs:string" >
<xsl:param name="originalString" as="xs:string?"/>
<xsl:variable name="length">
<xsl:value-of select="string-length($originalString)"/>
</xsl:variable>
<xsl:variable name="start" as="xs:integer">
<xsl:value-of select="1"/>
</xsl:variable>
<xsl:variable name="eol" as="xs:integer">
<xsl:value-of select="65"/>
</xsl:variable>
<xsl:variable name="newLines">
<xsl:value-of select="$length idiv number('65')"/>
</xsl:variable>
<xsl:for-each select="1 to $newLines">
<xsl:value-of select="substring($originalString, $start, $eol)" />
</xsl:for-each>
</xsl:function>
The more I write code the more variables I need to introduce. This is still my lack on understanding.
For example we want every 5 chars an [eol]
aaaaaaabbbbbbccccccccc
aaaaa[eol]aabbb[eol]bbbcc[eol]ccccc[eol]cc
Hope someone has a starting point for me..
Regards Dirk
Rather straight-forward and short -- no recursion is necessary (and can even be specified as a single XPath expression):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="pLLength" select="5"/>
<xsl:template match="/*">
<xsl:variable name="vText" select="string()"/>
<xsl:for-each select="1 to string-length($vText) idiv $pLLength +1">
<xsl:value-of select="substring($vText, $pLLength*(position()-1)+1, $pLLength)"/>
<xsl:if test=
"not(position() eq last()
or position() eq last() and string-length($vText) mod $pLLength)">[eol]</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on this XML document:
<t>aaaaaaabbbbbbccccccccc</t>
the wanted, correct result is produced:
aaaaa[eol]aabbb[eol]bbbcc[eol]ccccc[eol]cc
When this XML document is processed:
<t>aaaaaaabbbbbbcccccccccddd</t>
again the wanted, correct result is produced:
aaaaa[eol]aabbb[eol]bbbcc[eol]ccccc[eol]ccddd[eol]
You can treat it as a grouping problem, using for-each-group:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:function name="mf:insert-eol" as="xs:string">
<xsl:param name="input" as="xs:string"/>
<xsl:param name="chunk-size" as="xs:integer"/>
<xsl:value-of>
<xsl:for-each-group select="string-to-codepoints($input)" group-by="(position() - 1) idiv $chunk-size">
<xsl:if test="position() gt 1"><xsl:sequence select="'eol'"/></xsl:if>
<xsl:sequence select="codepoints-to-string(current-group())"/>
</xsl:for-each-group>
</xsl:value-of>
</xsl:function>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text">
<xsl:copy>
<xsl:sequence select="mf:insert-eol(., 5)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
That stylesheet transforms
<root>
<text>aaaaaaabbbbbbccccccccc</text>
</root>
into
<root>
<text>aaaaaeolaabbbeolbbbcceolccccceolcc</text>
</root>
Try this one:
<?xml version='1.0' ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:param name="TextToChange" select="'aaaaaaabbbbbbccccccccc'"/>
<xsl:param name="RequiredLength" select="xs:integer(5)"/>
<xsl:template match="/">
<xsl:call-template name="AddText"/>
</xsl:template>
<xsl:template name="AddText">
<xsl:param name="Text" select="$TextToChange"/>
<xsl:param name="TextLength" select="string-length($TextToChange)"/>
<xsl:param name="start" select="xs:integer(1)"/>
<xsl:param name="end" select="$RequiredLength"/>
<xsl:choose>
<xsl:when test="$TextLength gt $RequiredLength">
<xsl:value-of select="substring($Text,$start,$end)"/>
<xsl:text>[eol]</xsl:text>
<xsl:call-template name="AddText">
<xsl:with-param name="Text" select="substring-after($Text, substring($Text,$start,$end))"/>
<xsl:with-param name="TextLength"
select="string-length(substring-after($Text, substring($Text,$start,$end)))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$Text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I saw a similar question on creating a Map.
That answer has this code:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="/">
<xsl:variable name="map">
<map>
<entry key="key-1">value1</entry>
<entry key="key-2">value2</entry>
<entry key="key-3">value3</entry>
</map>
</xsl:variable>
<output>
<xsl:value-of select="msxsl:node-set($map)/map/entry[#key='key-1']"/>
</output>
</xsl:template>
I would like to replace the output command to use a value in my XML to see if it is a key in the map and then replace it with the value.
Is the best way to do a for-each select on the map and compare with contains?
Here is a snippet of the XML:
<document>
<content name="PART_DESC_SHORT" type="text" vse-streams="2" u="22" action="cluster" weight="1">
SCREW - ADJUST
</content>
</document>
The content node value may have a string containing an abbreviation that I want to replace with the full value.
Thanks,
Paul
No need to use a for-each - this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output indent="yes" method="xml" />
<xsl:variable name="abbreviations">
<abbreviation key="Brkt Pivot R">Bracket Pivot R</abbreviation>
<abbreviation key="Foo">Expanded Foo</abbreviation>
<abbreviation key="Bar">Expanded Bar</abbreviation>
</xsl:variable>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:variable name="text" select="."/>
<xsl:variable name="abbreviation" select="msxsl:node-set($abbreviations)/*[#key=$text]"/>
<xsl:choose>
<xsl:when test="$abbreviation">
<xsl:value-of select="$abbreviation"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
will convert any XML in an exact copy expanding all the text matching an abbreviation defined in the abbreviations variable at the top.
If you want to expand the abbreviations only within specific elements you can modify the second template match="..." rule.
On the other hand if you want to expand ANY occurance of all the abbreviations within the text you need loops - that means recursion in XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output indent="yes" method="xml" />
<xsl:variable name="abbreviations">
<abbreviation key="Brkt">Bracket</abbreviation>
<abbreviation key="As">Assembly</abbreviation>
<abbreviation key="Foo">Expanded Foo</abbreviation>
<abbreviation key="Bar">Expanded Bar</abbreviation>
</xsl:variable>
<!-- Replaces all occurrences of a string with another within a text -->
<xsl:template name="replace">
<xsl:param name="text"/>
<xsl:param name="from"/>
<xsl:param name="to"/>
<xsl:choose>
<xsl:when test="contains($text,$from)">
<xsl:value-of select="concat(substring-before($text,$from),$to)"/>
<xsl:call-template name="replace">
<xsl:with-param name="text" select="substring-after($text,$from)"/>
<xsl:with-param name="from" select="$from"/>
<xsl:with-param name="to" select="$to"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Replace all occurences of a list of abbreviation with their expanded version -->
<xsl:template name="replaceAbbreviations">
<xsl:param name="text"/>
<xsl:param name="abbreviations"/>
<xsl:choose>
<xsl:when test="count($abbreviations)>0">
<xsl:call-template name="replaceAbbreviations">
<xsl:with-param name="text">
<xsl:call-template name="replace">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="from" select="$abbreviations[1]/#key"/>
<xsl:with-param name="to" select="$abbreviations[1]"/>
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="abbreviations" select="$abbreviations[position()>1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="replaceAbbreviations">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="abbreviations" select="msxsl:node-set($abbreviations)/*"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Applying this second XSLT to
<document>
<content name="PART_DESC_SHORT" type="text" vse-streams="2" u="22" action="cluster" weight="1">
Brkt Pivot R
</content>
</document>
produces
<document>
<content name="PART_DESC_SHORT" type="text" vse-streams="2" u="22" action="cluster" weight="1">
Bracket Pivot R
</content>
</document>
Note that:
this solution assumes that no abbreviation ovelap (e.g. two separate abbreviations Brk and Brkt)
it uses XSLT 1.0 - a better solution is probably possible with XSLT 2.0
this kind of heavy string processing is likely quite inefficient in XSLT, it is probably better to write an extension function in some other language and call it from the XSLT.