I newby to XSLT and having some trouble to solve this problem.
The input is coming from an XML Excel document and has this format :
<Row>
<Cell><Data ss:Type="String">ToE.3</Data></Cell>
<Cell ss:Index="15"><Data ss:Type="String">Maintain</Data></Cell>
<Cell><Data ss:Type="Number">3</Data></Cell>
<Cell><Data ss:Type="String">Other</Data></Cell>
<Cell ss:Index="131"><Data ss:Type="String">Windows 2003</Data></Cell>
<Cell><Data >Microsoft SQL Server 2005</Data></Cell>
</Row>
..more rows (note the excel sheet has 132 columns)
I need to convert this to a standard text file, something like (with the right column) separator :
Col1 Col2 Col3 ..To.. Col15 Col16 ..To.. Col131
ToE.3 Maintain 3 Windows 2003
The problem is how to insert the empty row values that are skipt with the Index attribute.
The transformation without the empty, index handling looks like :
<xsl:for-each select="Row">
<xsl:for-each select="Cell/Data">
<xsl:value-of select="current()"/>
<xsl:text>\</xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
Some help would be warmly appreciated
step1: you need to declare output format, ie, "text" and not "xml"..
step2: you need to get rid of additional whitespace. use Strip-space with element='*', that means 'all'!
step3: you need to write header row first ie, col1, col2 etc..
so using template match select an element row that is first in your XML.. assuming that all the rows have same number of columns, you need to write "COL+ NUMBER" .. column numbers = no of cells you have in first row.
step4: if the cell is last then insert 'enter character'..
step5: call the generic function
step6: explaining generic function:
this function copies data under each cells separated by \. Only for the first row, we would be calling it manually, otherwise template match will take care of it.
Here is the code:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template name="Header" match="Row[not(preceding-sibling::Row)]">
<xsl:for-each select="Cell">
<xsl:value-of select="'Col'"/>
<xsl:value-of select="position()"/>
<xsl:if test="position()!=last()">
<xsl:value-of select="'\'"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:call-template name="CopyData"/>
</xsl:template>
<xsl:template name="CopyData" match="Row">
<xsl:for-each select="Cell">
<xsl:for-each select="Data">
<xsl:apply-templates select="."/>
</xsl:for-each>
<xsl:if test="position()!=last()">
<xsl:value-of select="'\'"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
corresponding sample output:
Col1\Col2\Col3\Col4\Col5\Col6
ToE.3\Maintain\3\Other\Windows 2003\Microsoft SQL Server 2005
ToE.3\Maintain\3\Other\Windows 2003\Microsoft SQL Server 2005
This is tricky because as you are seeing Excel skips columns in which no data appears, then provides an ss:Index attribute for the subsequent non-blank column. You have to reconstruct the "missing" cell positions on your own. That is, if you wish to retain the original column position like "15" or "131" in your example, with intervening blanks.
Agreeing with InfantProgrammer above, but suggest you'd add some logic to the "CopyData" template above to (a) determine the number of missing cells, then (b) call a recursive named template to write 'em to output.
<xsl:template name="WriteBlanks">
<xsl:param name="Count" select="0"/>
<xsl:if test="Count > 0">
<xsl:value-of select="'\'"/>
<xsl:call-template name="WriteBlanks">
<xsl:with-param name="Count" select="$Count - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
You could do something similar to generate the first row of column headers.
Given the simplicity of your need to just write backslash characters as column separator, a more succinct approach of just creating a long string of them, then lopping off however many are needed with XPath substring() could be in reach. However a recursive template may be suitable for more complex outputs.
Related
I'm trying to solve a problem, where I have to translate strings using xslt.
I saw this: XSLT key() lookup
and this: XSLT Conditional Lookup Table
but I'm not able to get it to work. I've tried to come up with the minimal example below which shows the problems that I'm facing.
The "real" xsl is assembled from code snippets using a build process. This involves some constraints.
The inner structure of the translation lookup tables always is the same, since they are downloaded from a translation tool in flat xml format http://docs.translatehouse.org/projects/translate-toolkit/en/latest/formats/flatxml.html. I can only wrap them into distinct parent nodes which is what i tried using the "lu" namespace.
The translation tables for all languages have to be stored inside the xsl, because different generations of xsl with different translations may exist next to each other. So no "sidecar" files.
Until now I can't get the key to work. The output of xsltproc is the following:
Setup Key - Start
German
xsltApplyOneTemplate: key was not compiled
Setup Key - End
de # skipped #
de # failed #
Expected output:
Setup Key - Start
German
Setup Key - End
de # skipped # Übersprungen
de # failed # Fehlgeschlagen
The XML file just needs to contain a root element.
So obviously the way I try to define the key depending on the target language is wrong, but my xsl knowledge has reached its limit now. The language stays the same during the transformation, so the key for all translation lookups has to be set up only once at the beginning.
The xsl Transformation:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:lu="http://www.my.domain.de/lookup"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="vLanguageCode">
<!-- <xsl:value-of select="/root/#language"/> -->
<xsl:value-of select="'de'"/>
</xsl:variable>
<xsl:template match="/">
<xsl:call-template name="setupKey"/>
<xsl:call-template name="getLabel">
<xsl:with-param name="pKey" select="'skipped'"/>
</xsl:call-template>
<xsl:call-template name="getLabel">
<xsl:with-param name="pKey" select="'failed'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="setupKey">
<xsl:message>Setup Key - Start</xsl:message>
<xsl:choose>
<xsl:when test="$vLanguageCode='DE' or $vLanguageCode='de'">
<xsl:message>German</xsl:message>
<xsl:key name="kLanguageDict" match="/lu:de/root/str" use="#key"/>
</xsl:when>
<xsl:otherwise>
<xsl:message>English (default)</xsl:message>
<xsl:key name="kLanguageDict" match="/lu:en/root/str" use="#key"/>
</xsl:otherwise>
</xsl:choose>
<xsl:message>Setup Key - End</xsl:message>
</xsl:template>
<xsl:template name="getLabel">
<xsl:param name="pKey"/>
<xsl:variable name="vResult">
<xsl:value-of select="key('kLanguageDict', $pKey)/#str"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$vResult!=''">
<xsl:value-of select="$vResult"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pKey"/>
</xsl:otherwise>
</xsl:choose>
<xsl:message>
<xsl:value-of select="concat($vLanguageCode, ' # ', $pKey, ' # ', $vResult)"/>
</xsl:message>
</xsl:template>
<lu:de>
<root>
<str key="skipped">Übersprungen</str>
<str key="failed">Fehlgeschlagen</str>
</root>
</lu:de>
<lu:en>
<root>
<str key="skipped">Skipped</str>
<str key="failed">Failed</str>
</root>
</lu:en>
</xsl:stylesheet>
Additions in response to the answer from #michael.hor257k:
Thank you. I didn't know that. So this means that I can't selectively define a key depending on language?
The translation system originally has one key at the top level and a translation table with interleaved entries for each language. It uses a double index (language+id) to look up the values.
I am trying to find a solution where I can embed the xml files returned by the translation management system (weblate) directly into the xsl without having to modify them. Unfortunately it looks like I'm limited in what I can get back (only default nodes and attributes).
This is the core of the original working translation lookup code:
<xsl:variable name="vLanguageDict" select="document('')/*/lu:strings"/>
<xsl:key name="kLanguageDict" match="lu:string" use="concat(#lang,#id)"/>
<xsl:template name="getLabel">
<xsl:param name="pKey"/>
<xsl:variable name="vResult">
<xsl:for-each select="$vLanguageDict">
<xsl:value-of select="key('kLanguageDict', concat($vLanguageCode,$pKey))/#value" />
</xsl:for-each>
</xsl:variable>
<xsl:choose>
<xsl:when test="$vResult!=''">
<xsl:value-of select="$vResult"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pKey"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<lu:strings>
<lu:string lang="DE" id="skipped" value="Übersprungen"/>
<lu:string lang="EN" id="skipped" value="skipped"/>
<lu:string lang="DE" id="failed" value="Fehlgeschlagen"/>
<lu:string lang="EN" id="failed" value="failed"/>
</lu:strings>
There are two mistakes in your XSLT stylesheet that immediately jump out:
The xsl:key element is allowed only at the top level, as a child
of the xsl:stylesheet element.
In XSLT 1.0, keys operate only on the current document. If you want to lookup from the stylesheet itself, you must change the context to the stylesheet document before calling the key() function. Here are two examples: https://stackoverflow.com/a/32440143/3016153
https://stackoverflow.com/a/30188334/3016153
I am afraid that's about all that can be said without a reproducible example.
--- added ---
So this means that I can't selectively define a key depending on language?
You cannot define a key conditionally - but you can define more than one key and select the one to use based on the specified language. Here's a simplified example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dict="http://example.com/dict">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:key name="de" match="dict:de/entry" use="#key" />
<xsl:key name="en" match="dict:en/entry" use="#key" />
<xsl:param name="input">skipped</xsl:param>
<xsl:param name="lang">de</xsl:param>
<xsl:template match="/">
<xsl:value-of select="$lang"/>
<xsl:text> # </xsl:text>
<xsl:value-of select="$input"/>
<xsl:text> = </xsl:text>
<!-- switch context to stylesheet in order to use key -->
<xsl:for-each select="document('')">
<xsl:value-of select="key($lang, $input)"/>
</xsl:for-each>
</xsl:template>
<dict:de>
<entry key="skipped">Übersprungen</entry>
<entry key="failed">Fehlgeschlagen</entry>
</dict:de>
<dict:en>
<entry key="skipped">Skipped</entry>
<entry key="failed">Failed</entry>
</dict:en>
</xsl:stylesheet>
Applied to any XML input, this will return:
Result
de : skipped = Übersprungen
I have an XML file that contains a sequence of strings
<ModsConfigData>
<buildNumber>1393</buildNumber>
<activeMods>
<li>Core</li>
<li>ZhentarFix</li>
<li>WorldPawnGC</li>
<li>HugsLib</li>
<!-- more entries -->
</activeMods>
</ModsConfigData>
And I wish to partition the long list of elements into subsets of a given length -- in my case, lengths of five. The purpose is to then work with the subsets to generate a table structure that is five across and however many rows it takes to consume the full list.
The output I'm wanting to achieve is a subset of BBCode like so:
[table]
[tr]
[td]Core[/td][td]ZhentarFix[/td][td]WorldPawnGC[/td] ...
[/tr]
[tr]
[td] ... (next set of five entries) [/td]
[/tr]
[/table]
It looks like a set of queries to me. There are probably ways to do a for-each loop, and position() mod 5 or position div 5 might be a way to get there. I'm not familiar enough with the grouping instruction to even guess if can be of assistance.
This is what I'm currently using, any improvements welcome.
<xsl:variable name="modsConfigFile" select="document('ModsConfig.xml')"/>
<xsl:variable name="groupSize" select="5"/>
<xsl:template match="/ModsConfigData/activeMods">
<xsl:text>[hr][table]</xsl:text>
<xsl:value-of>
<xsl:apply-templates
select="li[position() mod $groupSize = 1]"/>
</xsl:value-of>
<xsl:text>[/table]</xsl:text>
</xsl:template>
<xsl:template match="li">
<xsl:text>[tr]</xsl:text>
<xsl:apply-templates select=
".|following-sibling::li[position() < $groupSize]"/>
<xsl:text>[/tr]</xsl:text>
<br/>
</xsl:template>
<xsl:template match="li" >
<xsl:text>[td]</xsl:text>
<xsl:value-of select="."/>
<xsl:text>[/td]</xsl:text>
</xsl:template>
</xsl:stylesheet>
This is what I'm currently using,
You can't be "using" that, because it doesn't work. It doesn't work because (among other things):
you cannot place xsl:apply-templates within xsl-value-of; and
you have two templates matching li, with no distinction between them.
To do it the way you have started, you need to do:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:variable name="groupSize" select="5"/>
<xsl:template match="/ModsConfigData">
<xsl:text>[table]
</xsl:text>
<xsl:apply-templates select="activeMods/li[position() mod $groupSize = 1]"/>
<xsl:text>[/table]</xsl:text>
</xsl:template>
<xsl:template match="li">
<xsl:text> [tr]
</xsl:text>
<xsl:apply-templates select= ". | following-sibling::li[position() < $groupSize]" mode="cell"/>
<xsl:text>
[/tr]
</xsl:text>
</xsl:template>
<xsl:template match="li" mode="cell">
<xsl:text>[td]</xsl:text>
<xsl:value-of select="."/>
<xsl:text>[/td]</xsl:text>
</xsl:template>
</xsl:stylesheet>
When the above is applied to the following test input:
XML
<ModsConfigData>
<buildNumber>1393</buildNumber>
<activeMods>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
<li>E</li>
<li>F</li>
<li>G</li>
<li>H</li>
<li>I</li>
<li>J</li>
<li>K</li>
<li>L</li>
</activeMods>
</ModsConfigData>
the result will be:
[table]
[tr]
[td]A[/td][td]B[/td][td]C[/td][td]D[/td][td]E[/td]
[/tr]
[tr]
[td]F[/td][td]G[/td][td]H[/td][td]I[/td][td]J[/td]
[/tr]
[tr]
[td]K[/td][td]L[/td]
[/tr]
[/table]
I'm new in xslt, so I've some problems with adding Lp to my transformation.
This's my simple xml data:
<booking>
<bookingID>ww1</bookingID>
<voucherNumber>R-108</voucherNumber>
</booking>
<booking>
<bookingID>ww2</bookingID>
<voucherNumber>R-108</voucherNumber>
</booking>
<booking>
<bookingID>ww3</bookingID>
<voucherNumber>R-108</voucherNumber>
</booking>
<booking>
<bookingID>ww4</bookingID>
<voucherNumber>R-109</voucherNumber>
</booking>
<booking>
<bookingID>ww5</bookingID>
<voucherNumber>R-109</voucherNumber>
</booking>
<booking>
<bookingID>ww6</bookingID>
<voucherNumber>R-110</voucherNumber>
</booking>
The key is voucherNumber, i need to add Lp for the same voucherNumber
I'need output text file to look like this:
ID;VN,LP
ww1;108;1
ww2;108;2
ww3;108;3
ww4;109;1
ww5;109;2
ww6;110;1
I add the key on voucherNumber
<xsl:key name="x" match="booking" use="voucherNumber"/>
in for-each statement I've add this code: it's adding me on the last position (i know that i can change this for another position) the number of count my items for the same voucherNumber, but how i can add number Lp for the other items?
<xsl:choose>
<xsl:when test="generate-id(.) =generate-id(key('x',voucherNumber)[last()])">
<xsl:value-of select="count(key('x',voucherNumber)) "/>
</xsl:when>
<xsl:otherwise>
-- need LP for other items --
</xsl:otherwise>
</xsl:choose>
I can use only version 1.0 of xslt stylesheet.
Thank you for your advice
Best regards
It looks like you are trying to use Muenchian Grouping here, but what you probably should do is start off by selected the booking elements with the first occurrence of each distinct voucherNumber
<xsl:for-each select="booking[generate-id() = generate-id(key('x',voucherNumber)[1])]">
Then, you have a nested xsl:for-each where you get all the booking elements within that group (i.e. the booking elements with the same voucherNumber)
<xsl:for-each select="key('x', voucherNumber)">
Then, within this next xsl:for-each you can use the position() function to get the count of the record within that specific group
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" />
<xsl:key name="x" match="booking" use="voucherNumber"/>
<xsl:template match="/*">
<xsl:for-each select="booking[generate-id() =generate-id(key('x',voucherNumber)[1])]">
<xsl:for-each select="key('x', voucherNumber)">
<xsl:value-of select="bookingID" />
<xsl:text>,</xsl:text>
<xsl:value-of select="substring-after(voucherNumber, '-')" />
<xsl:text>,</xsl:text>
<xsl:value-of select="position()" />
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note, this assumed your actual XML is well-formed and there is a single root element containing all your booking elements.
I have no idea what "Lp" means. Assuming you want to number the bookings sequentially, restarting on voucherNumber, try something like:
-- Edit --
The proper solution here would be to use <xsl:number> to number the nodes. However, since I could not find a single combination of attributes that would work the same way with all XSLT 1.0 processors, I have resorted to the following hack:
<xsl:key name="booking-by-voucherNumber" match="booking" use="voucherNumber"/>
<xsl:template match="/root">
<xsl:for-each select="booking">
<!-- get id and voucher number -->
<xsl:variable name="id" select="generate-id()" />
<xsl:for-each select="key('booking-by-voucherNumber', voucherNumber)">
<xsl:if test="generate-id()=$id">
<xsl:value-of select="position()"/>
</xsl:if>
</xsl:for-each>
<!-- new line -->
</xsl:for-each>
</xsl:template>
First post here after looking at tons of awesome suggestions from the community.
I have three fields in XSLT 2.0, all at the same level (shoulders, knees, and toes). I am needing to output sums of toes based on unique combinations of shoulders and knees, so I have created two nested for-each-groups. On each output, I'm also needing to output an incrementer from from 1 to number of unique combinations of shoulders and knees.
This incrementer is where I'm having issues. The closest I've come is by calling position(), but if I call it in the innermost group, the counter resets at each unique shoulder. If I call it in the outermost group, every knee inside of a unique shoulder gets the same value, then it resets at each unique shoulder. If I call it outside of the groups completely, it never gets past 1. I've also tried to use xsl:number , keys, etc., to no avail. In those cases, the correct number of rows are still being printed, but the incrementer values are looking at the individual, non-grouped values.
I read one suggestion about "tunneling" values between templates, but I haven't been able to get that to work, mostly because I don't think I'm invoking the templates correctly (with these fields being same-level and not parent-child). Any thoughts on making this work with for-each-group or otherwise? Many thanks in advance.
Sample XML:
<bodies>
<parts>
<shoulders>shoulders1</shoulders>
<knees>knees1</knees>
<toes>1</toes>
</parts>
<parts>
<shoulders>shoulders2</shoulders>
<knees>knees2</knees>
<toes>2</toes>
</parts>
<parts>
<shoulders>shoulders1</shoulders>
<knees>knees2</knees>
<toes>10</toes>
</parts>
<parts>
<shoulders>shoulders2</shoulders>
<knees>knees1</knees>
<toes>10</toes>
</parts>
<parts>
<shoulders>shoulders1</shoulders>
<knees>knees1</knees>
<toes>9</toes>
</parts>
<parts>
<shoulders>shoulders2</shoulders>
<knees>knees2</knees>
<toes>8</toes>
</parts>
</bodies>
Sample XSLT:
<xsl:stylesheet exclude-result-prefixes="xsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:this="urn:this-stylesheet" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:template match="/">
<xsl:for-each-group select="bodies/parts" group-by="shoulders">
<xsl:for-each-group select="current-group()" group-by="knees">
<xsl:value-of select="shoulders"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="knees"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="sum(current-group()/toes)"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="position()"/>
<xsl:text>. </xsl:text>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
Resulting Output:
shoulders1, knees1, 10, 1. shoulders1, knees2, 10, 2. shoulders2, knees2, 10, 1. shoulders2, knees1, 10, 2.
Desired Output:
shoulders1, knees1, 10, 1. shoulders1, knees2, 10, 2. shoulders2, knees2, 10, 3. shoulders2, knees1, 10, 4.
When you've got two nested xsl:for-each-group instructions like this, then an alternative is to do single level grouping on a composite key, like this:
<xsl:for-each-group select="bodies/parts" group-by="concat(shoulders, '~', knees)">
The position() will then increment the way you are looking for, if I've understood the requirement correctly.
This doesn't work, of course, if you actually want to produce hierarchically structured output in which the outer group is significant.
I've tried this three times now and I think you have to process your results and then count them. I've run out of time to try and figure a way to do the counting inline, because I think it can't be done.
<xsl:stylesheet exclude-result-prefixes="xsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:this="urn:this-stylesheet" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:template match="/">
<xsl:variable name="results" as="xsd:string*">
<xsl:for-each-group select="bodies/parts" group-by="shoulders">
<xsl:for-each-group select="current-group()" group-by="knees">
<xsl:value-of>
<xsl:value-of select="shoulders"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="knees"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="sum(current-group()/toes)"/>
<xsl:text>,</xsl:text>
</xsl:value-of>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:variable>
<xsl:for-each select="$results">
<xsl:value-of select=".,position()"/>
<xsl:text>. </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
xml file like below I want concat value of name, add1, city, add2 with separated by comma
<Details>
<name>abc</name>
<profile>
<address>
<add1>ccc</add1>
<add2>bbb</add2>
<city>CA</city>
</address>
</profile>
</Details>
I want Output like below:-
abc, ccc, CA, bbb
(I mean city will come first before add2 and if any value is blank then it will adjust accordingly)
If you are looking to output all the text nodes within the Details element, you simply iterate over them all using xsl:for-each and use the position() function to output a comma if the node is not the first one
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Details">
<xsl:for-each select="//text()">
<xsl:if test="position() > 1">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:value-of select="." />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
So, if one of your elements has no text in it, it will not get output or have an extra comma.
<xsl:variable name="name">
<xsl:value-of select="Details/name"/>
</xsl:variable>
<xsl:variable name="add1">
<xsl:value-of select="Details/profile/address/add1"/>
</xsl:variable>
<xsl:variable name="add2">
<xsl:value-of select="Details/profile/address/add2"/>
</xsl:variable>
<xsl:variable name="city">
<xsl:value-of select="Details/profile/address/city"/>
</xsl:variable>
<xsl:value-of select="concat($name,',',$add1,',',$city,',',$add2)"/><br>
It will display the O/P like this abc, ccc, CA, bbb if add1 returns null then it will display like this abc, , CA, bbb
If you're using XSLT 2.0 you can use the () operator to construct a sequence in the order you want and then use the separator attribute on xsl:value-of to output the whole sequence with commas:
<xsl:template match="Details">
<xsl:value-of select="(name, profile/address/add1, profile/address/city,
profile/address/add2)" separator=", " />
</xsl:template>
If you want to filter out elements with an empty value (e.g. if the document contains <city/>) then you can do that with a predicate on the select expression:
(name, profile/address/add1, profile/address/city,
profile/address/add2)[normalize-space()]
The predicate removes from the sequence any nodes whose value is empty or consists entirely of whitespace.