I'm very new to XSLT, and I want to sort a part of XML file.(keeping all the file data)
Here's the file :
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<Project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test.xsd">
<SchemaVersion>1.0</SchemaVersion>
<Header>### uVision Project, (C) Keil Software</Header>
<Extensions>
<cExt>*.c</cExt>
<aExt>*.s*; *.src; *.a*</aExt>
<oExt>*.obj</oExt>
<lExt>*.lib</lExt>
<tExt>*.txt; *.h; *.inc</tExt>
<pExt>*.plm</pExt>
<CppX>*.cpp</CppX>
<nMigrate>0</nMigrate>
</Extensions>
<DateTime>
<dwLowDateTime>0</dwLowDateTime>
<dwHighDateTime>0</dwHighDateTime>
</DateTime>
<Target>
<TargetName>LYNX_XERIUS_APRR_CPU2006_0091</TargetName>
<ToolsetNumber>0x4</ToolsetNumber>
<TargetOption>
<OPTTT>
<gFlags>0</gFlags>
</OPTTT>
<AAA>
<SetRegEntry>
<Number>0</Number>
</SetRegEntry>
</AAA>
<Breakpoint>
<Bp>
<Number>0</Number>
</Bp>
<Bp>
<Number>1</Number>
</Bp>
<Bp>
<Number>2</Number>
</Bp>
<Bp>
<Number>3</Number>
</Bp>
<Bp>
<Number>4</Number>
</Bp>
</Breakpoint>
<Tracepoint>
<THDelay>0</THDelay>
</Tracepoint>
<DebugFlag>
<trace>0</trace>
</DebugFlag>
<LintExecutable></LintExecutable>
</TargetOption>
</Target>
<Target>
<TargetName>Debug</TargetName>
<ToolsetNumber>0x4</ToolsetNumber>
<TargetOption>
<OPTTT>
<gFlags>0</gFlags>
</OPTTT>
<AAA>
<SetRegEntry>
<Number>0</Number>
</SetRegEntry>
</AAA>
<Breakpoint>
<Bp>
<Number>0</Number>
</Bp>
<Bp>
<Number>1</Number>
</Bp>
<Bp>
<Number>2</Number>
</Bp>
<Bp>
<Number>3</Number>
</Bp>
<Bp>
<Number>4</Number>
</Bp>
</Breakpoint>
<Tracepoint>
<THDelay>0</THDelay>
</Tracepoint>
<DebugFlag>
<trace>0</trace>
</DebugFlag>
<LintExecutable></LintExecutable>
</TargetOption>
</Target>
<Target>
<TargetName>LYNX_HERMES_APRR_SERIE_200</TargetName>
<ToolsetNumber>0x4</ToolsetNumber>
<TargetOption>
<OPTTT>
<gFlags>0</gFlags>
</OPTTT>
<AAA>
<SetRegEntry>
<Number>0</Number>
</SetRegEntry>
</AAA>
<Breakpoint>
<Bp>
<Number>0</Number>
</Bp>
<Bp>
<Number>1</Number>
</Bp>
<Bp>
<Number>2</Number>
</Bp>
<Bp>
<Number>3</Number>
</Bp>
<Bp>
<Number>4</Number>
</Bp>
</Breakpoint>
<Tracepoint>
<THDelay>0</THDelay>
</Tracepoint>
<DebugFlag>
<trace>0</trace>
</DebugFlag>
<LintExecutable></LintExecutable>
</TargetOption>
</Target>
<Group>
<GroupName>::CMSIS</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>1</RteFlg>
</Group>
</Project>
Here's my XSLT :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="urn:TestNamespace" >
<xsl:output indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="text()[not(string-length(normalize-space()))]"/>
<xsl:template match="Project">
<xsl:copy>
<xsl:apply-templates select="Target">
<xsl:sort select="TargetName"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<!-- global template to copy everything that doesn't match the other templates -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I got a XML file sorted by TargetName as output, but with only Target nodes....
How can I setup the select case to keep the unfiltered nodes ?
I also tested with :
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="Target">
<xsl:sort select="TargetName"/>
</xsl:apply-templates>
</xsl:copy>
In this case, all data is kept but there is no sorting ?
I will appreciate some explanation to improve my knowledge.
Best Regards
One approach is:
<xsl:template match="Project">
<xsl:copy>
<xsl:apply-templates select="node()">
<xsl:sort select="TargetName" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
Why is your solution not working:
<xsl:apply-templates select="Target">
You select only Target to apply further.
I would probably do this (using XSLT 2.0):
<xsl:template match="Project">
<xsl:copy>
<xsl:for-each-group select="*" group-adjacent="node-name()">
<xsl:apply-templates>
<xsl:sort select="TargetName"/>
</xsl:apply-templates>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
on the assumption that sorting groups of elements other than Target elements does no harm. But it would probably be cleaner to have a conditional (xsl:choose) inside the xsl:for-each-group so the sorting is only done when test="self::Target".
Related
I am acting on a set of documents that have a <DataTypes> area, which defines the structure of groups of primative datatypes and other structures, and a <Tags> area, which defines the values of instances of these datatypes.
Original XML
<?xml version="1.0" encoding="utf-8" ?>
<Program>
<DataTypes>
<DataType Name="String20">
<Member Name="LEN" DataType="INTEGER" Dimension="0" />
<Member Name="DATA" DataType="BYTE" Dimension="20" />
</DataType>
<DataType Name="UDT_Params">
<Member Name="InAlarm" DataType="BIT" Dimension="0" />
<Member Name="SetPoint" DataType="FLOAT" Dimension="0" />
<Member Name="DwellTime" DataType="INTEGER" Dimension="0" />
<Member Name="UserName" DataType="String20" Dimension="0" />
</DataType>
</DataTypes>
<Tags>
<Tag Name="MyParameters" DataType="UDT_Params">
<Data Name="InAlarm" DataType="BIT" Value="0" />
<Data Name="SetPoint" DataType="FLOAT" Value="4.5" />
<Data Name="DwellTime" DataType="INTEGER" Value="10" />
<Data Name="UserName" DataType="String20">
<Data Name="LEN" DataType="INTEGER" Value="3" />
<Data Name="DATA" DataType="String20" > <!--The system I'm working in shows strings as arrays of BYTES in DataType, -->
Bob <!--but calls them out as Strings when they are used as tags. I cannot change it.-->
</Data>
</Data>
</Tag>
</Tags>
</Program>
Stylesheet
<?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:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--Packing algorithm. Works fine on datatypes, but not on Tags.-->
<xsl:template name="pack-nodes">
<xsl:param name="nodes" />
<!--Omitted for brevity-->
</xsl:template>
<!--Pack DataTypes-->
<xsl:variable name="datatypes-packed">
<xsl:call-template name="pack-nodes">
<xsl:with-param name="nodes" select="/Program/DataTypes/DataType" />
</xsl:call-template>
</xsl:variable>
<!--Write DataTypes to output.-->
<xsl:template match="/Program/DataTypes">
<xsl:copy>
<xsl:for-each select="msxsl:node-set($datatypes-packed)">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<!--Pack tags-->
<xsl:variable name="tags-packed">
<xsl:call-template name="pack-nodes">
<xsl:with-param name="nodes" select="/Program/Tags/Tag" />
</xsl:call-template>
</xsl:variable>
<!--Write Tags to output.-->
<xsl:template match="/Program/Tags">
<xsl:copy>
<xsl:for-each select="msxsl:node-set($tags-packed)">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="utf-8"?>
<Program>
<DataTypes>
<DataType Name="String20">
<Member Name="LEN" DataType="INTEGER" Dimension="0"/>
<Member Name="DATA" DataType="BYTE" Dimension="20"/>
</DataType>
<DataType Name="Parameters" DataType="UDT_Params">
<Member Name="UserName" DataType="String20" Dimension="0"/>
<Member Name="SetPoint" DataType="FLOAT" Dimension="0"/>
<Member Name="DwellTime" DataType="INTEGER" Dimension="0"/>
<Member Name="InAlarm" DataType="BIT" Dimension="0"/>
</DataType>
</DataTypes>
<Tags>
<Tag Name="MyParameters" DataType="UDT_Params">
<Data Name="UserName" DataType="String20">
<Data Name="DATA" DataType="String20"> <!--Note that DATA comes before LEN -->
Bob
</Data>
<Data Name="LEN" DataType="INTEGER" Value="3"/>
</Data>
<Data Name="SetPoint" DataType="FLOAT" Value="4.5"/>
<Data Name="DwellTime" DataType="INTEGER" Value="10"/>
<Data Name="InAlarm" DataType="BIT" Value="0"/>
</Tag>
</Tags>
</Program>
My operations on the DataTypes section adds nodes and changes the node order. For the section to work correctly, the tag elements must match the contents and order of their respective datatypes, exactly.
If I keep a variable in memory of the final state of the DataSet nodes, is there a simple way to have the tag nodes look up their dataset (via the Structure and StructureMember #DataSet attributes, and sort their members accordingly?
I'm having trouble figuring out where to start.
NOTE: Transformation must be in XSLT 1.0. I'm using .Net, and don't want to introduce a lot of dependencies on external libraries.
It's a bit tricky in XSLT 1.0 (isn't everything?) but a technique that sometimes works is to construct a variable $tokens containing the list of tokens in the required order, for example "|Description|Name|ProcessEntityIndex|Severity|...", and then sort on select="string-length(substring-before($tokens, concat('|',#Name)))".
Using Michael Kay's suggesting for sorting, I ended up with the following:
Stylesheet
<?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:output method="xml" indent="yes"/>
<!--Skipped to new part -->
<xsl:template name="sort-by-datatype">
<xsl:param name="tags" />
<xsl:param name="datatypes" />
<xsl:for-each select="msxsl:node-set($tags)">
<!--First, do an edge-check.-->
<xsl:variable name="edge">
<xsl:call-template name="edge-check">
<xsl:with-param name="n1" select="." />
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<!--No children, nothing to sort. Just do a deep copy.-->
<xsl:when test="$edge = 'true'">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise>
<!--Search for datatype in the DataTypes nodeset, and use it to create a list of members in order.-->
<xsl:variable name="tag-datatype" select="./#DataType" />
<xsl:variable name="tokens-untrimmed">
<xsl:for-each select="msxsl:node-set($datatypes)/DataType[#Name = $tag-datatype]/Member">
<xsl:value-of select="concat(' | ', #Name)"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="tokens" select="substring-after($tokens-untrimmed, '|')" />
<xsl:choose>
<!--If tokens string is empty (maybe because we couldn't find the datatype?), just copy the tag as it is, then recurse.-->
<xsl:when test="string-length($tokens) = 0">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:call-template name="sort-by-datatype">
<xsl:with-param name="tags" select="." />
<xsl:with-param name="datatypes" select="$datatypes" />
</xsl:call-template>
</xsl:copy>
</xsl:when>
<!--Otherwise, sort members in the same order as datatype-->
<xsl:otherwise>
<!--Build variable with sorted members.-->
<xsl:variable name="tag-members-sorted">
<xsl:for-each select="*">
<xsl:sort data-type="number" order="ascending" select="string-length(substring-before($tokens, concat(' | ', #Name)))" /> <!--Magic Sort Algorithm-->-->
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<!--Copy the parent node node.-->
<xsl:copy>
<xsl:copy-of select="#*"/>
<!--Now sort and copy the children.-->
<xsl:for-each select="msxsl:node-set($tag-members-sorted)/*">
<!--Recurse. This copies the child node.-->
<xsl:call-template name="sort-by-datatype">
<xsl:with-param name="tags" select="." />
<xsl:with-param name="datatypes" select="$datatypes" />
</xsl:call-template>
</xsl:for-each>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<!--Pack tags-->
<xsl:variable name="tags-packed">
<xsl:call-template name="sort-by-datatype">
<xsl:with-param name="tags" select="/Program/Tags/Tag" />
<xsl:with-param name="datatypes" select="$datatypes-packed" />
</xsl:call-template>
</xsl:variable>
<!--Skipped for brevity-->
</xsl:stylesheet>
I need to create XML that is sorted by numeric values I pull from another XSLT, which I use as a cross reference. The below source XML (source.xml) has four alpha characters at Partner/Header/#whse.
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<Header whse="NCCH" >
<Contract claimNumber="00000000" />
</Header>
<Header whse="TXAU" >
<Contract claimNumber="00000000" />
</Header>
<Header whse="LANO" >
<Contract claimNumber="00000000" />
</Header>
<Header whse="MIGR">
<Contract claimNumber="00000000" />
</Header>
<Header whse="TXHO">
<Contract claimNumber="00000000" />
</Header>
</Partner>
I need to cross reference the alpha characters to get the DUNS+4.
I use this XSLT (Duns_config.xslt) to get the DUNS.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="SHIPTODUNS">
<xsl:param name="Whse" />
<xsl:choose>
<xsl:when test="$Whse = 'LANO'"><xsl:value-of select="'0044893600101'" /></xsl:when>
<xsl:when test="$Whse = 'TXHO'"><xsl:value-of select="'0044893600103'" /></xsl:when>
<xsl:when test="$Whse = 'TXAU'"><xsl:value-of select="'0044893600105'" /></xsl:when>
<xsl:when test="$Whse = 'NCCH'"><xsl:value-of select="'0044893600214'" /></xsl:when>
<xsl:when test="$Whse = 'MIGR'"><xsl:value-of select="'8949713340601'" /></xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
In the main XSLT (Transaction.xslt), I include the Duns_config.xslt and call SHIPTODUNS putting the data in the variable $headerDuns. I then get the last three digits of the DUNS+4 and put them into the variable $varWhse and try to sort by this variable:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:include href="Duns_config.xslt"/>
<xsl:template match="Partner">
<Partner partnerId="{./#partnerId}">
<xsl:apply-templates select="./Header" />
</Partner>
</xsl:template>
<xsl:template match="Header">
<xsl:variable name="headerDuns">
<xsl:call-template name = "SHIPTODUNS">
<xsl:with-param name="Whse" select="./#whse" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="varWhse">
<xsl:value-of select="substring($headerDuns, 11, 3)" />
</xsl:variable>
<xsl:for-each select="current()">
<xsl:sort select="$varWhse" />
<transaction varwhse="{$varWhse}">
<duns number="{$headerDuns}" />
</transaction>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The output is not sorted by the $varWhse:
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<transaction varwhse="214">
<duns number="0044893600214"/>
</transaction>
<transaction varwhse="105">
<duns number="0044893600105"/>
</transaction>
<transaction varwhse="101">
<duns number="0044893600101"/>
</transaction>
<transaction varwhse="601">
<duns number="8949713340601"/>
</transaction>
<transaction varwhse="103">
<duns number="0044893600103"/>
</transaction>
</Partner>
I am wanting the data to come out like this:
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<transaction varwhse="101">
<duns number="0044893600101"/>
</transaction>
<transaction varwhse="103">
<duns number="0044893600103"/>
</transaction>
<transaction varwhse="105">
<duns number="0044893600105"/>
</transaction>
<transaction varwhse="214">
<duns number="0044893600214"/>
</transaction>
<transaction varwhse="601">
<duns number="8949713340601"/>
</transaction>
</Partner>
Anyone see what I am doing wrong or have another way? This is my first post on this site. It's a lot of information and I hope it makes sense.
One of the problems with your code is that the xsl:sort in your Header template is not the first instruction; xsl:sort has to be the first instruction. Another problem is that the sorting appears in the Header template, but it would have to be applied on an upper level. To correct these mistakes, some restructuring of your templates has to be performed.
For the following solution I chose to put all of the logic into one template - with two steps:
Fill a variable named mapping with a sub-data-structure with item elements.
Iterate over that variable with a sorted xsl:for-each and output the transaction elements.
The result looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:include href="Duns_config.xslt"/>
<xsl:template match="Partner">
<Partner partnerId="{./#partnerId}">
<xsl:variable name="mapping">
<xsl:for-each select="Header">
<xsl:variable name="headerDuns">
<xsl:call-template name = "SHIPTODUNS">
<xsl:with-param name="Whse" select="#whse" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="varWhse">
<xsl:value-of select="substring($headerDuns, 11, 3)" />
</xsl:variable>
<item>
<whse><xsl:value-of select="$varWhse" /></whse>
<duns><xsl:value-of select="$headerDuns" /></duns>
</item>
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="$mapping/item">
<xsl:sort select="whse" /> <!-- See how xsl:sort is the first instruction -->
<transaction varwhse="{whse}">
<duns number="{duns}"/>
</transaction>
</xsl:for-each>
</Partner>
</xsl:template>
</xsl:stylesheet>
And its output is:
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<transaction varwhse="101">
<duns number="0044893600101"/>
</transaction>
<transaction varwhse="103">
<duns number="0044893600103"/>
</transaction>
<transaction varwhse="105">
<duns number="0044893600105"/>
</transaction>
<transaction varwhse="214">
<duns number="0044893600214"/>
</transaction>
<transaction varwhse="601">
<duns number="8949713340601"/>
</transaction>
</Partner>
Which is as desired.
Apply sorting after getting the result of choose. What happen is you are still generating the xml output, but you are sorting it on the Header item level.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:include href="Duns_config.xslt"/>
<xsl:template match="Partner">
<xsl:variable name="partial">
<PartnerTemp partnerId="{./#partnerId}">
<xsl:apply-templates select="./Header" />
</PartnerTemp>
</xsl:variable>
<xsl:apply-templates select="$partial"></xsl:apply-templates>
</xsl:template>
<xsl:template match="PartnerTemp">
<Partner partnerId="{./#partnerId}">
<xsl:perform-sort select="transaction">
<xsl:sort select="#varwhse"/>
</xsl:perform-sort>
</Partner>
</xsl:template>
<xsl:template match="Header">
<xsl:variable name="headerDuns">
<xsl:call-template name = "SHIPTODUNS">
<xsl:with-param name="Whse" select="./#whse" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="varWhse">
<xsl:value-of select="substring($headerDuns, 11, 3)" />
</xsl:variable>
<xsl:for-each select="current()">
<transaction varwhse="{$varWhse}">
<duns number="{$headerDuns}" />
</transaction>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I have the below input XML,
for some reason my xslt is not able to sort it.
please advice.
appreciate your insights.
this is the full XML
......................................................
I want to sort the G_SHL segment with its content based on D_628 field.
`<?xml version="1.0" encoding="UTF-8"?>
<LIST>
<S_ISA>
<D_I01>00</D_I01>
<D_I02/>
<D_I03>00</D_I03>
<D_I04/>
<D_I05>01</D_I05>
<D_I06></D_I06>
<D_I05_2>ZZ</D_I05_2>
<D_I07></D_I07>
<D_I08>160427</D_I08>
<D_I09>1106</D_I09>
<D_I10>U</D_I10>
<D_I11>00401</D_I11>
<D_I12>000000001</D_I12>
<D_I13>0</D_I13>
<D_I14/>
<D_I15>></D_I15>
<S_GS>
<D_479>SH</D_479>
<D_142></D_142>
<D_124></D_124>
<D_373>20160427</D_373>
<D_337>1106</D_337>
<D_28>1</D_28>
<D_455>X</D_455>
<D_480>004010</D_480>
<S_ST>
<D_143>856</D_143>
<D_329>0001</D_329>
<S_BSN>
<D_353>00</D_353>
<D_396>0081664420</D_396>
<D_373>20160426</D_373>
<D_337>1347</D_337>
<D_1005>0001</D_1005>
</S_BSN>
<G_SHL>
<S_HL>
<D_628>0000001</D_628>
<D_734>0000000</D_734>
<D_735>S</D_735>
<S_PRF>
<D_324>SITT10-1447195769627</D_324>
<D_328>SITT10-1447195769627</D_328>
</S_PRF>
<S_MEA>
<D_737>PD</D_737>
<D_738>G</D_738>
<D_739>0.081</D_739>
<C_C001>
<D_355>KG</D_355>
</C_C001>
</S_MEA>
<S_TD1>
<D_103>PKG</D_103>
<D_80>00002</D_80>
<D_187>G</D_187>
<D_81>00000.18</D_81>
<D_355>LB</D_355>
</S_TD1>
<S_TD5>
<D_133>O</D_133>
<D_66>2</D_66>
<D_368>CC</D_368>
</S_TD5>
<S_REF>
<D_128>WH</D_128>
<D_127>WH:</D_127>
</S_REF>
<S_REF>
<D_128>RN</D_128>
<D_127>RN:</D_127>
</S_REF>
<S_DTM>
<D_374>011</D_374>
<D_373>20160414</D_373>
</S_DTM>
<G_SN1>
<S_N1>
<D_98>ST</D_98>
<D_93>C3333</D_93>
<S_N3>
<D_166>TEST</D_166>
</S_N3>
<S_N4>
<D_19>TEST</D_19>
<D_116>12345</D_116>
<D_26>US</D_26>
</S_N4>
</S_N1>
</G_SN1>
</S_HL>
</G_SHL>
<G_SHL>
<S_HL>
<D_628>0000002</D_628>
<D_734>0000001</D_734>
<D_735>O</D_735>
<S_MAN>
<D_88>CP</D_88>
<D_87></D_87>
</S_MAN>
</S_HL>
</G_SHL>
<G_SHL>
<S_HL>
<D_628>0000003</D_628>
<D_734>0000002</D_734>
<D_735>P</D_735>
</S_HL>
</G_SHL>
<G_SHL>
<S_HL>
<D_628>0000005</D_628>
<D_734>0000002</D_734>
<D_735>P</D_735>
</S_HL>
</G_SHL>
<G_SHL>
<S_HL>
<D_628>0000004</D_628>
<D_734>0000003</D_734>
<D_735>I</D_735>
<S_LIN>
<D_350>000010</D_350>
<D_235>SK</D_235>
<D_234></D_234>
<D_235_2>BP</D_235_2>
<D_234_2></D_234_2>
</S_LIN>
<S_SN1>
<D_350></D_350>
<D_382>2.000</D_382>
<D_355>EA</D_355>
<D_668>AC</D_668>
</S_SN1>
</S_HL>
</G_SHL>
<G_SHL>
<S_HL>
<D_628>0000006</D_628>
<D_734>0000005</D_734>
<D_735>I</D_735>
<S_LIN>
<D_350>000020</D_350>
<D_235></D_235>
<D_234></D_234>
<D_235_2>BP</D_235_2>
<D_234_2></D_234_2>
</S_LIN>
<S_SN1>
<D_382>1.000</D_382>
<D_355>EA</D_355>
<D_668>AC</D_668>
</S_SN1>
</S_HL>
</G_SHL>
<S_CTT>
<D_354>1</D_354>
</S_CTT>
<S_SE>
<D_96>[]</D_96>
<D_329>0001</D_329>
</S_SE>
</S_ST>
<S_GE>
<D_97>0</D_97>
<D_28>1</D_28>
</S_GE>
</S_GS>
<S_IEA>
<D_I16>01</D_I16>
<D_I12>000000001</D_I12>
</S_IEA>
</S_ISA>
</LIST>`
Try
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/LIST/S_ISA/S_GS/S_ST">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="G_SHL">
<xsl:sort select="S_HL/D_628" data-type="number" order="descending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
I can't verify the path /LIST/S_ISA/S_GS/S_ST but assume you have those ancestor elements in your real XML.
If there can be sibling elements you want to keep in their original input position then one way in XSLT 2.0 is to first identify the adjacent elements you want to sort with for-each-group group-adjacent:
<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 indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/LIST/S_ISA/S_GS/S_ST">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:for-each-group select="*" group-adjacent="boolean(self::G_SHL)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()">
<xsl:sort select="S_HL/D_628" data-type="number" order="descending"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"></xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Need some help in sorting and then choosing either maximum or minimum value in XSLT.
Source xml:
<target>
<relatedTarget>
<permitExpiry>2005-07-02T08:11:00.000Z</permitExpiry>
<permitStart>2015-07-11T09:22:00.000Z</permitStart>
</relatedTarget>
<relatedTarget>
<permitExpiry>2003-07-12T08:11:00.000Z</permitExpiry>
<permitStart>2014-07-01T09:22:00.000Z</permitStart>
</relatedTarget>
<relatedTarget>
<permitExpiry>2002-07-10T08:11:00.000Z</permitExpiry>
<permitStart>2016-07-06T09:22:00.000Z</permitStart>
</relatedTarget>
</target>
Result xml:
<target>
<relatedTarget>
<permitStart>2014-07-01T09:22:00.000Z</permitStart>
<permitExpiry>2005-07-02T08:11:00.000Z</permitExpiry>
</relatedTarget>
</target>
Basically i need the result should have minimum permitStart date and maximum permitExpiry date from among all the dates comming.
My sample xsl:
<xsl:template match="/">
<xsl:variable name="permitStartVar" select="//permitStart"/>
<xsl:variable name="permitStopVar" select="//permitExpiry"/>
<xsl:for-each select="relatedTask">
<xsl:sort select="substring(permitStart,1,4)" /> <!-- Year -->
<xsl:sort select="substring(permitStart,6,2)" /> <!-- Month -->
<xsl:sort select="substring(permitStart,9,2)" /> <!-- Day -->
<xsl:sort select="substring(permitStart,12,2)" /> <!-- Hour -->
<xsl:sort select="substring(permitStart,15,2)" /> <!-- Minute -->
<xsl:sort select="substring(permitStart,18,2)" /> <!-- Second -->
<xsl:sort select="substring(permitExpiry,1,4)" /> <!-- Year -->
<xsl:sort select="substring(permitExpiry,6,2)" /> <!-- Month -->
<xsl:sort select="substring(permitExpiry,9,2)" /> <!-- Day -->
<xsl:sort select="substring(permitExpiry,12,2)" /> <!-- Hour -->
<xsl:sort select="substring(permitExpiry,15,2)" /> <!-- Minute -->
<xsl:sort select="substring(permitExpiry,18,2)" /> <!-- Second -->
</xsl:for-each>
<target>
<relatedTarget>
<permitStart><xsl:value-of select="$permitStartVar[1]"/></permitStart>
<permitExpiry><xsl:value-of select="$permitStopVar[last()]"/></permitExpiry>
</relatedTarget>
</target>
</template>
You could do this without sorting by using max() and min() on the xs:dateTime values.
Example
XML Input
<target>
<relatedTarget>
<permitExpiry>2005-07-02T08:11:00.000Z</permitExpiry>
<permitStart>2015-07-11T09:22:00.000Z</permitStart>
</relatedTarget>
<relatedTarget>
<permitExpiry>2003-07-12T08:11:00.000Z</permitExpiry>
<permitStart>2014-07-01T09:22:00.000Z</permitStart>
</relatedTarget>
<relatedTarget>
<permitExpiry>2002-07-10T08:11:00.000Z</permitExpiry>
<permitStart>2016-07-06T09:22:00.000Z</permitStart>
</relatedTarget>
</target>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<target>
<relatedTarget>
<xsl:copy-of select="(relatedTarget/permitStart[.=min(/*/relatedTarget/xs:dateTime(permitStart))])[1]"/>
<xsl:copy-of select="(relatedTarget/permitExpiry[.=max(/*/relatedTarget/xs:dateTime(permitExpiry))])[1]"/>
</relatedTarget>
</target>
</xsl:template>
</xsl:stylesheet>
Output
<target>
<relatedTarget>
<permitStart>2014-07-01T09:22:00.000Z</permitStart>
<permitExpiry>2005-07-02T08:11:00.000Z</permitExpiry>
</relatedTarget>
</target>
As I follow your approach ,
<xsl:stylesheet exclude-result-prefixes="xs" version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="target">
<xsl:variable name="permitStartVar" select="//permitStart"/>
<xsl:variable name="permitStopVar" select="//permitExpiry"/>
<xsl:variable name="temp">
<xsl:for-each select="relatedTarget/permitStart">
<xsl:sort select="substring(permitStart,1,4)"/>
<!--Year-->
<xsl:sort select="substring(permitStart,6,2)"/>
<!--Month-->
<xsl:sort select="substring(permitStart,9,2)"/>
<!--Day-->
<xsl:sort select="substring(permitStart,12,2)"/> <!--Hour-->
<xsl:sort select="substring(permitStart,15,2)"/>
<!--Minute-->
<xsl:sort select="substring(permitStart,18,2)"/>
<!--Second-->
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="temp1">
<xsl:for-each select="relatedTarget/permitExpiry">
<xsl:sort select="substring(permitExpiry,1,4)" order="descending"/>
<!--Year-->
<xsl:sort select="substring(permitExpiry,6,2)" order="descending"/>
<!--Month-->
<xsl:sort select="substring(permitExpiry,9,2)" order="descending"/>
<!--Day-->
<xsl:sort select="substring(permitExpiry,12,2)" order="descending"/>
<!--Hour-->
<xsl:sort select="substring(permitExpiry,15,2)" order="descending"/>
<!--Minute-->
<xsl:sort select="substring(permitExpiry,18,2)" order="descending"/>
<!--Second-->
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<target>
<relatedTarget>
<permitStart>
<xsl:value-of select="$temp//permitStart[1]"/>
</permitStart>
<permitExpiry>
<xsl:value-of select="$temp1/permitExpiry[1]"/>
</permitExpiry>
</relatedTarget>
</target>
</xsl:template>
</xsl:stylesheet>
Output :
<target>
<relatedTarget>
<permitStart>2014-07-01T09:22:00.000Z</permitStart>
<permitExpiry>2003-07-12T08:11:00.000Z</permitExpiry>
</relatedTarget>
</target>
EDIT :
More simplified version :
<xsl:stylesheet exclude-result-prefixes="xs" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="target">
<xsl:variable name="permitStartVar" select="//permitStart"/>
<xsl:variable name="permitStopVar" select="//permitExpiry"/>
<xsl:variable name="temp">
<xsl:for-each select="relatedTarget/permitStart">
<xsl:sort select="."/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="temp1">
<xsl:for-each select="relatedTarget/permitExpiry">
<xsl:sort select="." order="descending"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<target>
<relatedTarget>
<permitStart>
<xsl:value-of select="$temp//permitStart[1]"/>
</permitStart>
<permitExpiry>
<xsl:value-of select="$temp1/permitExpiry[1]"/>
</permitExpiry>
</relatedTarget>
</target>
</xsl:template>
</xsl:stylesheet>
I have a xml as below that I'd like to copy n times while incrementing one of its element and one of its attribute.
XML input:
<?xml version="1.0"?>
<header xmlns="http://test.com" >
<Batch>
<test document="dump" >
<Person position=1>
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
</test>
</Batch>
</header>
and I'd like something like below with the number of increment to be a variable.
XML output:
<?xml version="1.0"?>
<header xmlns="http://test.com" >
<Batch>
<test document="dump" >
<Person position=1>
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
<Person position=2>
<properties>
<name>John</name>
<number>2</number>
</properties>
</Person>
...
<Person position=n>
<properties>
<name>John</name>
<number>n</number>
</properties>
</Person>
</test>
</Batch>
</header>
To solve this, I've started with the xslt below:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pTimes" select="2"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes"/>
<xsl:with-param name="pPosition" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="applyNTimes">
<xsl:param name="pTimes" select="0"/>
<xsl:param name="pPosition" select="1"/>
<xsl:if test="$pTimes > 0">
<xsl:choose>
<xsl:when test="$pTimes = 1">
<xsl:apply-templates select="*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vHalf" select="floor($pTimes div 2)"/>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:call-template>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes - $vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition + $vHalf"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template match="Person">
<xsl:param name="pPosition" select="1"/>
<xsl:value-of select="$newline"/>
<Person position="{$pPosition}">
<xsl:apply-templates>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</Person>
</xsl:template>
<xsl:template match="number">
<xsl:param name="pPosition" select="1"/>
<number><xsl:value-of select="$pPosition"/></number>
</xsl:template>
</xsl:stylesheet>
but the output includes the namespace in elements. The element and attribute #position are always set to 1. Also, the header surrounds each element.
Please refer to the output below with n=2
<Batch xmlns="http://test.com">
<test document="dump">
<Person position="1">
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
</test>
</Batch>
<Batch xmlns="http://test.com">
<test document="dump">
<Person position="1">
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
</test>
</Batch>
Any clue?
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://test.com"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pTimes" select="2"/>
<xsl:template match="node()|#*">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:apply-templates select="node()|#*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="t:test">
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes"/>
<xsl:with-param name="pPosition" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="applyNTimes">
<xsl:param name="pTimes" select="0"/>
<xsl:param name="pPosition" select="1"/>
<xsl:if test="$pTimes > 0">
<xsl:choose>
<xsl:when test="$pTimes = 1">
<xsl:apply-templates select="*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vHalf" select="floor($pTimes div 2)"/>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:call-template>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes - $vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition + $vHalf"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template match="t:Person">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:attribute name="position">
<xsl:value-of select="$pPosition"/>
</xsl:attribute>
<xsl:apply-templates>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="t:number">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:value-of select="$pPosition"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<header xmlns="http://test.com" >
<Batch>
<test document="dump" >
<Person position="1">
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
</test>
</Batch>
</header>
produces the wanted results:
<header xmlns="http://test.com">
<Batch>
<Person position="1">
<properties>
<name>John</name>
<number>1</number>
</properties>
</Person>
<Person position="2">
<properties>
<name>John</name>
<number>2</number>
</properties>
</Person>
</Batch>
</header>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:t="http://test.com">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pTimes" select="2"/>
<xsl:template match="node()|#*">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:apply-templates select="node()|#*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="t:test">
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes"/>
<xsl:with-param name="pPosition" select="1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="applyNTimes">
<xsl:param name="pTimes" select="0"/>
<xsl:param name="pPosition" select="1"/>
<xsl:if test="$pTimes > 0">
<xsl:choose>`enter code here`
<xsl:when test="$pTimes = 1">
<xsl:apply-templates select="*">
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vHalf" select="floor($pTimes div 2)"/>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:call-template>
<xsl:call-template name="applyNTimes">
<xsl:with-param name="pTimes" select="$pTimes - $vHalf"/>
<xsl:with-param name="pPosition" select="$pPosition + $vHalf"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template match="t:Person">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:attribute name="position">
<xsl:value-of select="$pPosition"/>
</xsl:attribute>
<xsl:apply-templates>
<xsl:with-param name="pPosition" select="$pPosition"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="t:number">
<xsl:param name="pPosition" select="1"/>
<xsl:copy>
<xsl:value-of select="$pPosition"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>