XSLT 1 how to group by 2 elements - xslt

I have some problems in grouping some xml using 2 elements. While this can be done easy using version 2.0 i am restricted to xslt 1.0 :(.
What i want to achieve is to get only the students with statisticsCode=1 and then group them by gradeId so in the end i have the attendance sum per each course name:
|---------------------|------------------|
| CourseName | Attendance |
|---------------------|------------------|
| Bio | 17 |
|---------------------|------------------|
| Math | 31 |
|---------------------|------------------|
Here is the xml that i am working with:
<Student>
<statisticsCode>-1</statisticsCode>
<attendance>15</attendance>
<groupid>1</groupid>
<statisticsCode>3</statisticsCode>
<Grade>
<gradeId>1</gradeId>
<uidObjectID>00010004-0000-0000-0000-000000000031</uidObjectID>
<CourseName>Science</CourseName>
</Grade>
</Student>
<Student>
<statisticsCode>-1</statisticsCode>
<attendance>31</attendance>
<groupid>1</groupid>
<statisticsCode>1</statisticsCode>
<Grade>
<gradeId>1</gradeId>
<uidObjectID>00010004-0000-0000-0000-000000000031</uidObjectID>
<CourseName>Math</CourseName>
</Grade>
</Student>
<Student>
<statisticsCode>-1</statisticsCode>
<attendance>14</attendance>
<groupid>1</groupid>
<statisticsCode>1</statisticsCode>
<Grade>
<gradeId>2</gradeId>
<uidObjectID>00010004-0000-0000-0000-000000000031</uidObjectID>
<CourseName>Bio</CourseName>
</Grade>
</Student>
<Student>
<statisticsCode>-1</statisticsCode>
<attendance>3</attendance>
<groupid>1</groupid>
<statisticsCode>1</statisticsCode>
<Grade>
<gradeId>2</gradeId>
<uidObjectID>00010004-0000-0000-0000-000000000031</uidObjectID>
<CourseName>Bio</CourseName>
</Grade>
</Student>

The following stylesheet should do the job.
It has two configurable parameters:
statisticsCode-match
gradeId-match
The values of theses are currently set to 1 so students will be filtered according to your request and in the current setup only attendances of gradeId 1 students will be summed.
Hope it helps ;-)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl" exclude-result-prefixes="xd" version="1.0">
<xd:doc scope="stylesheet">
<xd:desc>
<xd:p><xd:b>Created on:</xd:b> Apr 9, 2020</xd:p>
<xd:p><xd:b>Author:</xd:b> bwb</xd:p>
<xd:p/>
</xd:desc>
</xd:doc>
<xsl:output method="text"/>
<xsl:param name="statisticsCode-match">1</xsl:param>
<xsl:variable name="line-divider">
<xsl:text>|--------------------|--------------------|</xsl:text>
</xsl:variable>
<xsl:variable name="break">
<xsl:text>
</xsl:text>
</xsl:variable>
<xsl:variable name="students.filtered">
<xsl:for-each select="//Student[statisticsCode = $statisticsCode-match]">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="coursenames.distinct">
<xsl:for-each select="//Student[statisticsCode = $statisticsCode-match]//CourseName">
<xsl:choose>
<xsl:when test=". = preceding::CourseName"/>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="twenty-whitespaces">
<xsl:text> </xsl:text>
</xsl:variable>
<xsl:template match="/">
<xsl:value-of select="$break"/>
<xsl:text>statisticsCode: </xsl:text>
<xsl:value-of select="$statisticsCode-match"/>
<xsl:value-of select="$break"/>
<xsl:value-of select="$line-divider"/>
<xsl:value-of select="$break"/>
<xsl:text>| CourseName | Attendance |</xsl:text>
<xsl:value-of select="$break"/>
<xsl:value-of select="$line-divider"/>
<xsl:for-each select="//Student[statisticsCode = $statisticsCode-match]//CourseName">
<xsl:choose>
<xsl:when test=". = preceding::CourseName"/>
<xsl:otherwise>
<!-- course name -->
<xsl:variable name="padding.course" select="substring($twenty-whitespaces, 1, floor((20 - string-length(.)) div 2))"/>
<xsl:value-of select="$break"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="$padding.course"/>
<xsl:value-of select="."/>
<xsl:value-of select="$padding.course"/>
<xsl:if test="string-length(.) mod 2 = 1">
<xsl:text> </xsl:text>
</xsl:if>
<!-- end course name -->
<xsl:text>|</xsl:text>
<!-- attendance -->
<xsl:variable name="attendance.count" select="sum(//Student[statisticsCode = $statisticsCode-match][Grade[CourseName = current()]]/attendance)"/>
<xsl:variable name="padding.attendance" select="substring($twenty-whitespaces, 1, floor((20 - string-length($attendance.count)) div 2))"/>
<xsl:value-of select="$padding.attendance"/>
<xsl:value-of select="$attendance.count"/>
<xsl:value-of select="$padding.attendance"/>
<xsl:if test="string-length($attendance.count) mod 2 = 1">
<xsl:text> </xsl:text>
</xsl:if>
<!-- end attendance -->
<xsl:text>|</xsl:text>
<!-- table divider -->
<xsl:value-of select="$break"/>
<xsl:value-of select="$line-divider"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Related

xslt if not value, no row

I have this xml that needs to be transformed:
<?xml version="1.0" encoding="utf-8"?>
<racine>
<index>
<Parent nom="00000002" Name="" Address="" />
<Meter numSerie="00000002" />
<arrêté dateArrêté="28/02/2015 00:00:00">
<ValeurIndex Libelle="PMAXVALUE0">0.104</ValeurIndex>
</arrêté>
</index>
<index>
<Parent nom="00000002B" Name="" Address="" />
<Meter numSerie="" />
<arrêté dateArrêté="28/02/2015 00:00:00">
<ValeurIndex Libelle="R1INDEX0">3.754</ValeurIndex>
<ValeurIndex Libelle="PMAXVALUE0">1.047</ValeurIndex>
</arrêté>
</index>
</racine>
The xslt that make the transformation is:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="ISO-8859-1"/>
<!-- Parcours des noeuds "racine/index" -->
<xsl:variable name="v_separateur">
<xsl:text>,</xsl:text>
</xsl:variable>
<!-- sort by date desc" -->
<xsl:template match="/racine">
<xsl:apply-templates select="index">
<xsl:sort select="arrêté/#dateArrêté" order="descending" data-type="number"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="index">
<!-- get repeating values once -->
<xsl:variable name="constants">
<!-- SP_ID -->
<xsl:value-of select="Parent/#nom"/>
<xsl:value-of select="$v_separateur"/>
<!-- METER_NUMBER-->
<xsl:value-of select="Meter/#numSerie"/>
<xsl:value-of select="$v_separateur"/>
<!-- DATE & TIME-->
<xsl:value-of select="arrêté/#dateArrêté"/>
<xsl:value-of select="$v_separateur"/>
</xsl:variable>
<!-- Only Process OBIS* values ignore other -->
<xsl:apply-templates select="arrêté/ValeurIndex[
#Libelle='EA.R00'
or #Libelle='EA.R01'
or #Libelle='EA.R02'
or #Libelle='EA.R03'
or #Libelle='EA.R05'
or #Libelle='EA.R06'
or #Libelle='EA.R07'
or #Libelle='EAE.R00'
or #Libelle='AEINDEX0'
or #Libelle='AEINDEX1'
or #Libelle='AEINDEX2'
or #Libelle='AEINDEX3'
or #Libelle='AEINDEX5'
or #Libelle='AEINDEX6'
or #Libelle='AEINDEX7'
or #Libelle='R1INDEX0'
or #Libelle='PMAXVALUE0'
]">
<xsl:with-param name="constants" select="$constants"/>
</xsl:apply-templates>
<!-- ADD NEW ROW -->
<xsl:value-of select="$constants"/>
<xsl:text>KWH</xsl:text>
<xsl:value-of select="$v_separateur"/>
<xsl:value-of select="$v_separateur"/>
<xsl:choose>
<xsl:when test="arrêté/ValeurIndex[#Libelle='EA.R00'] > arrêté/ValeurIndex[#Libelle='EA.R02']">
<xsl:value-of select="arrêté/ValeurIndex[#Libelle='EA.R00']"/>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="arrêté/ValeurIndex[#Libelle='EA.R02']"/>
<xsl:text>
</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="ValeurIndex"> <!--match="ValeurIndex"-->
<xsl:param name="constants"/>
<xsl:value-of select="$constants"/>
<xsl:choose>
<xsl:when test="#Libelle = 'EA.R00'">KWH,PUNTA</xsl:when>
<xsl:when test="#Libelle = 'EA.R01'">KWH,VALLE</xsl:when>
<xsl:when test="#Libelle = 'EA.R02'">KWH,LLANO</xsl:when>
<xsl:when test="#Libelle = 'EA.R03'">KW,</xsl:when>
<xsl:when test="#Libelle = 'EA.R05'">KWH,</xsl:when>
<xsl:when test="#Libelle = 'EA.R06'">KVH,ER_Q2</xsl:when>
<xsl:when test="#Libelle = 'EA.R07'">KVH,ER_Q3</xsl:when>
<xsl:when test="#Libelle = 'EAE.R00'">KVH,ER_Q4</xsl:when>
<xsl:when test="#Libelle = 'AEINDEX0'">KWH,PUNTA_SA</xsl:when>
<xsl:when test="#Libelle = 'AEINDEX1'">KWH,VALLE_SA</xsl:when>
<xsl:when test="#Libelle = 'AEINDEX2'">KWH,LLANO_SA</xsl:when>
<xsl:when test="#Libelle = 'AEINDEX3'">KWH,</xsl:when>
<xsl:when test="#Libelle = 'AEINDEX5'">KWH,ENERSAL</xsl:when>
<xsl:when test="#Libelle = 'AEINDEX6'">KW,PUNTA</xsl:when>
<xsl:when test="#Libelle = 'AEINDEX7'">KW,VALLE</xsl:when>
<xsl:when test="#Libelle = 'R1INDEX0'">KW,LLANO</xsl:when>
<xsl:when test="#Libelle = 'PMAXVALUE0'">KW, PUNTA_SA</xsl:when>
<xsl:otherwise>
<xsl:value-of select="Param[#code = 'TYPE_EQP']/#value"/>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$v_separateur"/>
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
I want to have just the lines that we have some value, so in this case:
00000002,00000002,28/02/2015 00:00:00,KW, PUNTA_SA,0.104
00000002B,,28/02/2015 00:00:00,KW,LLANO,3.754
00000002B,,28/02/2015 00:00:00,KW, PUNTA_SA,1.047
But as we put the maximum of EA.R00 and EA.R02 I will have a row without date and without data. I want that in case that both values dont exist dont write anything. As the xslt is right now, I will obtain:
00000002,00000002,28/02/2015 00:00:00,KW, PUNTA_SA,0.104
00000002,00000002,28/02/2015 00:00:00,KWH,,
00000002B,,28/02/2015 00:00:00,KW,LLANO,3.754
00000002B,,28/02/2015 00:00:00,KW, PUNTA_SA,1.047
00000002B,,28/02/2015 00:00:00,KWH,,
(I dont want to see the 2nd and 5th row)
Thanks
The extra lines your refer to are output by the code after the comment <!-- ADD NEW ROW -->. What you can do is wrap this code in an xsl:if to check if either EA.R00 or EA.R02 exists.
Try this:
<!-- ADD NEW ROW -->
<xsl:if test="arrêté/ValeurIndex[#Libelle='EA.R00' or #Libelle='EA.R02']">
<xsl:value-of select="$constants"/>
<xsl:text>KWH</xsl:text>
<xsl:value-of select="$v_separateur"/>
<xsl:value-of select="$v_separateur"/>
<xsl:choose>
<xsl:when test="arrêté/ValeurIndex[#Libelle='EA.R00'] > arrêté/ValeurIndex[#Libelle='EA.R02']">
<xsl:value-of select="arrêté/ValeurIndex[#Libelle='EA.R00']"/>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="arrêté/ValeurIndex[#Libelle='EA.R02']"/>
<xsl:text>
</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:if>

Rolling sum of template result in XSL1.0

I've searched far and wide for an example of this, and have so far had no luck in applying a sum template to make my XSTL work.
This is the XML (number of lines varies on each planfeature)
<PlanFeatures>
<PlanFeature name="Line0001">
<CoordGeom>
<Line>
<Start pntRef="7540">5605 8950 1020</Start>
<End pntRef="7541">5605 8951 1019</End>
</Line>
<Line>
<Start pntRef="7541">5605 8951 1019</Start>
<End pntRef="7542">5605 8947 1019</End>
</Line>
<Line>
<Start pntRef="7542">5605 8947 1019</Start>
<End pntRef="7543">5605 8940 1011</End>
</Line>
<Line>
<Start pntRef="7543">5605 8940 1011</Start>
<End pntRef="7544">5605 8931 1020</End>
</Line>
</CoordGeom>
</PlanFeature>
</PlanFeatures>
This is where I'm at with the XSL, which uses a recursive call template to calculate the distance of each line segment.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:landxml="http://www.landxml.org/schema/LandXML-1.2" xmlns:hexagon="http://xml.hexagon.com/schema/HeXML-1.5" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" version="1.0" encoding="UTF-16" indent="no" omit-xml-declaration="yes"/>
<xsl:variable name="XML" select="/"/>
<xsl:variable name="fileExt" select="'txt'"/>
<xsl:variable name="fileDesc" select="'line distance report'"/>
<xsl:template match="/">
<xsl:for-each select="$XML">
<xsl:for-each select="landxml:LandXML/landxml:PlanFeatures/landxml:PlanFeature">
<xsl:value-of select="#name"/><xsl:text>::</xsl:text>
<xsl:for-each select="landxml:CoordGeom/landxml:Line">
<xsl:value-of select="landxml:Start/#pntRef"/><xsl:text>-</xsl:text>
<xsl:variable name="lista" select="landxml:Start"/>
<xsl:variable name="x1" select="substring-before($lista,' ')"/>
<xsl:variable name="yt1" select="substring-after($lista,' ')"/>
<xsl:variable name="y1" select="substring-before($yt1,' ')"/>
<xsl:variable name="z1" select="substring-after($yt1,' ')"/>
<xsl:variable name="listb" select="landxml:End"/>
<xsl:value-of select="landxml:End/#pntRef"/><xsl:text>: </xsl:text>
<xsl:variable name="x2" select="substring-before($listb,' ')"/>
<xsl:variable name="yt2" select="substring-after($listb,' ')"/>
<xsl:variable name="y2" select="substring-before($yt2,' ')"/>
<xsl:variable name="z2" select="substring-after($yt2,' ')"/>
<xsl:variable name="seg" select= "((($x2 - $x1)*($x2 - $x1))+(($y2 - $y1)*($y2 - $y1))+(($z2 - $z1)*($z2 - $z1)))"/>
<xsl:call-template name="root">
<xsl:with-param name="X" select="$seg"/>
</xsl:call-template>
<xsl:text>, </xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template name="root">
<xsl:param name="X"/>
<xsl:param name="xn" select="0"/>
<xsl:param name="xn_1" select="($X+1) div 2"/>
<xsl:choose>
<xsl:when test="string(number($X)) = 'NaN'">
<xsl:value-of select=" ' ' "/>
</xsl:when>
<xsl:when test="($xn_1 - $xn) * ($xn_1 - $xn) < 0.00000001">
<xsl:value-of select='format-number($xn_1, "#.000")'/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="root">
<xsl:with-param name="X" select="$X"/>
<xsl:with-param name="xn" select="$xn_1"/>
<xsl:with-param name="xn_1" select="($xn_1 + ($X div $xn_1)) div 2"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I need to sum the value of X (distance) from the root call template, to create a value which represents the sum of each line segment. I think I need to use a match template, but so far it hand even got close to working.
Currently exporting LINEID::StartPt-EndPt: dist, StartPt-EndPt: dist, etc. I need the sum of the 'dist' to be shown at the end of each line as well. As below
Line0001::7540-7541: 1.414, 7541-7542: 2.000, 7542-7543: 12.042, 7543-7544: 12.720
but I would like
Line0001::7540-7541: 1.414, 7541-7542: 2.000, 7542-7543: 12.042, 7543-7544: 12.728 -- 28.184
Any help would be appreciated... the examples on this site have helped me so much already, but I just can't seem to get through this roadblock.
Cheers,
Chris
You can accomplish this with some relatively simple recursion and parameter passing. Try replacing your first template with these four templates:
<xsl:template match="/">
<xsl:for-each select="$XML">
<xsl:apply-templates
select="landxml:LandXML/landxml:PlanFeatures/landxml:PlanFeature" />
</xsl:for-each>
</xsl:template>
<xsl:template match ="landxml:PlanFeature">
<xsl:value-of select="concat(#name, '::')" />
<xsl:apply-templates select="landxml:CoordGeom/landxml:Line[1]" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="landxml:Line">
<xsl:param name="total" select="0" />
<xsl:value-of
select="concat(landxml:Start/#pntRef, '-', landxml:End/#pntRef, ': ')"/>
<xsl:variable name="len">
<xsl:call-template name="root">
<xsl:with-param name="X">
<xsl:call-template name="seg" />
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$len"/>
<xsl:variable name="next" select="following-sibling::landxml:Line[1]" />
<xsl:variable name="newTot" select="$total + $len" />
<xsl:choose>
<xsl:when test="$next">
<xsl:text>, </xsl:text>
<xsl:apply-templates select="$next">
<xsl:with-param name="total" select="$newTot" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' -- ', format-number($newTot, '#.000'))" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="seg">
<xsl:variable name="lista" select="landxml:Start"/>
<xsl:variable name="x1" select="substring-before($lista,' ')"/>
<xsl:variable name="yt1" select="substring-after($lista,' ')"/>
<xsl:variable name="y1" select="substring-before($yt1,' ')"/>
<xsl:variable name="z1" select="substring-after($yt1,' ')"/>
<xsl:variable name="listb" select="landxml:End"/>
<xsl:variable name="x2" select="substring-before($listb,' ')"/>
<xsl:variable name="yt2" select="substring-after($listb,' ')"/>
<xsl:variable name="y2" select="substring-before($yt2,' ')"/>
<xsl:variable name="z2" select="substring-after($yt2,' ')"/>
<xsl:value-of select= "($x2 - $x1)*($x2 - $x1)+
($y2 - $y1)*($y2 - $y1)+
($z2 - $z1)*($z2 - $z1)"/>
</xsl:template>
When run on your sample input (after adjusting it to match the paths in your XSLT), the result is:
Line0001::7540-7541: 1.414, 7541-7542: 4.000, 7542-7543: 10.630, 7543-7544: 12.728 -- 28.772

Getting First Child in XSLT Transform

I have the following XSL, which I use to transform Oracle SQL developer's XML format to the "full" XML format expected by DBUnit (for creating data sets for testing).
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:java="java"
xmlns:dbutil="com.jason.util.DatabaseTestUtil">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<xsl:text disable-output-escaping="yes"><dataset></xsl:text>
<xsl:text>
</xsl:text>
<xsl:text disable-output-escaping="yes"><table name=""></xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="RESULTS/ROW">
<xsl:for-each select="COLUMN">
<xsl:text disable-output-escaping="yes"><column></xsl:text>
<xsl:value-of select="#NAME" />
<xsl:text disable-output-escaping="yes"></column></xsl:text>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="RESULTS">
<xsl:for-each select="ROW">
<xsl:text disable-output-escaping="yes"><row></xsl:text>
<xsl:for-each select="COLUMN">
<xsl:text disable-output-escaping="yes"><value descriptor="</xsl:text>
<xsl:value-of select="#NAME"/>
<xsl:text disable-output-escaping="yes">"></xsl:text>
<xsl:choose>
<xsl:when test="#NAME='NAME'">
<xsl:value-of select="dbutil:generateRandomName()" />
</xsl:when>
<xsl:when test="#NAME='MEMBER_SSN'">
<xsl:value-of select="dbutil:generateRandomSsn()" />
</xsl:when>
<xsl:when test="#NAME='SPOUSE_SSN'">
<xsl:variable name="spouseSsn">
<xsl:value-of select="." />
</xsl:variable>
<xsl:if test="dbutil:hasSpouseSsn($spouseSsn)"><xsl:value-of select="dbutil:generateRandomSsn()" /></xsl:if>
</xsl:when>
<xsl:when test="#NAME=E_MAIL_ADDRESS">
<xsl:text>noname#mail.com</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
<xsl:text disable-output-escaping="yes"></value></xsl:text>
<xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:text disable-output-escaping="yes"></row></xsl:text>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
<xsl:text disable-output-escaping="yes"></table></xsl:text>
<xsl:text>
</xsl:text>
<xsl:text disable-output-escaping="yes"></dataset></xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
I have a call to some "backend" java so that I don't have people's private information embedded in my test sets.
Unfortunately, the first loop through /RESULTS/ROW should only be a loop through the columns of the first child of /RESULT/ROW.
Can anyone tell me how to get that first child?
Unfortunately, the first loop through /RESULTS/ROW should only be a
loop through the columns of the first child of /RESULT/ROW.
Can anyone tell me how to get that first child?
Replace:
<xsl:for-each select="RESULTS/ROW">
<xsl:for-each select="COLUMN">
. . . . . . . . .
</xsl:for-each>
</xsl:for-each>
with:
<xsl:for-each select="RESULTS/ROW[1]/Column">
. . . . . . . . .
</xsl:for-each>

Removing the root element using xslt

I have an xml schema that has a structure like
<Level>
<Level1>...data...</Level1>
<Level2>...data...</Level2>
.
.
.
</Level>
I want to remove the <Level> tag. How am I supposed to do that, with the help of xslt.
The standard answer to any "how do I keep most of my XML the same but tweak some little bits of it" question is "use an identity template and then override it for the specific things you want to change"
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- omit the <?xml?> line in the output, it won't be well-formed anyway -->
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()" /></xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:apply-templates select="node()" />
</xsl:template>
</xsl:stylesheet>
but as Mr Lister points out in the comments, this will leave you with something that is not well formed XML, as it will have more than one document element.
When I apply this stylesheet on the input XML
<Level>
<Level1>...data...</Level1>
<Level2>...data...</Level2>
</Level>
it produces the result
<Level1>...data...</Level1>
<Level2>...data...</Level2>
If you want to store all child elements of the document element in a variable, you can do something like:
<xsl:variable name="myVar" select="/*/*"/>
If, however, you want to the stylesheet to produce a string, this might be a solution:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="*">
<!-- We write the opening tag -->
<xsl:value-of select="concat('<',local-name())"/>
<!-- Now, all attributes -->
<xsl:apply-templates select="#*"/>
<!-- Depending on whether we have an empty element or not,
we're adding the content or closing it immediately -->
<xsl:choose>
<xsl:when test="node()">
<!-- Close opening tag -->
<xsl:value-of select="'>'"/>
<!-- Add the content -->
<xsl:apply-templates select="node()"/>
<!-- Write closing tag -->
<xsl:value-of select="concat('</',local-name(),'>')"/>
</xsl:when>
<xsl:otherwise>
<!-- Create empty element by closing tag immediately -->
<xsl:value-of select="'/>'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*">
<!-- Write an attribute -->
<xsl:value-of select="concat(' ',local-name(),'="',.,'"')"/>
</xsl:template>
</xsl:stylesheet>
It produces text (and therefore you won't get non-well-formed XML). It's a little over-simplified because it does not handle namespaces, comments, processing instructions and quotes in attributes. If your input XML contains any of these, you'd have to refine the stylesheet.
I have created a new XSLT definition which fulfill my requremtn with the help of your code
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="text" omit-xml-declaration="yes"/>
<xsl:variable name="nl">
<xsl:text/>
</xsl:variable>
<xsl:variable name="tb">
<xsl:text/>
</xsl:variable>
<xsl:template match="/*">
<!-- Open the root array -->
<xsl:text>[{</xsl:text>
<xsl:value-of select="$nl"/>
<!-- Process all the child nodes of the root -->
<xsl:apply-templates mode="detect" select="*">
<xsl:with-param name="indent" select="$tb"/>
</xsl:apply-templates>
<!-- Close the root array -->
<xsl:value-of select="$nl"/>
<xsl:text>}]</xsl:text>
</xsl:template>
<xsl:template match="*" mode="detect">
<xsl:choose>
<xsl:when test="name(preceding-sibling::*[1]) = name(current()) and name(following-sibling::*[1]) != name(current())">
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:text>]</xsl:text>
<xsl:if test="count(following-sibling::*[name() != name(current())]) > 0">, </xsl:if>
</xsl:when>
<xsl:when test="name(preceding-sibling::*[1]) = name(current())">
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:if test="name(following-sibling::*) = name(current())">, </xsl:if>
</xsl:when>
<xsl:when test="following-sibling::*[1][name() = name(current())]">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>" : [</xsl:text>
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:text>, </xsl:text>
</xsl:when>
<xsl:when test="count(./child::*) > 0 or count(#*) > 0">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : [<xsl:apply-templates
mode="obj-content" select="."/>
<xsl:if test="count(following-sibling::*) > 0">], </xsl:if>
</xsl:when>
<xsl:when test="count(./child::*) = 0">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:apply-templates select="."/>
<xsl:text>"</xsl:text>
<xsl:if test="count(following-sibling::*) > 0">, </xsl:if>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="*" mode="obj-content">
<xsl:text>{</xsl:text>
<xsl:apply-templates mode="attr" select="#*"/>
<xsl:if test="count(#*) > 0 and (count(child::*) > 0 or text())">, </xsl:if>
<xsl:apply-templates mode="detect" select="./*"/>
<xsl:if test="count(child::*) = 0 and text() and not(#*)">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:value-of select="text()"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:if test="count(child::*) = 0 and text() and #*">
<xsl:text>: "</xsl:text>
<xsl:value-of select="text()"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:text>}</xsl:text>
<xsl:if test="position() < last()">, </xsl:if>
</xsl:template>
<xsl:template match="#*" mode="attr">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:value-of select="."/>
<xsl:text>"</xsl:text>
<xsl:if test="position() < last()">,</xsl:if>
</xsl:template>
<xsl:template match="node/#TEXT | text()" name="removeBreaks">
<xsl:param name="pText" select="normalize-space(.)"/>
<xsl:choose>
<xsl:when test="not(contains($pText, '
'))">
<xsl:copy-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(substring-before($pText, '
'), ' ')"/>
<xsl:call-template name="removeBreaks">
<xsl:with-param name="pText" select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

xsl choose in concat

I have the following data:
XML
<team>
<rectx>30</rectx>
<diadata>
<bestAnd>-350</bestAnd>
</diadata>
<diadata>
<bestAnd>-250</bestAnd>
</diadata>
<diadata>
<bestAnd>-50</bestAnd>
</diadata>
</team>
XSL
<xsl:variable name="list">
<xsl:value-of select="'M'" />
<xsl:for-each select="/team/diadata/bestAnd">
<xsl:choose>
<xsl:when test=". <0">
<xsl:value-of select=".*-1+400" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
<xsl:variable name="position" select="position()" />
<xsl:value-of select="concat(/team/rectx*$position+40,' ',.,' L')" />
</xsl:for-each>
</xsl:variable>
<xsl:variable name="finallist">
<xsl:value-of select="substring($list, 1, string-length($list) - 2)" />
</xsl:variable>
<text x="250" y="50"
style="font-family: Arial;
font-size : 24;
stroke : #000000;
fill : #000000;">
<xsl:value-of select="$finallist" />
</text>
The output has to be
M70 750 L100 650 L130 450
however with the choose statement it is
M75070 -350 L650100 -250 L450130 -50
so it does
"letter""y-val after calc""x-val" "y-val"
I can't understand why the concat does not work with the choose statement but without it works great. Prob is that I can't have negative numbers but instead need to take those and convert them to positive (*-1) and add 400.
Any ideas?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="list">
<xsl:value-of select="'M'"/>
<xsl:for-each select="/team/diadata/bestAnd">
<xsl:variable name="position" select="position()"/>
<xsl:value-of select="concat(/team/rectx*$position+40,' ')"/>
<xsl:choose>
<xsl:when test=". <0">
<xsl:value-of select=".*-1+400"/>
<xsl:value-of select="' L'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
</xsl:template>
</xsl:stylesheet>