Converting XML to CSV using XSLT Formatting Issue - csv

I have the following XML
<Data>
<Employee>
<Name>
<FirstName>John</FirstName>
<LastName>Snow</LastName>
</Name>
<DOB>1990-01-01</DOB>
<Passport>
<Country>United Kingdom</Country>
<ID>12345678</ID>
</Passport>
<Passport>
<Country>United States of America</Country>
<ID>789101112</ID>
</Passport>
</Employee>
</Data>
And I am converting it as seen below:
<xsl:for-each select="Data/Employee">
<xsl:value-of select="Name/FirstName"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="Name/LastName"/>
<xsl:text>,</xsl:text>
<xsl:for-each select="Passport">
<xsl:value-of select="Country"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="ID"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:text>,</xsl:text>
<xsl:value-of select="DOB"/>
</xsl:for-each>
The output I am getting is:
John,Snow,United Kingdom,12345678
United States of America,789101112
,1990-01-01
But I require the passport information to be populated below each other and then DOB to be shown on the top line as seen below:
John,Snow,United Kingdom,12345678,1990-01-01
,,United States of America,789101112
Any help would be appreciated

Perhaps like this:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/Data">
<xsl:for-each select="Employee">
<xsl:for-each select="Passport">
<xsl:choose>
<xsl:when test="position()=1">
<xsl:value-of select="../Name/(FirstName, LastName), Country,ID, ../DOB" separator=","/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'', '', Country,ID, ''" separator=","/>
</xsl:otherwise>
</xsl:choose>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note that I have added a trailing comma in the place of the "missing" DOB.

Related

XSLT: convert XML to JSON creating a group of sub nodes

it's been days trying to perform a transformation to get a JSON file from XML documents. My xml doc have different levels of sub nodes, all examples I found on internet don't catch my case.
Here is my xml example:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<foo id="1" group="B" />
<foo id="2" group="A" />
<foo id="3", group="A">
<foo id="4" group="A" />
<foo id="5" group="A">
<foo id="6" group="A" />
<foo id="7" group="A" />
<foo id="8" group="A" />
</foo>
</foo>
<foo id="9" group="A"></foo>
</root>
desired JSON:
{
"B": {
"id": 1
},
"A": {
"id": 2
},
"A": [{
"id": 4
},
{
"A": [{
"id": 6
},
{
"id": 7
},
{
"id": 8
}
]
}
],
"A": {
"id": 9
}
}
Means whenever I have nested <foo> elements, child elements are grouped with the parent element, and so on.
I tried some xsl code (see1 and see2) and failed to make them work for my case.
<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:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="root">
<xsl:text>{</xsl:text>
<xsl:apply-templates select="//foo"/>
<xsl:text>}</xsl:text>
</xsl:template>
<xsl:template match="//foo">
<xsl:choose>
<xsl:when test=" count(ancestor::foo) = 1 and child::foo"/>
<xsl:when test="foo[child::foo]"/>
<xsl:otherwise>
<xsl:text>"</xsl:text><xsl:value-of select="#group"/><xsl:text>"</xsl:text>:<xsl:text> {
</xsl:text>
<xsl:text> "</xsl:text><xsl:value-of select="#id/name()"/><xsl:text>"</xsl:text>:<xsl:value-of select=" concat(' ',#id)"/>
<xsl:text>
},</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Please Check this code:
<?xml version="1.0" encoding="UTF-8"?>
<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="xml" omit-xml-declaration="yes"/>
<xsl:template match="root">
<xsl:text>{</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="foo">
<xsl:if test="not(child::foo)">
<xsl:text> "</xsl:text><xsl:value-of select="#group"/><xsl:text>": </xsl:text><xsl:text>{</xsl:text><xsl:text>
</xsl:text>
<xsl:text> "id": </xsl:text><xsl:value-of select="#id"/><xsl:text>
</xsl:text>
<xsl:text> }</xsl:text>
<xsl:if test="position()!=last()">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:for-each select="foo">
<xsl:if test="not(child::foo)">
<xsl:text> "</xsl:text><xsl:value-of select="#group"/><xsl:text>": </xsl:text><xsl:if test="parent::foo"><xsl:text>[</xsl:text></xsl:if><xsl:text>{</xsl:text><xsl:text>
</xsl:text>
<xsl:text> "id": </xsl:text><xsl:value-of select="#id"/><xsl:text>
</xsl:text>
<xsl:text> },</xsl:text><xsl:text>
</xsl:text>
<xsl:text> {</xsl:text><xsl:text>
</xsl:text>
<xsl:text> "</xsl:text><xsl:value-of select="#group"/><xsl:text>": </xsl:text><xsl:text>[</xsl:text><xsl:text>
</xsl:text>
</xsl:if>
<!-- <xsl:text> "</xsl:text><xsl:value-of select="#group"/><xsl:text>": </xsl:text><xsl:text>[{</xsl:text><xsl:text>
</xsl:text>-->
<xsl:for-each select="foo">
<xsl:text> {</xsl:text><xsl:text>
</xsl:text>
<xsl:text> "id": </xsl:text><xsl:value-of select="#id"/><xsl:text>
</xsl:text>
<xsl:text> }</xsl:text>
<xsl:if test="position()!=last()">
<xsl:text>,</xsl:text>
</xsl:if><xsl:text>
</xsl:text>
<xsl:if test="position()=last()">
<xsl:text> ]</xsl:text><xsl:text>
</xsl:text>
</xsl:if>
<xsl:if test="position()=last()">
<xsl:text> }</xsl:text><xsl:text>
</xsl:text>
</xsl:if>
<xsl:if test="position()=last()">
<xsl:text>],</xsl:text><xsl:text>
</xsl:text>
</xsl:if>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
<xsl:text>}</xsl:text>
</xsl:template>
</xsl:stylesheet>
It is not quite clear which result you want as the desired "JSON" has duplicated properties if there are various foo group="A" elements but in general of XML to JSON generation you can use XSLT 3 with support for XPath 3.1 maps and arrays (https://www.w3.org/TR/xpath-31/#id-maps-and-arrays) and the json output method or the xml-to-json function (https://www.w3.org/TR/xslt-30/#json, https://www.w3.org/TR/xslt-30/#func-xml-to-json), that way you have two ways of creating JSON and outputting it, you can either transform your XML directly to XPath 3.1 maps/arrays and serialize them with <xsl:output method="json"/> or you can use the usual XSLT processing to transform your input XML to the JSON XML representation the xml-to-json function expects and apply it to get the JSON as text.
Here is an example of the first approach:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-skip"/>
<xsl:strip-space elements="*"/>
<xsl:output method="json" indent="yes"/>
<xsl:template match="*[*]">
<xsl:map>
<xsl:for-each-group select="foo" group-by="#group">
<xsl:map-entry key="string(current-grouping-key())">
<xsl:choose>
<xsl:when test="not(tail(current-group()))">
<xsl:apply-templates select="current-group()"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="group-transform" as="map(*)*">
<xsl:apply-templates select="current-group()"/>
</xsl:variable>
<xsl:sequence select="array { $group-transform }"/>
</xsl:otherwise>
</xsl:choose>
</xsl:map-entry>
</xsl:for-each-group>
</xsl:map>
</xsl:template>
<xsl:template match="*[not(*)]">
<xsl:sequence select="map { 'id' : data(#id) }"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6r5Gh3i
And here one of the second:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="#all"
xmlns="http://www.w3.org/2005/xpath-functions"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-skip"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<xsl:variable name="json-xml">
<xsl:apply-templates/>
</xsl:variable>
<xsl:template match="/">
<xsl:sequence select="xml-to-json($json-xml, map { 'indent' : true() })"/>
</xsl:template>
<xsl:template match="*[*]">
<map>
<xsl:for-each-group select="foo" group-by="string(#group)">
<xsl:choose>
<xsl:when test="not(tail(current-group()))">
<map key="{current-grouping-key()}">
<string key="id">{#id}</string>
</map>
</xsl:when>
<xsl:otherwise>
<array key="{current-grouping-key()}">
<xsl:apply-templates select="current-group()"/>
</array>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</map>
</xsl:template>
<xsl:template match="*[not(*)]">
<map>
<string key="id">{#id}</string>
</map>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6r5Gh3i/5
XSLT 3 is available using Saxon 9.8 or 9.9 HE for the Java platform on Sourceforge and Maven https://mvnrepository.com/artifact/net.sf.saxon/Saxon-HE/9.9.1-2 and for the .NET platform on NuGet https://www.nuget.org/packages/Saxon-HE/.

XSLT output JSON instead of XML

I have a XSLT that works the way I want when it outputs XML, however I would like to change the XSLT it to output JSON instead. The problem appears to me with the inner most for-each loop, but I'm not sure. If there is way to make this more efficient I'm also interested in your suggestions.
Sample input XML
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xs="http://www.w3.org/2001/XMLSchema">
<Metric measType="1526727075"
measResult="0"
endTime="2016-08-25T04:30:00-07:00"
measObjLdn="LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7"
Element_Type="ENODEB"
Key1="LTHBC0126858"
TableName="HH_ENODEB"
ColumnName="H1526727075"
H1526727075="0"/>
<Metric measType="1526727076"
measResult="0"
endTime="2016-08-25T04:30:00-07:00"
measObjLdn="LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7"
Element_Type="ENODEB"
Key1="LTHBC0126858"
TableName="HH_ENODEB"
ColumnName="H1526727076"
H1526727076="0"/>
<Metric measType="1526727077"
measResult="0"
endTime="2016-08-25T04:30:00-07:00"
measObjLdn="LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7"
Element_Type="ENODEB"
Key1="LTHBC0126858"
TableName="HH_ENODEB"
ColumnName="H1526727077"
H1526727077="0"/>
</root>
This is the XSLT that outputs XML and works the way I expect
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<root>
<xsl:for-each-group select="Metric" group-by="#measObjLdn">
<xsl:sort select="current-grouping-key()"/>
<xsl:variable name="curr_key" select="current-grouping-key()"/>
<xsl:for-each-group select="current-group()" group-by="#TableName">
<xsl:sort select="current-grouping-key()"/>
<xsl:if test="current-grouping-key() != ''">
<Table TableName="{current-grouping-key()}">
<xsl:for-each select="current-group()">
<xsl:attribute name="Stamp">
<xsl:value-of select="#endTime"/>
</xsl:attribute>
<xsl:attribute name="measObjLdn">
<xsl:value-of select="$curr_key"/>
</xsl:attribute>
<xsl:attribute name="Element_Type">
<xsl:value-of select="#Element_Type"/>
</xsl:attribute>
<xsl:attribute name="Key1">
<xsl:value-of select="#Key1"/>
</xsl:attribute>
<xsl:for-each select="#*">
<xsl:if test="starts-with(name(), 'H')">
<xsl:attribute name="{name()}">
<xsl:value-of select="number(.)"/>
</xsl:attribute>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</Table>
</xsl:if>
</xsl:for-each-group>
</xsl:for-each-group>
</root>
</xsl:template>
</xsl:stylesheet>
This is my JSON output code, but not working as expected.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:text>{"root":{</xsl:text>
<xsl:for-each-group select="Metric" group-by="#TableName">
<xsl:sort select="current-grouping-key()"/>
<xsl:if test="current-grouping-key() != ''">
<xsl:text>"Table":[</xsl:text>
<xsl:text>{"TableName":"</xsl:text>
<xsl:value-of select="current-grouping-key()"/>
<!--<Table TableName="{current-grouping-key()}">-->
<xsl:text>",</xsl:text>
<xsl:for-each-group select="current-group()" group-by="#measObjLdn">
<xsl:sort select="current-grouping-key()"/>
<xsl:for-each select="current-group()">
<xsl:text>"Stamp":"</xsl:text>
<xsl:value-of select="#endTime"/>
<xsl:text>",</xsl:text>
<xsl:text>"measObjLdn":"</xsl:text>
<xsl:value-of select="current-grouping-key()"/>
<xsl:text>",</xsl:text>
<xsl:text>"Element_Type":"</xsl:text>
<xsl:value-of select="#Element_Type"/>
<xsl:text>",</xsl:text>
<xsl:text>"Key1":"</xsl:text>
<xsl:value-of select="#Key1"/>
<xsl:text>",</xsl:text>
<xsl:for-each select="#attribute()">
<xsl:if test="starts-with(name(), 'H')">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>":"</xsl:text>
<xsl:value-of select="number(.)"/>
<xsl:text>"</xsl:text>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each-group>
<!--</Table>-->
<xsl:text>}</xsl:text>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:if>
</xsl:for-each-group>
<xsl:text>}</xsl:text>
</xsl:template>
</xsl:stylesheet>
Good point, so the XML version groups all the attributes for the same table together; Given the sample above the XML output is as follows (some reformatting applied for clarity):
<root xmlns:xs="http://www.w3.org/2001/XMLSchema">
<Table
TableName="HH_ENODEB"
Stamp="2016-08-25T04:30:00-07:00"
measObjLdn="LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7"
Element_Type="ENODEB"
Key1="LTHBC0126858"
H1526727075="0"
H1526727076="0"
H1526727077="0"/>
</root>
JSON example output is not a JSON version of the XML version which I expected (some whitespace reformatting applied for clarity).
{
"root": {
"Table": [
{
"TableName":"HH_ENODEB",
"Stamp":"2016-08-25T04:30:00-07:00",
"measObjLdn":"LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7",
"Element_Type":"ENODEB",
"Key1":"LTHBC0126858",
"H1526727075":"0"
"Stamp":"2016-08-25T04:30:00-07:00",
"measObjLdn":"LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7",
"Element_Type":"ENODEB",
"Key1":"LTHBC0126858",
"H1526727076":"0"
"Stamp":"2016-08-25T04:30:00-07:00",
"measObjLdn":"LTHBC0126858/GTPU:Board Type=MPT, Cabinet No.=0, Subrack No.=1, Slot No.=7",
"Element_Type":"ENODEB",
"Key1":"LTHBC0126858",
"H1526727077":"0"
}}
(The above is not even well-formed JSON)
You didn't actually say what your expected JSON should look like, but based on your XML, I am assuming it should look like this
{
"root":
{
"Table":
[
{
"TableName":"HH_ENODEB",
"Stamp":"2016-08-25T04:30:00-07:00",
"measObjLdn":"HH_ENODEB",
"Element_Type":"ENODEB",
"Key1":"LTHBC0126858",
"H1526727075":"0",
"H1526727076":"0",
"H1526727077":"0"
}
]
}
}
The issue is that in the XSLT that produces the XML output, you have the creation of the xsl:attributes within the statement <xsl:for-each select="current-group()">, but that means you are repeatedly outputting the same attribute names for a single Table element. In this case, XSLT will just replace any existing attribute with the latest one created, so you don't notice what is going on.
When outputting JSON (or rather, when outputting text, which just happens to be in JSON format), you do end up with the repeated attributes as it is just text.
The solution is to move the creation of the main attributes outside the inner loop.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:text>{
"root":
{
</xsl:text>
<xsl:for-each-group select="Metric" group-by="#measObjLdn">
<xsl:sort select="current-grouping-key()"/>
<xsl:variable name="curr_key" select="current-grouping-key()"/>
<xsl:text> "Table":
[
</xsl:text>
<xsl:for-each-group select="current-group()" group-by="#TableName">
<xsl:sort select="current-grouping-key()"/>
<xsl:if test="current-grouping-key() != ''">
<xsl:text> {
"TableName":"</xsl:text>
<xsl:value-of select="current-grouping-key()"/>
<xsl:text>",
</xsl:text>
<xsl:text> "Stamp":"</xsl:text>
<xsl:value-of select="#endTime"/>
<xsl:text>",
</xsl:text>
<xsl:text> "measObjLdn":"</xsl:text>
<xsl:value-of select="$curr_key"/>
<xsl:text>",
</xsl:text>
<xsl:text> "Element_Type":"</xsl:text>
<xsl:value-of select="#Element_Type"/>
<xsl:text>",
</xsl:text>
<xsl:text> "Key1":"</xsl:text>
<xsl:value-of select="#Key1"/>
<xsl:text>"</xsl:text>
<xsl:for-each select="current-group()">
<xsl:for-each select="#*[starts-with(name(), 'H')]">
<xsl:text>,
</xsl:text>
<xsl:text> "</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>":"</xsl:text>
<xsl:value-of select="number(.)"/>
<xsl:text>"</xsl:text>
</xsl:for-each>
</xsl:for-each>
<xsl:text>
}
</xsl:text>
</xsl:if>
</xsl:for-each-group>
<xsl:text> ]
</xsl:text>
</xsl:for-each-group>
<xsl:text> }
}</xsl:text>
</xsl:template>
</xsl:stylesheet>

xslt 1.0 - translate stirng backslash to double backslash

My xml:
<workorder>
<specifications>
<hpath>94 \ 72</hpath>
<classdesc></classdesc>
</specifications>
</workorder>
my xslt:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0">
<xsl:output method="text" encoding="UTF-8" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="workorder">
<xsl:apply-templates select="specifications" />
</xsl:template>
<xsl:template match="specifications">
<xsl:text>{
"Hierarchy Path"="</xsl:text>
<xsl:value-of select="translate(//workorder/specifications/hpath,'\','\\')" />
<xsl:text>"
}</xsl:text>
</xsl:template>
</xsl:stylesheet>
Expecting output is:
{
"Hierarchy Path"="94 \\ 72"
}
Current output is :
{
"Hierarchy Path"="94 \ 72"
}
Problem is: When I send json format as "Current output", not a valid json format.
If I send as "Expected output", it is a valid json format.
Please help. Thanks in advance.
With XSLT 1.0 you need a recursive approach.
You may try something like this named template.
<xsl:template name="jsonescape">
<xsl:param name="str" select="."/>
<xsl:choose>
<xsl:when test="contains($str, '\')">
<xsl:value-of select="concat(substring-before($str, '\'), '\\' )"/>
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="substring-after($str, '\')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And call it with your strings(for example).
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="//workorder/specifications/hpath"/>
</xsl:call-template>

Generic xml -> html conversion using xslt and convention?

I have been scouring the web for something like this and being a lazy programmer, before I attempt to do so myself, I thought I would ask here.
What I would like is a method to take any xml with node names following a convention (like cakephp or ruby) and then for that data to be presented in a ready to print format.
ie:
<xml>
<home_address>The Street</home_address>
</xml>
to:
<tr><td>Home Address</td><td>The Street</td></tr>
etc. with children having separate tables.
It seems pretty straightforward and something that would have been desired many times before. I found one useful discussion, but with no conclusion.
http://www.daniweb.com/software-development/xml-xslt-and-xpath/threads/363621/xml-to-html-using-xslt-without-hardcoding-node-names-in-xslt
Have I missed something here? Is there a simple generic xslt/css method for doing this? Or is this work being repeated hundreds of times a day in cubicles around the world?
Thanks in advance,
F
use the following XSL stylesheet. I have tested it and it works.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template match="/">
<xsl:for-each select="*">
<xsl:for-each select="*">
<tr>
<xsl:variable name="NodeNameClearText">
<xsl:call-template name="repalceNodeName">
<xsl:with-param name="value" select="local-name(.)"/>
</xsl:call-template>
</xsl:variable>
<td>
<xsl:value-of select="$NodeNameClearText" />
</td>
<td>
<xsl:value-of select="." />
</td>
</tr>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template name="repalceNodeName">
<xsl:param name="value"/>
<xsl:variable name="valueWithoutUnderscores">
<xsl:value-of select="translate($value, '_',' ')"/>
</xsl:variable>
<xsl:call-template name="caseLowerAcceptFirstWord">
<xsl:with-param name="data" select="$valueWithoutUnderscores"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="caseDown">
<xsl:param name="data"/>
<xsl:if test="$data">
<xsl:choose>
<xsl:when test="starts-with($data,' ')">
<xsl:text> </xsl:text>
<xsl:call-template name="caseLowerAcceptFirstWord">
<xsl:with-param name="data" select="normalize-space(substring($data,2))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate(substring($data,1,1),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')"/>
<!-- put all the chars you want to change
into the last two strings -->
<xsl:call-template name="caseDown">
<xsl:with-param name="data" select="substring($data,2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template name="caseUP">
<xsl:param name="data"/>
<xsl:if test="$data">
<xsl:choose>
<xsl:when test="starts-with($data,' ')">
<xsl:text> </xsl:text>
<xsl:call-template name="caseLowerAcceptFirstWord">
<xsl:with-param name="data" select="substring($data,2)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate(substring($data,1,1),
'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
<!-- put all the chars you want to change
into the last two strings -->
<xsl:call-template name="caseDown">
<xsl:with-param name="data" select="substring($data,2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template name="caseLowerAcceptFirstWord">
<xsl:param name="data"/>
<xsl:variable name="upperData">
<xsl:call-template name="caseUP">
<xsl:with-param name="data" select="$data"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="$upperData">
<xsl:value-of select="substring($upperData,1,1)"/>
<xsl:call-template name="caseDown">
<xsl:with-param name="data" select="substring($data,2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
A little more info on what I tested. The following XML:
<xml>
<home_address>The Street</home_address>
<po_box>474</po_box>
</xml>
Was output to
<tr><td>Home Address</td><td>The Street</td></tr>
<tr><td>Po Box</td><td>474</td></tr>
If thats not what you wanted your question is to vague

Formatting XSLT document to display multiple nicknames on a single line

My xml documents contain a list of people, and these people can have 0 or more nicknames. I am having trouble trying to display all the nicknames properly in my xslt document.
I can have all the nicknames listed by using:
<xsl:for-each select="name/nickname">
Nickname: <xsl:value-of select="." />
</xsl:for-each>
The output of this is something like:
Nickname: nickname1
Nickname: nickname2
Which is a problem as I would like to get an output without Nickname: being listed so many times, i.e.
Nickname: nickname1, nickname2.
What I currently have is:
<p>
Nickname:
<xsl:for-each select="name/nickname">
<xsl:value-of select="." />,
</xsl:for-each>
</p>
Problems with this are:
Nickname will always be printed at least once even if a nickname doesn't exist.
There will always be a left over comma (,).
I am hoping there are suggestions to get around these two issues, I tried to use != "" but I'm not sure if this is allowed if an person doesn't contain a nickname.
Thanks :)
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="name[nickname]">
<xsl:text>
Nicknames: </xsl:text>
<xsl:apply-templates select="nickname"/>
</xsl:template>
<xsl:template match="nickname">
<xsl:if test="not(position() = 1)">
<xsl:text>, </xsl:text>
</xsl:if>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>
<person>
<name trueName="John">
<nickname>X1</nickname>
<nickname>X2</nickname>
<nickname>X3</nickname>
</name>
</person>
<person>
<name trueName="Peter">
<nickname>Y1</nickname>
<nickname>Y2</nickname>
<nickname>Y3</nickname>
</name>
</person>
</t>
produces the wanted, correct result:
Nicknames: X1, X2, X3
Nicknames: Y1, Y2, Y3
Or,
<xsl:for-each select="name/nickname">
<xsl:if test="position() = 1">Nickname: </xsl:if>
<xsl:value-of select="." />
<xsl:if test="not(position()=last())">, </xsl:if>
</xsl:for-each>
Something like that (untested):
<xsl:when test="name/nickname">
Nickname:
<xsl:for-each select="name/nickname">
<xsl:value-of select="." />
<xsl:if test="count(following-sibling::nickname)">,</xsl:if>
</xsl:for-each>
</xsl:when>
Input :
<?xml version="1.0" encoding="UTF-8"?>
<test>
<nickname>1</nickname>
<nickname>2</nickname>
<nickname>3</nickname>
</test>
Transform :
<xsl:template match='/'>
<xsl:if test='count(//nickname) > 0'>
<result>
<xsl:for-each select='//nickname'>
<xsl:choose>
<xsl:when test='position() = 1'>
Nickname : <xsl:value-of select="."/><xsl:if test="not(position() = last())">,</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
<xsl:if test="not(position() = last())">,</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</result>
</xsl:if>
</xsl:template>
Output :
<?xml version="1.0" encoding="UTF-8"?>
<result>Nickname : 1,2,3</result>
Or in XSLT 2.0:
Input :
<?xml version="1.0" encoding="UTF-8"?>
<test>
<nickname>1</nickname>
<nickname>2</nickname>
<nickname>3</nickname>
</test>
Transform :
<xsl:template match='/'>
Nickname: <xsl:value-of select="/test/nickname" separator=", "/>
</xsl:template>