Resolving variables in XSLT - xslt

I'm having problems with resolving variables in XSLT. I have a working XSL file with fixed values that I now want to make dynamic (the variale declarations will be move outside the XSL-file once it works). My current problem is to use variable $beginning in the starts-with function. This is the way all the googling has lead me to believe it should look, but it will not compile. It works how I use it in the substring-after function. How should this be done?
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="oldRoot" select="'top'" />
<xsl:variable name="beginning" select="concat('$.',$oldRoot)" />
<xsl:variable name="newRoot" select="'newRoot'" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="bind/#ref[starts-with(., $beginning)]">
<xsl:attribute name="ref">
<xsl:text>$.newRoot.</xsl:text><xsl:value-of select="$oldRoot"></xsl:value-of>
<xsl:value-of select="substring-after(., $beginning)" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>

In XSLT 1.0 it is considered an error for a template match expression to contain a variable (See https://www.w3.org/TR/xslt#section-Defining-Template-Rules), so this line is failing
<xsl:template match="bind/#ref[starts-with(., $beginning)]">
(I believe some processors may allow it, but if they were following the spec, they shouldn't. It is allowed in XSLT 2.0 though).
What you can do is move the condition inside the template, and handle it with an xsl:choose
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="oldRoot" select="'top'" />
<xsl:variable name="beginning" select="concat('$.',$oldRoot)" />
<xsl:variable name="newRoot" select="'newRoot'" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="bind/#ref">
<xsl:choose>
<xsl:when test="starts-with(., $beginning)">
<xsl:attribute name="ref">
<xsl:text>$.newRoot.</xsl:text><xsl:value-of select="$oldRoot"></xsl:value-of>
<xsl:value-of select="substring-after(., $beginning)" />
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

Related

Flattening with XSLT: I want to move one kind element one level

I have a XML file where elements B are inside elements A and I want to move them up. From:
<?xml version="1.0" encoding="utf-8"?>
<root>
<A>
<C>Text</C>
Text again
More text
<D>Other text</D>
<B>Text again</B>
<C>No</C>
<D>May be</D>
<B>What?</B>
</A>
<A>
Something
<B>Nothing</B>
<D>Again</D>
<B>Content</B>
End
</A>
</root>
I would like to have:
<?xml version="1.0" encoding="utf-8"?>
<root>
<A>
<C>Text</C>
Text again
More text
<D>Other text</D>
</A>
<B>Text again</B>
<A>
<C>No</C>
<D>May be</D>
</A>
<B>What?</B>
<A>
Something
</A>
<B>Nothing</B>
<A>
<D>Again</D>
</A>
<B>Content</B>
<A>
End
</A>
</root>
The closest XSLT program I have is this:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="A">
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="name()='B'">
<xsl:apply-templates select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="A">
<xsl:apply-templates select="."/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
It has two problems: it ignores text nodes (this is probably just a matter of adding |text() to the select="*") but, more important, it creates a element for each node while I would like them to stay together under one . For instance, the above stylesheet makes:
<A><C>No</C></A>
<A><D>May be</D></A>
where I want:
<A><C>No</C>
<D>May be</D></A>
In my XML files, are always direct children of , and there is no or nesting.
The main use case is producing HTML where UL and OL cannot be inside a P.
This question is related but not identical to xslt flattening out child elements in a DocBook para element (and may be also to Flatten xml hierarchy using XSLT
)
As I said in the comment to your question, this is not about moving elements up in hierarchy. It is about grouping nodes, and creating a new parent A element for each group determined by the dividing B element.
In XSLT 1.0 this can be achieved using a so-called sibling recursion:
XSLT 1.0
<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">
<xsl:copy>
<xsl:apply-templates select="A"/>
</xsl:copy>
</xsl:template>
<xsl:template match="A">
<xsl:copy>
<xsl:apply-templates select="node()[1][not(self::B)]" mode="sibling"/>
</xsl:copy>
<xsl:apply-templates select="B[1]" mode="sibling"/>
</xsl:template>
<xsl:template match="node()" mode="sibling">
<xsl:copy-of select="." />
<xsl:apply-templates select="following-sibling::node()[1][not(self::B)]" mode="sibling"/>
</xsl:template>
<xsl:template match="B" mode="sibling">
<xsl:copy-of select="." />
<xsl:if test="following-sibling::node()[normalize-space()]">
<A>
<xsl:apply-templates select="following-sibling::node()[1][not(self::B)]" mode="sibling"/>
</A>
<xsl:apply-templates select="following-sibling::B[1]" mode="sibling"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
An XSLT-1.0 solution - which is quite ugly - is the following. The output is as desired, but only for this simple MCVE. A general solution would be far more complicated as #michael.hor257k mentioned in the comments. Without more data it is unlikely to create a better solution in XSLT-1.0. Solutions for XSLT-2.0 and above may simplify this.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/root">
<xsl:copy>
<xsl:for-each select="A">
<xsl:if test="normalize-space(text()[1])">
<A>
<xsl:copy-of select="text()[1]" />
</A>
</xsl:if>
<xsl:if test="preceding::*">
<xsl:copy-of select="B[1]" />
</xsl:if>
<A>
<xsl:copy-of select="C[1] | C[1]/following-sibling::text()[1] | D[1]" />
</A>
<xsl:if test="not(preceding::*)">
<xsl:copy-of select="B[1]" />
</xsl:if>
<A>
<xsl:copy-of select="C[2] | C[2]/following-sibling::text()[1]" />
<xsl:if test="D[2]">
<xsl:copy-of select="D[2]" />
</xsl:if>
</A>
<xsl:copy-of select="B[2]" />
<xsl:if test="normalize-space(text()[last()])">
<A>
<xsl:copy-of select="text()[last()]" />
</A>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Concerning the situation of
<A><C>No</C></A>
<A><D>May be</D></A>
It is handled appropriately in the above code. So its output is
<A>
<C>No</C>
<D>May be</D>
</A>
Easy in XSLT 2 or 3 with group-adjacent=". instance of element(B)" or group-adjacent="boolean(self::B)", here is an XSLT 3 example (XSLT 3 is supported by Saxon 9.8 or 9.9 on Java and .NET (https://sourceforge.net/projects/saxon/files/Saxon-HE/) and by Altova since 2017 releases):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="A">
<xsl:for-each-group select="node()" group-adjacent=". instance of element(B)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy select="..">
<xsl:apply-templates select="current-group()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/gWmuiKv
In XSLT 2 you need to spell out the <xsl:mode on-no-match="shallow-copy"/> as the identity transformation template and use xsl:element instead xsl:copy:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="A">
<xsl:for-each-group select="node()" group-adjacent=". instance of element(B)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{name(..)}" namespace="{namespace-uri(..)}">
<xsl:apply-templates select="current-group()"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:transform>
http://xsltransform.hikmatu.com/pPqsHT2

Avoid repetition in XSLT template rules using XSLT 1.0

Given the following XML document:
<document>
<a>123</a>
<foo_1>true</foo_1>
<foo_2>false</foo_2>
<foo_3>true</foo_3>
<foo_4>true</foo_4>
<foo_5>false</foo_5>
<b/>
<bar_1>false</bar_1>
<bar_2>false</bar_2>
<bar_3>true</bar_3>
<bar_4>false</bar_4>
<bar_5>true</bar_5>
<c>some text</c>
</document>
I want to transform this document by eliminating all enumerated elements containing false and by converting all enumerated elements containing true into the form <prefix_n>value</prefix_n>, where value is the number after the underscore.
For the example given above the result should look like this:
<document>
<a>123</a>
<foo_n>1</foo_n>
<foo_n>3</foo_n>
<foo_n>4</foo_n>
<b/>
<bar_n>3</bar_n>
<bar_n>5</bar_n>
<c>some text</c>
</document>
For this I am using the following transformation, which works fine.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<!-- standard copy template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[substring(name(), 1, 4) = 'foo_']">
<xsl:if test=". = 1 or . = 'true'">
<xsl:element name="foo_n">
<xsl:value-of select="substring-after(name(), 'foo_')"/>
</xsl:element>
</xsl:if>
</xsl:template>
<xsl:template match="*[substring(name(), 1, 4) = 'bar_']">
<xsl:if test=". = 1 or . = 'true'">
<xsl:element name="bar_n">
<xsl:value-of select="substring-after(name(), 'bar_')"/>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Now I'm facing the problem that I have to deal with a multitude of different prefixes, not just foo_ and bar_.
Is there a way to turn the template rules into a named template where I can pass in the prefix as an argument, thereby avoiding to write a lot of repetitive template rules?
Here's one way you could look at it:
XSLT 1.0
<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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'foo_') or starts-with(name(), 'bar_')]">
<xsl:if test=". = 1 or . = 'true'">
<xsl:element name="{substring-before(name(), '_')}-n">
<xsl:value-of select="substring-after(name(), '_')"/>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Here's another:
XSLT 1.0
<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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[contains(translate(name(), '123456789', '000000000'), '_0')]">
<xsl:if test=". = 1 or . = 'true'">
<xsl:variable name="prefix" select="substring-before(translate(name(), '123456789', '000000000'), '_0')" />
<xsl:element name="{$prefix}-n">
<xsl:value-of select="substring(name(), string-length($prefix) + 2)"/>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This last one should work with any prefix followed by _[1-9] - including prefixes that contain an additional underscore and including prefixes that contain other prefixes.
Is there a way to turn the template rules into a named template where I can pass in the prefix as an argument
Well, sure:
<xsl:template name="if-true">
<xsl:param name="prefix"/>
<xsl:if test=". = 1 or . = 'true'">
<xsl:element name="{concat($prefix, 'n')}">
<xsl:value-of select="substring-after(name(), $prefix)"/>
</xsl:element>
</xsl:if>
</xsl:template>
<!-- example using the above: -->
<xsl:template match="*[starts-with(name(), 'foo_')]">
<xsl:call-template name="if-true">
<xsl:with-param name="prefix" select="'foo_'"/>
</xsl:call-template>
</xsl:template>
But it's not clear whether that would really achieve your objective:
, thereby avoiding to write a lot of repetitive template rules?
because you would still need a bunch of templates of the second kind to match the specific elements you want to transform. A solution along the lines #michael.hor257k suggested, that avoids duplication by making the same template match all the elements you want to transform, is a better alternative.

get value of variable to another template

There are references I found in the internet about the passing of variable to other template. I tried to follow all the references but, I can't get the value that I need to populate. I have this xml file:
<Item>
<Test>
<ID>123345677</ID>
</Test>
<DisplayID>99884534</DisplayID>
</Item>
I need to populate MsgId element if the DisplayID is not null, else get value from the ID. My XSLT:
<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="ID">
<xsl:variable name="IDV" select="substring(.,0,35)"/>
<xsl:apply-templates select="DisplayID">
<xsl:with-param name="IDP" select="$IDV"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="DisplayID">
<xsl:param name="IDP"/>
<xsl:element name="MsgId">
<xsl:choose>
<xsl:when test=".!='' or ./*">
<xsl:value-of select="substring(.,0,35)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($IDP,0,35)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:template>
The condition if DisplayID is not null is working, however, if I remove the value of DisplayID, there's no value getting from the ID. I don't know if I doing it correctly.
Your feedback is highly appreciated.
Please try this,
Demo for references : http://xsltransform.net/ejivdHb/16
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="http://locomotive/bypass/docx" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Item">
<xsl:element name="MsgId">
<xsl:choose>
<xsl:when test="DisplayID !='' ">
<xsl:value-of select="substring(DisplayID , 0 ,35)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(Test/ID,0,35)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Since this is tagged XSLT 2.0, the match="Item" template from #TechBreak can be replaced by
<xsl:template match="Item">
<MsgId>
<xsl:value-of select="substring(
if (DisplayId != '')
then DisplayID
else Test/ID, 1 ,35)"/>
</MsgId>
</xsl:template>
(Note character counting starts from 1)

XSLT regular expression to remove sequences text

I have an XML, something like this:
<?xml version="1.0" encoding="UTF-8"?>
<earth>
<computer>
<parts>;;remove;;This should stay;;remove too;;This stay;;yeah also remove;;this stay </parts>
</computer>
</earth>
I want to create an XSLT 2.0 transform to remove all text which starts and ends with ;;
<?xml version="1.0" encoding="utf-8"?>
<earth>
<computer>
<parts>This should stay This stay this stay </parts>
</computer>
</earth>
Try to do something like this but no luck:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="fn">
<xsl:output encoding="utf-8" method="xml" indent="yes" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="parts">
<xsl:element name="parts" >
<xsl:value-of select="replace(., ';;.*;;','')" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Wow, what a dumb way to markup text. You have XML at your disposal, why not use it? And even if marking this way, why not use different symbols for opening and closing the marked parts?
Anyway, I believe this returns the expected result:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="parts">
<xsl:copy>
<xsl:value-of select="replace(., ';;.+?;;', '')" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Another approach would be tokenize on ";;" as separator, then remove all even-numbered tokens:
<xsl:template match="parts">
<parts>
<xsl:value-of select="tokenize(.,';;')[position() mod 2 = 1]"
separator=""/>
</parts>
</xsl:template>
XSLT 1.0
For this kind of thing I'd use recursion. Just using string replace you can get what is before and after a certain character (or set of characters). All you need to do is continually loop over the string until there are no more occurrences of the replace character, like follows:
<xsl:template name="string-remove-between">
<xsl:param name="text" />
<xsl:param name="remove" />
<xsl:choose>
<xsl:when test="contains($text, $remove)">
<xsl:value-of select="substring-before($text,$remove)" />
<xsl:call-template name="string-remove-between">
<xsl:with-param name="text" select="substring-after(substring-after($text,$remove), $remove)" />
<xsl:with-param name="remove" select="$remove" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Then you'd just call the template with your text and the section you want to remove:
<xsl:call-template name="string-remove-between">
<xsl:with-param name="text" select="parts"/>
<xsl:with-param name="remove">;;</xsl:with-param>
</xsl:call-template>
Note that there are two substring-after calls, this makes sure we get the second instance of the replace characters ';;' so we aren't pulling in the text between.

correct xsl template matching syntax

I'm learning XSL and hope to get some help. I want to extract part of the following datasets.xml and output them as tab delimited texts:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<listDatasetsResponse xmlns="http://www.algorithmics.com/schema">
<status>OK</status>
<datasets size="31">
<dataset>
<id>stress_20150910_20150910_160259</id>
<basedOn>Mimm_20150910_20150910_030922</basedOn>
<active>false</active>
<sandbox>true</sandbox>
<ownedBy>admin</ownedBy>
<createdOn>2015-09-10T16:04:24.199-04:00</createdOn>
<createdBy>rtcesupp</createdBy>
<evaluated>true</evaluated>
<stopped>false</stopped>
</dataset>
<dataset>
<id>imm_20150910_20150910_140315</id>
<basedOn>Mimm_20150910_20150910_030922</basedOn>
<active>true</active>
<sandbox>true</sandbox>
<ownedBy>admin</ownedBy>
<createdOn>2015-09-10T14:04:42.696-04:00</createdOn>
<createdBy>rtcesupp</createdBy>
<evaluated>true</evaluated>
<stopped>false</stopped>
</dataset>
</datasets>
</listDatasetsResponse>
here is the XSL I used:
$ vi dataset.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="iso-8859-1"/>
<xsl:strip-space elements="*" />
<xsl:template match="/listDatasetsResponse/datasets/dataset">
<xsl:value-of select="id"/><xsl:text> </xsl:text>
<xsl:choose>
<xsl:when test="active='true'">
<xsl:text>ACTIVE</xsl:text>
</xsl:when>
<xsl:when test="stopped='true'">
<xsl:text>,STOPPED</xsl:text>
</xsl:when>
<xsl:when test="evaluated='true'">
<xsl:text>,EVALUATED</xsl:text>
</xsl:when>
</xsl:choose>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
I expected the result of:
stress_20150910_20150910_160259 EVALUATED
imm_20150910_20150910_140315 ACTIVE,EVALUATED
but what I got is:
OKstress_20150910_20150910_160259Mimm_20150910_20150910_030922falsetrueadmin2015-09-10T16:04:24.199-04:00rtcesupptruefalseimm_20150910_20150910_140315Mimm_20150910_20150910_030922truetrueadmin2015-09-10T14:04:42.696-04:00rtcesupptruefalse
It seemed like the XSL stylesheet was ignored. Could some one point me to correct XSL template matching syntax?
The fundamental problem in your XSL is that none of the XPath expression being used match the element in the source XML. Notice your XML has default namespace declared at the root element :
xmlns="http://www.algorithmics.com/schema"
Descendant elements without explicit prefix and without local default namespace inherits ancestor default namespace implicitly. To match element in namespace, simply declare a prefix that point to the namespace-uri and use that prefix in your XPath expressions, for example :
<xsl:stylesheet .....
xmlns:d="http://www.algorithmics.com/schema">
.....
<xsl:template match="/d:listDatasetsResponse/d:datasets/d:dataset">
<xsl:value-of select="d:id"/><xsl:text> </xsl:text>
.....
</xsl:template>
</xsl:stylesheet>
How about ...
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://www.algorithmics.com/schema"
version="1.0" >
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()" />
<xsl:template match="/">
<xsl:apply-templates select="a:listDatasetsResponse/a:datasets/a:dataset" />
</xsl:template>
<xsl:template match="a:dataset">
<xsl:value-of select="a:id"/>
<xsl:text> </xsl:text>
<xsl:apply-templates select="a:active|a:stopped|a:evaluated" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="a:active[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:text>ACTIVE</xsl:text>
</xsl:template>
<xsl:template match="a:stopped[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:text>STOPPED</xsl:text>
</xsl:template>
<xsl:template match="a:evaluated[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:text>EVALUATED</xsl:text>
</xsl:template>
</xsl:stylesheet>
... or this version ...
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://www.algorithmics.com/schema"
version="1.0" >
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()" />
<xsl:template match="/">
<xsl:apply-templates select="a:listDatasetsResponse/a:datasets/a:dataset" />
</xsl:template>
<xsl:template match="a:dataset">
<xsl:value-of select="a:id"/>
<xsl:text> </xsl:text>
<xsl:apply-templates select="a:active|a:stopped|a:evaluated" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="a:active[.='true'] | a:stopped[.='true'] | a:evaluated[.='true']">
<xsl:if test="preceding-sibling::a:active[.='true']|
preceding-sibling::a:stopped[.='true']|
preceding-sibling::a:evaluated[.='true']">,</xsl:if>
<xsl:value-of select="translate( local-name(), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
</xsl:template>
</xsl:stylesheet>