Using xsl to parse xml output, use of variables? - html

I'm trying to parse an xml file output from googletest to display the results in a pretty way in an html page. I have had a fair crack at it and have a working solution. Only one thing left to do.
Currently each class has a header and each function+test is displayed underneath, I'm looking for a way to extract the function name from the xml attribute to have it as its own sub-heading. Example below
Current Output
Class Name
functionName_testName
functionName_test2Name
function2Name_testName
Desired Output
Class Name
functionName
testName
test2Name
function2Name
testName
XML To be Parsed
<testsuite name="testPacketProcessor" tests="27" failures="0" disabled="0" errors="0" time="36.875">
<testcase name="testInitialise_IpNotSet" status="run" time="0" classname="testPacketProcessor" />
<testcase name="testInitialise_GoodIp" status="run" time="2.046" classname="testPacketProcessor" />
<testcase name="testInitialise_BadIp" status="run" time="0.032" classname="testPacketProcessor" />
</testsuite>
XSL Currently Implemented
<xsl:for-each select="testsuites/testsuite">
<div class="testHeading">
<span><xsl:value-of select="substring-after(#name,'test')"/></span>
</div>
<xsl:for-each select="testcase">
<div class="testReport">
<xsl:if test="not(starts-with(#name,'DISABLED'))"><xsl:value-of select="substring-after(#name,'test')"/></xsl:if>
<xsl:if test="starts-with(#name,'DISABLED')"><xsl:value-of select="substring-after(#name,'DISABLED_test')"/></xsl:if>
<xsl:text> - </xsl:text>
<xsl:if test="starts-with(#status,'run')">
<xsl:if test="failure"><xsl:text>Fail</xsl:text></xsl:if>
<xsl:if test= "not(failure)"><xsl:text>Pass</xsl:text></xsl:if>
</xsl:if>
<xsl:if test="starts-with(#status,'not')"><xsl:text>Disabled</xsl:text></xsl:if>
</div>
</xsl:for-each>
</xsl:for-each>
Using the above xml as an example I would like a subheading of "Initialise" under the packet processor heading with each test doucumented under the subheading
Any advice on the best way to go about this would be much appreciated. I've looked at xsl variables but cannot fathom how to deal with not being able to change the value. Have also read a little on xsl templates and recursion but I'm not sure how that would work for this situation.
Thanks in advance
Dougie

This transformation produces the subheading and name for each testcase:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="testcase">
Subheading: <xsl:value-of select=
"substring-before(substring-after(#name, 'test'),
'_'
)
"/>
Name: <xsl:value-of select="substring-after(#name, '_')"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document:
<testsuites>
<testsuite name="testPacketProcessor" tests="27" failures="0" disabled="0" errors="0" time="36.875">
<testcase name="testInitialise_IpNotSet" status="run" time="0" classname="testPacketProcessor" />
<testcase name="testInitialise_GoodIp" status="run" time="2.046" classname="testPacketProcessor" />
<testcase name="testInitialise_BadIp" status="run" time="0.032" classname="testPacketProcessor" />
</testsuite>
</testsuites>
the wanted, correct result is produced:
Subheading: Initialise
Name: IpNotSet
Subheading: Initialise
Name: GoodIp
Subheading: Initialise
Name: BadIp
Explanation: Using the standard XPath functions substring-before() and substring-after()
Edit: In a comment the OP clarified that he wanted grouping by function (tired of bad questions ?):
This transformation performs the wanted grouping:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kTestsByFunction" match="testcase"
use="substring-before(substring-after(#name, 'test'),
'_'
)"
/>
<xsl:template match=
"testcase
[generate-id()
=
generate-id(key('kTestsByFunction',
substring-before(substring-after(#name, 'test'),
'_'
)
)[1]
)
]
">
Subheading:
<xsl:value-of select=
"substring-before(substring-after(#name, 'test'),
'_'
)
"/>
<xsl:for-each select=
"key('kTestsByFunction',
substring-before(substring-after(#name, 'test'),
'_'
)
)
">
Name:
<xsl:value-of select="substring-after(#name, '_')"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
and produces the wanted result:
Subheading:
Initialise
Name:
IpNotSet
Name:
GoodIp
Name:
BadIp

Related

Using xml-to-json. "Inner XML/XHTML" should go in a json string value field

I'm using Saxon Home Edition to convert XML to JSON:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />
<xsl:template match="/">
<xsl:variable name="xmljson">
<map xmlns="http://www.w3.org/2005/xpath-functions">
<string key="name">Some name</string>
<string key="description">A nice description</string>
</map>
</xsl:variable>
<xsl:value-of select="xml-to-json($xmljson)" />
</xsl:template>
</xsl:stylesheet>
Produces the desired output:
{"name":"Some name","description":"A nice description"}
The description field contains arbitrary complex xhtml. The template rule does not work with the following description field:
<string key="description">A <strong>nice</strong> description</string>
Error message:
xml-to-json: unknown element <strong>
Enclosing the description in a CDATA section does work:
<string key="description"><![CDATA[A <strong>nice</strong> description]]></string>
Desired output:
{"name":"Some name","description":"A <strong>nice<\/strong> description"}
Problem/Question
The content of the description field is the result of a transformation. So with and withou CDATA fails. This will not work:
<string key="description"><xsl:apply-template select="description" /></string>
Neither this:
<string key="description"><![CDATA[<xsl:apply-template select="description" />]]></string>
Use the serialize() function to produce escaped XML markup as text.
<xsl:variable name="options" as="element()">
<output:serialization-parameters xmlns:output="http://www.w3.org/2010/xslt-xquery-serialization">
<output:omit-xml-declaration value="yes"/>
</output:serialization-parameters>
</xsl:variable>
<xsl:variable name="xmljson" as="element()">
<map xmlns="http://www.w3.org/2005/xpath-functions">
<string key="name">Some name</string>
<string key="description"><xsl:value-of select="serialize(description, $options)"/></string>
</map>
</xsl:variable>
I came up with the following quick hack. Have to check this tomorrow again, kind of tired now...t
Edit: This is a horrible hack! The template rule does not treat special characters. Please read the comments below.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />
<xsl:template match="/">
<xsl:variable name="doc">
<description>A <strong class="red">nice</strong> description</description>
</xsl:variable>
<xsl:variable name="xmljson">
<map xmlns="http://www.w3.org/2005/xpath-functions">
<string key="description"><xsl:apply-templates select="$doc/description/text()|$doc/description/*" /></string>
</map>
</xsl:variable>
<xsl:value-of select="xml-to-json($xmljson)" />
</xsl:template>
<xsl:template match="*">
<!-- opening tag -->
<xsl:text><</xsl:text>
<xsl:value-of select="name()"/>
<!-- attribute nodes -->
<xsl:apply-templates select="#*"/>
<xsl:text>></xsl:text>
<xsl:apply-templates/>
<!-- closing tag -->
<xsl:text><</xsl:text>
<xsl:text>/</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>></xsl:text>
</xsl:template>
<xsl:template match="#*">
<xsl:text> </xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>="</xsl:text>
<xsl:value-of select="." />
<xsl:text>"</xsl:text>
</xsl:template>
</xsl:stylesheet>
Produces the desired output:
{"description":"A <strong class=\"red\">nice<\/strong> description"}

Converting an RSS pubDate to a mySQL time stamp format using XSLT

I've got a little tool which strips out and re-arranges an iTunes formatted RSS feed and converts it to a nice simple XML file.
I then import the cleansed XML into mySQL to do things with later.
I need to be able to convert the pubDate in the feed to a mySQL timestamp so I can import this properly into a TIMESTAMP field in my table.
I'm having some issues with it.
My current XSL file does a tidy up on the date, but I don't need this at all.
I just want the <pubDate> node to have the correct mySQL friendly timestamp inside instead.
I've not yet managed to find anything which does what I need. Any pointers?
Here is my XSLT file...
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:cc="http://web.resource.org/cc/"
xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
xmlns:libsyn="http://libsyn.com/rss-extension"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
exclude-result-prefixes="atom cc itunes libsyn media rdf">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<data>
<entries>
<xsl:apply-templates select="rss/channel/item"></xsl:apply-templates>
</entries>
</data>
</xsl:template>
<xsl:template match="item">
<entry>
<title><xsl:value-of select="title"/></title>
<link><xsl:value-of select="link"/></link>
<description><xsl:value-of select="description" disable-output-escaping="yes"/></description>
<subtitle><xsl:value-of select="itunes:subtitle"/></subtitle>
<pubDate><xsl:value-of select="pubDate"/></pubDate>
<xsl:apply-templates select="pubDate"/>
<explicit><xsl:value-of select="itunes:explicit"/></explicit>
<podcastImage><xsl:value-of select="itunes:image/#href"/></podcastImage>
<podcastURL><xsl:value-of select="enclosure/#url"/></podcastURL>
<podcastLength><xsl:value-of select="enclosure/#length"/></podcastLength>
<podcastDuration><xsl:value-of select="itunes:duration"/></podcastDuration>
</entry>
</xsl:template>
<xsl:template match="pubDate">
<date>
<xsl:attribute name="time"><xsl:value-of select="substring(text(),18,5)"/></xsl:attribute>
<xsl:call-template name="format-from-rfc-to-iso">
<xsl:with-param name="rfc-date" select="text()"/>
</xsl:call-template>
</date>
</xsl:template>
<xsl:template name="format-from-rfc-to-iso">
<xsl:param name="rfc-date"/>
<xsl:param name="day-with-zero" select="format-number(substring(substring($rfc-date,6,11),1,2),'00')"/>
<xsl:param name="month-with-zero">
<xsl:if test="contains($rfc-date,'Jan')">01</xsl:if>
<xsl:if test="contains($rfc-date,'Feb')">02</xsl:if>
<xsl:if test="contains($rfc-date,'Mar')">03</xsl:if>
<xsl:if test="contains($rfc-date,'Apr')">04</xsl:if>
<xsl:if test="contains($rfc-date,'May')">05</xsl:if>
<xsl:if test="contains($rfc-date,'Jun')">06</xsl:if>
<xsl:if test="contains($rfc-date,'Jul')">07</xsl:if>
<xsl:if test="contains($rfc-date,'Aug')">08</xsl:if>
<xsl:if test="contains($rfc-date,'Sep')">09</xsl:if>
<xsl:if test="contains($rfc-date,'Oct')">10</xsl:if>
<xsl:if test="contains($rfc-date,'Nov')">11</xsl:if>
<xsl:if test="contains($rfc-date,'Dec')">12</xsl:if>
</xsl:param>
<xsl:param name="year-full" select="format-number(substring(substring($rfc-date,6,11),7,5),'####')"/>
<xsl:param name="rfc-date-to-iso" select="concat($year-full,'-',$month-with-zero,'-',$day-with-zero)"/>
<xsl:value-of select="$rfc-date-to-iso"/>
</xsl:template>
</xsl:stylesheet>
The current date/time looks like this from the rss feed:
<pubDate>Sun, 07 Feb 2016 00:00:56 -0500</pubDate>
I'd like it to by displayed like this so it can be inserted into mySQL:
<pubDate>2016-02-07 00:00:56</pubDate>
I use PHP to process this.
$xml = new DOMDocument;
$xml->load('podbean.xml');
$xsl = new DOMDocument;
$xsl->load('podbean.xsl');
// Configure the transformer
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl); // attach the xsl rules
$proc->transformToXML($xml);
$proc->transformToURI($xml,'itunes.xml');
Simon
Using a processor that supports the EXSLT str:tokenize() function (as libxslt does), you can do something like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- ... -->
<xsl:template match="item">
<entry>
<!-- ... -->
<pubDate>
<xsl:variable name="date-tokens" select="str:tokenize(pubDate, ' ' )" />
<!-- year -->
<xsl:value-of select="$date-tokens[4]" />
<xsl:text>-</xsl:text>
<!-- month -->
<xsl:variable name="mmm" select="$date-tokens[3]" />
<xsl:variable name="m" select="string-length(substring-before('JanFebMarAprMayJunJulAugSepOctNovDec', $mmm)) div 3 + 1" />
<xsl:value-of select="format-number($m, '00')" />
<xsl:text>-</xsl:text>
<!-- day -->
<xsl:value-of select="format-number($date-tokens[2], '00')" />
<xsl:text> </xsl:text>
<!-- time -->
<xsl:value-of select="$date-tokens[5]" />
</pubDate>
<!-- ... -->
</entry>
</xsl:template>
</xsl:stylesheet>

XSL Delimited Values for HTML Links and their Descriptions

I am trying to debug some xsl code that puts links gathered from an XML Document with their descriptions in a list, it seems to be working, except when handling multiple links gathered from a single string that are delimited with a "|" and "^" symbol.
The XML Line that contains this code is very similar to this:
<WebContent>
<content_type_desc>Links</content_type_desc>
<content_value>Google|http://www.google.com^Yahoo|http://www.yahoo.com^Bing|Http://www.bing.com</content_value>
</WebContent>
The xsl that handles this is like
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="html" indent="yes" encoding="utf-8"/>
<xsl:template match="/">
<!--Links-->
<xsl:variable name="links" select="//WebContent[content_type_desc='Links']/content_value "/>
<!--Links-->
<!--Links section-->
<xsl:if test="$links != ''">
<br />
<xsl:choose>
<xsl:when test="contains($links,'^')">
<xsl:variable name="URL" select="substring-after(substring-before($links,'^'),'|')"/>
<xsl:variable name="URLText" select="substring-before($links,'|')"/>
<strong>Links: </strong><br />
<xsl:value-of select="$URLText"/>
<br />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="URL" select="substring-after($links,'|')"/>
<xsl:variable name="URLText" select="substring-before($links,'|')"/>
<strong>Links: </strong><br />
<xsl:value-of select="$URLText"/>
<br />
</xsl:otherwise>
</xsl:choose>
<br />
</xsl:if>
<!--Links section-->
</xsl:template>
</xsl:stylesheet>
What's wrong with this is that it only outputs
<a href="http://www.google.com>Google</a>
When I would like it to output
<a href="http://www.google.com>Google</a>
<a href="http://www.yahoo.com>Yahoo</a>
<a href="http://www.bing.com>Bing</a>
Any help will be greatly appreciated as this has me completely stumped.
Assuming you are actually only using XSLT 1.0, to solve this you could make use of a named-template which is called recursively.
At the moment, the code is only processing the URL before the first ^ in the code. What you could do is put the main bulk of the code that checks the links variable in a named template. Then, in the xsl:when condition that runs when the URL does contain a ^ you add a recursive call to the named template, but passing in only the substring after the ^.
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="html" indent="yes" encoding="utf-8"/>
<xsl:template match="/">
<xsl:param name="links" select="//WebContent[content_type_desc='Links']/content_value "/>
<xsl:if test="$links != ''">
<strong>Links: </strong>
<br />
<xsl:call-template name="splitlinks">
<xsl:with-param name="links" select="$links" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="splitlinks">
<xsl:param name="links" />
<xsl:choose>
<xsl:when test="contains($links,'^')">
<xsl:variable name="URL" select="substring-after(substring-before($links,'^'),'|')"/>
<xsl:variable name="URLText" select="substring-before($links,'|')"/>
<xsl:value-of select="$URLText"/>
<br />
<xsl:call-template name="splitlinks">
<xsl:with-param name="links" select="substring-after($links,'^')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="URL" select="substring-after($links,'|')"/>
<xsl:variable name="URLText" select="substring-before($links,'|')"/>
<xsl:value-of select="$URLText"/>
<br />
</xsl:otherwise>
</xsl:choose>
<br />
</xsl:template>
</xsl:stylesheet>
Given that you have tagged your question as classic ASP your XSLT processor is likely some version of MSXML for which there is an implementation of http://www.exslt.org/str/functions/tokenize/index.html so you could use that.

How to call template with name as a variable in XSLT?

I have the following XML document which needs to be parsed with an XSLT to HTML.
<root>
<c>
<c1>
<id>1</id>
<text>US</text>
</c1>
<c1>
<id>2</id>
<text>UK</text>
</c1>
</c>
</root>
The XSLT for converting this to HTML is given below.
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" />
<xsl:template match="root">
<html>
<xsl:for-each select="c/c1">
**<xsl:variable name="vTemplate" select="text"/>
<xsl:apply-templates select="$vTemplate[#name='text'"/>**
</xsl:for-each>
</html>
</xsl:template>
<xsl:template match="xsl:template[#name='text']" name="text">
<select>
<xsl:attribute name="id">
<xsl:value-of select="id"/>
</xsl:attribute>
</select>
</xsl:template>
</xsl:stylesheet>
I need to call a template depends up on the text field. So for the value US, one template will be executed and for UK, another will executed.
How to achieve this with a variable as a template name while calling the template? I just made a try but it gives error. Can someone help me to figure out where i made wrong?
I think it is not possible to choose name of template to be called dynamically. What could be done is xsl:choose utilization (perhaps with combination with mode attribute), like this
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<html>
<xsl:for-each select="c/c1">
<xsl:choose>
<xsl:when test="text = 'US'">
<xsl:apply-templates select="text" mode="US"/>
</xsl:when>
<xsl:when test="text = 'UK'">
<xsl:apply-templates select="text" mode="UK"/>
</xsl:when>
<xsl:otherwise>
<xsl:comment>Something's wrong</xsl:comment>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</html>
</xsl:template>
<xsl:template match="text" mode="US">
<xsl:comment>US mode</xsl:comment>
<select>
<xsl:attribute name="id">
<xsl:value-of select="preceding-sibling::id"/>
</xsl:attribute>
</select>
</xsl:template>
<xsl:template match="text" mode="UK">
<xsl:comment>UK mode</xsl:comment>
<select>
<xsl:attribute name="id">
<xsl:value-of select="preceding-sibling::id"/>
</xsl:attribute>
</select>
</xsl:template>
</xsl:stylesheet>
Or you can use match with appropriate predicate and avoid for-each like this
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<html>
<xsl:apply-templates select="//c1" />
</html>
</xsl:template>
<xsl:template match="c1[text = 'US']">
<xsl:comment>US mode</xsl:comment>
<select id="{id}" />
</xsl:template>
<xsl:template match="c1[text = 'UK']">
<xsl:comment>UK mode</xsl:comment>
<select id="{id}" />
</xsl:template>
</xsl:stylesheet>
The id attribute of select can be also filled by "Attribute value templates" (xpath in curly brackets) as shown in previous sample.

Manipulating xsl attributes before XSLT

Given xsl file to change color
<xsl:if test="!colorChanges()">
<StaticLabel style="{StaticLabel/#style}">
<Caption>
<xsl:value-of select="$StaticLabel/Caption"/>
</Caption>
<PreviewCaption>
<xsl:value-of select="$StaticLabel/Caption"/>
</PreviewCaption>
</StaticLabel>
</xsl:if>
Given this xml data
<StaticLabel style="font-family:Arial;color:#000000;font-size:9pt">
<Caption><![CDATA[FoodType]]></Caption>
<Name><![CDATA[French]]></Name>
</StaticLabel>
</xsl:if>
Current result after xslt
<StaticLabel style="font-family:Arial;color:#000000;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
Is there anyway to ONLY change the color while keeping other style attributes before xslt is performed without updating the xml file?
Expected result:
<StaticLabel style="font-family:Arial;color:#CCCCCC;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
Possible XSL solution
<xsl:if test="colorChanges()">
<StaticLabel>
<xsl:attribute name="style">
//other style attributes stay same and ONLY edit color
<xsl:text>color:#CCCCCC"</xsl:text>
</xsl:attribute>
<Caption>
<xsl:value-of select="$StaticLabel/Caption"/>
</Caption>
<PreviewCaption>
<xsl:value-of select="$StaticLabel/Caption"/>
</PreviewCaption>
</StaticLabel>
</xsl:if>
I. XSLT 1.0 solution:
This generic transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pStyle" select="'font-size:12pt'"/>
<xsl:variable name="vStyleName"
select="concat(';',substring-before($pStyle, ':'),':')"/>
<xsl:variable name="vCurrentStyleValue" select=
"substring-before(substring-after(concat(';', /*/#style, ';'), $vStyleName),
';')"/>
<xsl:variable name="vCurrentStyle"
select="concat($vStyleName,$vCurrentStyleValue)"/>
<xsl:template match="node()|#*" name="identiy">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/#style">
<xsl:attribute name="style">
<xsl:variable name="vprecStyles" select=
"substring-before(concat(';',., $vCurrentStyle), $vCurrentStyle)"/>
<xsl:value-of select="substring($vprecStyles, 2)"/>
<xsl:if test="$vprecStyles">;</xsl:if>
<xsl:value-of select="$pStyle"/>
<xsl:value-of select="substring-after(concat(';',.), $vCurrentStyle)"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<StaticLabel style="font-family:Arial;color:#000000;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
produces the wanted, correct result:
<StaticLabel style="font-family:Arial;color:#CCCCCC;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
When we change the $pStyle parameter to:
<xsl:param name="pStyle" select="'font-family:Courier'"/>
then again the wanted, correct result is produced:
<StaticLabel style="font-family:Courier;color:#000000;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
If we change the $pStyle parameter to:
<xsl:param name="pStyle" select="'font-size:12pt'"/>
We again get the correct result:
<StaticLabel style="font-family:Arial;color:#000000;font-size:12pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
Finally, if we change the $pStyle parameter to:
<xsl:param name="pStyle" select="'line-height:15pt'"/>
we again get the correct, desired result:
<StaticLabel style="font-family:Arial;color:#000000;font-size:9pt;line-height:15pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
This is quite error-proof -- observe:
<StaticLabel style="font-family:Arial;background-color:#ffffff;color:#000000;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
and we have the $pStyle parameter again as:
<xsl:param name="pStyle" select="'color:#CCCCCC'"/>
We still get the correct result (not confused by other CSS properties ending with "color"):
<StaticLabel style="font-family:Arial;background-color:#ffffff;color:#CCCCCC;font-size:9pt">
<Caption>Food Type</Caption>
<PreviewCaption>French</PreviewCaption>
</StaticLabel>
II. XSLT 2.0 solution -- much easier:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pStyle" select="'color:#CCCCCC'"/>
<xsl:variable name="vStyleName" select=
"substring-before($pStyle, ':')"/>
<xsl:variable name="vcurStyles"
select="tokenize(/*/#style, ';')"/>
<xsl:variable name="vnewStyles" select=
"if($vcurStyles[substring-before(.,':') eq $vStyleName])
then ($vcurStyles[substring-before(.,':') ne $vStyleName],
$pStyle)
else ($vcurStyles, $pStyle)
"/>
<xsl:template match="node()|#*" name="identiy">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/#style">
<xsl:attribute name="style" select="string-join($vnewStyles, ';')">
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>