Select repeating node with xpath - xslt

I am using XSLT 1.0
I have the following xml document
<ns0:Root xmlns:ns0="http://schemas.microsoft.com/BizTalk/2003/aggschema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InputMessagePart_0>
<ns1:ArrayOfArticleMasterDTO xmlns:ns1="http://BTS.GO.FactFeeds/DC_ArticleMaster">
<ArticleMasterDTO>
<AltBarcodes>
<string>5020436473709</string>
</AltBarcodes>
<ClientId>GO01</ClientId>
<ArticleID>100000005503</ArticleID>
<ArticleReference>CORE_OWBA_WW873R</ArticleReference>
<ArticleSize>14</ArticleSize>
<ArticleStyle>26600</ArticleStyle>
<ArticleFit>-</ArticleFit>
<SupplierID>50102</SupplierID>
<Description>AMELIE OTRS [REG]</Description>
<Sku>COREOWBAWW873RBLAC-14</Sku>
<UnitsPerCase>1</UnitsPerCase>
<UnitsPerLayer xsi:nil="true" />
<UnitsPerPallet xsi:nil="true" />
<LayersPerPallet xsi:nil="true" />
<FullPalletWeightKG xsi:nil="true" />
<CaseLengthMM xsi:nil="true" />
<CaseHeightMM xsi:nil="true" />
<CaseDepthMM xsi:nil="true" />
<CaseWeightG xsi:nil="true" />
<Colour>BLAC</Colour>
<Level1Description>CLOTHING</Level1Description>
<Level2Description>WOMENS</Level2Description>
<Level3Description>WP LEGWEAR</Level3Description>
<Level4Description>LEG COATED</Level4Description>
<PrimaryBarcode>5051513724896</PrimaryBarcode>
</ArticleMasterDTO>
<ArticleMasterDTO>
<AltBarcodes>
<string>5027793433728</string>
</AltBarcodes>
<ClientId>GO01</ClientId>
<ArticleID>100000032177</ArticleID>
<ArticleReference>CORE_OMOE_47354</ArticleReference>
<ArticleSize>L-XL</ArticleSize>
<ArticleStyle>24608</ArticleStyle>
<ArticleFit>-</ArticleFit>
<SupplierID>50013</SupplierID>
<Description>POWER STRETCH GLOVE</Description>
<Sku>COREOMOE47354BLAC-L-XL</Sku>
<UnitsPerCase>6</UnitsPerCase>
<UnitsPerLayer xsi:nil="true" />
<UnitsPerPallet xsi:nil="true" />
<LayersPerPallet xsi:nil="true" />
<FullPalletWeightKG xsi:nil="true" />
<CaseLengthMM xsi:nil="true" />
<CaseHeightMM xsi:nil="true" />
<CaseDepthMM xsi:nil="true" />
<CaseWeightG xsi:nil="true" />
<Colour>BLAC</Colour>
<Level1Description>CLOTHING</Level1Description>
<Level2Description>MENS</Level2Description>
<Level3Description>GLOVES</Level3Description>
<Level4Description>FLEECE GLOVE</Level4Description>
<PrimaryBarcode>5052071278609</PrimaryBarcode>
</ArticleMasterDTO>
</ns1:ArrayOfArticleMasterDTO>
</InputMessagePart_0>
<InputMessagePart_1>
<ns2:SelectResponse xmlns:ns2="http://Microsoft.LobServices.OracleDB/2007/03/USER_DWDEV/Table/DW_PACK_BARCODES">
<ns2:SelectResult>
<ns2:DW_PACK_BARCODESRECORDSELECT>
<ns2:PRODUCT_ID>100000005503</ns2:PRODUCT_ID>
<ns2:SYS_BARCODE>SYS_BARCODES</ns2:SYS_BARCODE>
<ns2:PACK_TYPE>PACK_T</ns2:PACK_TYPE>
<ns2:CHECK_DIGIT_PACK_BARCODE>PackBarcode5503</ns2:CHECK_DIGIT_PACK_BARCODE>
<ns2:PACK_BARCODE>PACK_BARCODEP</ns2:PACK_BARCODE>
<ns2:CHECK_DIGIT_CARTON_BARCODE>CHECK_DIGIT_CA</ns2:CHECK_DIGIT_CARTON_BARCODE>
<ns2:CARTON_BARCODE>CARTON_BARCOD</ns2:CARTON_BARCODE>
<ns2:IN_DATE>1999-05-31T13:20:00.000-05:00</ns2:IN_DATE>
<ns2:UP_DATE>1999-05-31T13:20:00.000-05:00</ns2:UP_DATE>
<ns2:CREATEDTIME>1999-05-31T13:20:00.000-05:00</ns2:CREATEDTIME>
</ns2:DW_PACK_BARCODESRECORDSELECT>
<ns2:DW_PACK_BARCODESRECORDSELECT>
<ns2:PRODUCT_ID>100000032177</ns2:PRODUCT_ID>
<ns2:SYS_BARCODE>SYS_BARCODES</ns2:SYS_BARCODE>
<ns2:PACK_TYPE>PACK_T</ns2:PACK_TYPE>
<ns2:CHECK_DIGIT_PACK_BARCODE>PackBarcode32177</ns2:CHECK_DIGIT_PACK_BARCODE>
<ns2:PACK_BARCODE>PACK_BARCODEP</ns2:PACK_BARCODE>
<ns2:CHECK_DIGIT_CARTON_BARCODE>CHECK_DIGIT_CA</ns2:CHECK_DIGIT_CARTON_BARCODE>
<ns2:CARTON_BARCODE>CARTON_BARCOD</ns2:CARTON_BARCODE>
<ns2:IN_DATE>1999-05-31T13:20:00.000-05:00</ns2:IN_DATE>
<ns2:UP_DATE>1999-05-31T13:20:00.000-05:00</ns2:UP_DATE>
<ns2:CREATEDTIME>1999-05-31T13:20:00.000-05:00</ns2:CREATEDTIME>
</ns2:DW_PACK_BARCODESRECORDSELECT>
</ns2:SelectResult>
</ns2:SelectResponse>
</InputMessagePart_1>
</ns0:Root>
I'd like to select the value of the CHECK_DIGIT_PACK_BARCODE element that is a sibling of a given PRODUCT_ID
Following Tim's suggestion, here's my XSLT which attempts to provide a value for OuterCartonBarcode by keying from the CHECK_DIGIT_PACK_BARCODE of the ns2:SelectResponse
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var ns2 s1" version="1.0" xmlns:ns1="http://BTS.GO.FactFeeds/DC_ArticleMaster" xmlns:s1="http://schemas.microsoft.com/BizTalk/2003/aggschema" xmlns:ns2="http://Microsoft.LobServices.OracleDB/2007/03/USER_DWDEV/Table/DW_PACK_BARCODES" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:key name="barcodes" match="ns2:DW_PACK_BARCODESRECORDSELECT" use="ns2:PRODUCT_ID" />
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="/s1:Root" />
</xsl:template>
<xsl:template match="/s1:Root">
<ns1:ArrayOfArticleMasterDTO>
<xsl:for-each select="InputMessagePart_0/ns1:ArrayOfArticleMasterDTO/ArticleMasterDTO">
<ArticleMasterDTO>
<AltBarcodes>
<string>
<xsl:value-of select="AltBarcodes/string/text()" />
</string>
<xsl:value-of select="AltBarcodes/text()" />
</AltBarcodes>
<ClientId>
<xsl:value-of select="ClientId/text()" />
</ClientId>
<ArticleID>
<xsl:value-of select="ArticleID/text()" />
</ArticleID>
<ArticleReference>
<xsl:value-of select="ArticleReference/text()" />
</ArticleReference>
<ArticleSize>
<xsl:value-of select="ArticleSize/text()" />
</ArticleSize>
<ArticleStyle>
<xsl:value-of select="ArticleStyle/text()" />
</ArticleStyle>
<ArticleFit>
<xsl:value-of select="ArticleFit/text()" />
</ArticleFit>
<SupplierID>
<xsl:value-of select="SupplierID/text()" />
</SupplierID>
<Description>
<xsl:value-of select="Description/text()" />
</Description>
<Sku>
<xsl:value-of select="Sku/text()" />
</Sku>
<UnitsPerCase>
<xsl:value-of select="UnitsPerCase/text()" />
</UnitsPerCase>
<UnitsPerLayer>
<xsl:value-of select="UnitsPerLayer/text()" />
</UnitsPerLayer>
<xsl:variable name="var:v1" select="string(UnitsPerPallet/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v1)='true'">
<UnitsPerPallet>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</UnitsPerPallet>
</xsl:if>
<xsl:if test="string($var:v1)='false'">
<UnitsPerPallet>
<xsl:value-of select="UnitsPerPallet/text()" />
</UnitsPerPallet>
</xsl:if>
<xsl:variable name="var:v2" select="string(LayersPerPallet/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v2)='true'">
<LayersPerPallet>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</LayersPerPallet>
</xsl:if>
<xsl:if test="string($var:v2)='false'">
<LayersPerPallet>
<xsl:value-of select="LayersPerPallet/text()" />
</LayersPerPallet>
</xsl:if>
<xsl:variable name="var:v3" select="string(FullPalletWeightKG/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v3)='true'">
<FullPalletWeightKG>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</FullPalletWeightKG>
</xsl:if>
<xsl:if test="string($var:v3)='false'">
<FullPalletWeightKG>
<xsl:value-of select="FullPalletWeightKG/text()" />
</FullPalletWeightKG>
</xsl:if>
<xsl:variable name="var:v4" select="string(CaseLengthMM/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v4)='true'">
<CaseLengthMM>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</CaseLengthMM>
</xsl:if>
<xsl:if test="string($var:v4)='false'">
<CaseLengthMM>
<xsl:value-of select="CaseLengthMM/text()" />
</CaseLengthMM>
</xsl:if>
<xsl:variable name="var:v5" select="string(CaseHeightMM/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v5)='true'">
<CaseHeightMM>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</CaseHeightMM>
</xsl:if>
<xsl:if test="string($var:v5)='false'">
<CaseHeightMM>
<xsl:value-of select="CaseHeightMM/text()" />
</CaseHeightMM>
</xsl:if>
<CaseDepthMM>
<xsl:value-of select="CaseDepthMM/text()" />
</CaseDepthMM>
<CaseWeightG>
<xsl:value-of select="CaseWeightG/text()" />
</CaseWeightG>
<Colour>
<xsl:value-of select="Colour/text()" />
</Colour>
<Level1Description>
<xsl:value-of select="Level1Description/text()" />
</Level1Description>
<Level2Description>
<xsl:value-of select="Level2Description/text()" />
</Level2Description>
<Level3Description>
<xsl:value-of select="Level3Description/text()" />
</Level3Description>
<Level4Description>
<xsl:value-of select="Level4Description/text()" />
</Level4Description>
<xsl:call-template name="PBTemplate">
<xsl:with-param name="id" select="string(ArticleID/text())" />
</xsl:call-template>
<xsl:variable name="var:v6" select="string(PrimaryBarcode/#xsi:nil) = 'true'" />
<xsl:if test="string($var:v6)='true'">
<PrimaryBarcode>
<xsl:attribute name="xsi:nil">
<xsl:value-of select="'true'" />
</xsl:attribute>
</PrimaryBarcode>
</xsl:if>
<xsl:if test="string($var:v6)='false'">
<PrimaryBarcode>
<xsl:value-of select="PrimaryBarcode/text()" />
</PrimaryBarcode>
</xsl:if>
</ArticleMasterDTO>
</xsl:for-each>
</ns1:ArrayOfArticleMasterDTO>
</xsl:template>
<xsl:template name="PBTemplate">
<xsl:param name="id" select="ArticleID" />
<xsl:for-each select="key('barcodes',$id)">
<OuterCartonBarcode>
<xsl:value-of select="/ns2:CHECK_DIGIT_PACK_BARCODE"/>
</OuterCartonBarcode>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Your current xpath ends with this...
/*[local-name() = 'CHECK_DIGIT_PACK_BARCODE'[PRODUCT_ID/text()="100000005503"]]
... which does not look correct.
The expression you are looking for is this....
/ns0:Root/InputMessagePart_1/*[local-name() = 'SelectResponse']/*[local-name() = 'SelectResult']/*[*[local-name()= 'PRODUCT_ID']/text()='100000005503']/*[local-name() = 'CHECK_DIGIT_PACK_BARCODE']
Or, to make it a bit more readable, this....
/ns0:Root
/InputMessagePart_1
/*[local-name() = 'SelectResponse']
/*[local-name() = 'SelectResult']
/*[*[local-name()= 'PRODUCT_ID']/text()='100000005503']
/*[local-name() = 'CHECK_DIGIT_PACK_BARCODE']
It would be much better if the expression did not try to ignore the namespaces, but used the relevant prefixes (as happens already with ns0:Root). Then the expression is much simplified
/ns0:Root/InputMessagePart_1/ns2:SelectResponse/ns2:SelectResult/*[ns2:PRODUCT_ID='100000005503']/ns2:CHECK_DIGIT_PACK_BARCODE
Or better still, define a key like so:
<xsl:key name="barcodes" match="ns2:DW_PACK_BARCODESRECORDSELECT" use="ns2:PRODUCT_ID" />
Then you can write just this...
key('barcodes', '100000005503')/ns2:CHECK_DIGIT_PACK_BARCODE

Related

Count the total of preceding siblings and comments

I have many files that resemble the following a.xml file, although they are much larger:
<?xml version="1.0" encoding="UTF-8"?>
<a version="3.0">
<b bb="P1">
<!--============== b:P1 c:1 ==============-->
<c cc="1">
<d dd="61">d1
</d>
</c>
<!--============== b:P1 c:2 ==============-->
<c cc="2">
<d dd="17">d2
</d>
</c>
</b>
</a>
For each c there is only one preceding comment.
I want to output a file with the same structure of the following a.csv file:
1|1|a|0| |0| |0|
2|1|a|1|b|0| |0|
3|1|a|1|b|1|!|0|
3|1|a|1|b|2|c|0|
4|1|a|1|b|2|c|1|d
3|1|a|1|b|3|!|0|
3|1|a|1|b|4|c|0|
4|1|a|1|b|4|c|1|d
It represents the hierarchical tree for a.xml:
Field 1 is the hierarchical level. For instance a has level 1, b has level 2, etc.
Fields 2, 4, 6 and 8 are equal to:
if the current node's level is less than the current field's level then 0
else the total number of preceding siblings and comments plus one
Field 3, 5, 7 and 9 are equal to:
if the current node's level is less than the current field's level then " "
else either "!" if the current node is preceded by a comment or the node's name
In this example level 3 contains comments.
I cannot find a good way to do a for-each that includes both nodes and comments. When I use <xsl:for-each select="*"> I only loop through the nodes.
Because of that, I've come out with the following xslt, that checks if the current node is preceded by a comment:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:for-each select="*">
<xsl:variable name="elm01" select="local-name()" />
<xsl:text>1</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|0| |0| |0|</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm02" select="local-name()" />
<xsl:text>2</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|0| |0|</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm03" select="local-name()" />
<xsl:if test="preceding-sibling::comment()[1]">
<xsl:text>3</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|!</xsl:text>
<xsl:text>|0|</xsl:text>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:text>3</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<!-- TODO: I want to count the total of preceding siblings and comments -->
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|comment())+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
<xsl:text>|0|</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm04" select="local-name()" />
<xsl:text>4</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm04" />
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
However when I run the following command:
xsltproc a.xslt a.xml > a.csv
I get the following a.csv file:
1|1|a|0| |0| |0|
2|1|a|1|b|0| |0|
3|1|a|1|b|1|!|0|
3|1|a|1|b|1|c|0|
4|1|a|1|b|1|c|1|d
3|1|a|1|b|2|!|0|
3|1|a|1|b|2|c|0|
4|1|a|1|b|2|c|1|d
Please notice that field 6 is incorrect:
it is equal to 1 both for the 1st comment and the 1st node c and its children
it is equal to 2 both for the 2nd comment and the 2nd node c and its children
Do you have any solutions to suggest?
SOLUTION (by Tim)
I can now get the correct output by using the following xslt file:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:for-each select="*">
<xsl:variable name="elm01" select="local-name()" />
<xsl:text>1</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|0| |0| |0|</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm02" select="local-name()" />
<xsl:text>2</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|0| |0|</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm03" select="local-name()" />
<xsl:if test="preceding-sibling::comment()[1]">
<xsl:text>3</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment())"/>
<xsl:text>|!</xsl:text>
<xsl:text>|0|</xsl:text>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:text>3</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment())+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
<xsl:text>|0|</xsl:text>
<xsl:text>
</xsl:text>
<xsl:for-each select="*">
<xsl:variable name="elm04" select="local-name()" />
<xsl:text>4</xsl:text>
<xsl:text>|</xsl:text><xsl:value-of select="count(../../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm01" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../../preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm02" />
<xsl:text>|</xsl:text><xsl:value-of select="count(../preceding-sibling::*|../preceding-sibling::comment())+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm03" />
<xsl:text>|</xsl:text><xsl:value-of select="count(preceding-sibling::*)+1"/>
<xsl:text>|</xsl:text><xsl:copy-of select="$elm04" />
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Alternatively you can also use the xslt in Tim's reply, that gets rid of repetitions.
There expression you want is this...
<xsl:value-of select="count(preceding-sibling::*|preceding-sibling::comment()) + 1" />
Or this would work too...
<xsl:value-of select="count(preceding-sibling::node()[self::*|self::comment()]) + 1" />
But you can also use xsl:number
<xsl:number count="*|comment()" />
Your stylesheet does seem a bit over-complicated though, with much repetition. Try this more generic one instead. This recursively calls each level, passing in the constructed line each call to save having to build it each time.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" />
<xsl:param name="maxLevel" select="4" />
<xsl:template match="*|comment()">
<xsl:param name="level" select="1" />
<xsl:param name="prev" />
<xsl:variable name="new">
<xsl:value-of select="$prev" />
<xsl:text>|</xsl:text>
<xsl:number count="*|comment()" />
<xsl:text>|</xsl:text>
<xsl:choose>
<xsl:when test="self::*">
<xsl:value-of select="local-name()" />
</xsl:when>
<xsl:otherwise>
<xsl:text>!</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$level" />
<xsl:value-of select="$new" />
<xsl:call-template name="pad">
<xsl:with-param name="levels" select="$maxLevel - $level" />
</xsl:call-template>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="*|comment()">
<xsl:with-param name="level" select="$level + 1" />
<xsl:with-param name="prev" select="$new" />
</xsl:apply-templates>
</xsl:template>
<xsl:template name="pad">
<xsl:param name="levels" />
<xsl:if test="$levels > 0">
<xsl:text>|0| </xsl:text>
<xsl:call-template name="pad">
<xsl:with-param name="levels" select="$levels - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
See it in action at http://xsltfiddle.liberty-development.net/jyRYYiy

Need to print different values of Child Node in separate lines in XSLT

I have a requirement to print one row for Each Bonus which employee received along with Salary and Allowances.
Please see the XML from the report -
(Bonus_Assignment is the node which I need to print in different line for each bonus)
<wd:Report_Data xmlns:wd="urn:com.workday.report/XMLNAME_1475b_-_Amit">
<wd:Report_Entry>
<wd:Employee_Compensation_Type_OfferSalary>OfferSalary</wd:Employee_Compensation_Type_OfferSalary>
<wd:Employee_Compensation_Type_OfferBonus>OfferBonus</wd:Employee_Compensation_Type_OfferBonus>
<wd:Allowance_Assignments>
<wd:Employee_Compensation_Type_OfferAllowance>OffersAutoAllowance</wd:Employee_Compensation_Type_OfferAllowance>
<wd:Allowance_Amount>700</wd:Allowance_Amount>
<wd:Flsa_Status_Code_Allowance wd:Descriptor="USA Car Allowance">
<wd:ID wd:type="WID">32bd01e62b27101f0c5d34bc3e398d93</wd:ID>
<wd:ID wd:type="Compensation_Plan_ID">USA_Car_Allowance</wd:ID>
</wd:Flsa_Status_Code_Allowance>
</wd:Allowance_Assignments>
<wd:Candidate_ID>C02271</wd:Candidate_ID>
<wd:Job_Application wd:Descriptor="Stefen Salvatore (Referral) - R00010 Sr Analyst Delivery Specialist_175328BR (C02271)">
<wd:ID wd:type="WID">c820a3cdb761900dcf51dc132e290529</wd:ID>
<wd:ID wd:type="Job_Application_ID">JOB_APPLICATION-6-2270</wd:ID>
</wd:Job_Application>
<wd:Worker_Sub-Type wd:Descriptor="Regular">
<wd:ID wd:type="WID">bc914f6ed6aa101d83de5572d1e46422</wd:ID>
<wd:ID wd:type="Employee_Type_ID">Regular</wd:ID>
</wd:Worker_Sub-Type>
<wd:Job_Application_Offer_Status wd:Descriptor="Offer Accepted">
<wd:ID wd:type="WID">bc914f6ed6aa101e1a6e235e35f5706c</wd:ID>
<wd:ID wd:type="Offer_Status_ID">OFFER_STATUS_Offer_Accepted</wd:ID>
</wd:Job_Application_Offer_Status>
<wd:Job_Requisition>
<wd:Employee_Num>R00010</wd:Employee_Num>
<wd:Currency_Code wd:Descriptor="AUD">
<wd:ID wd:type="WID">8a96ee707a264afe888d90ef93253305</wd:ID>
<wd:ID wd:type="Currency_ID">AUD</wd:ID>
<wd:ID wd:type="Currency_Numeric_Code">036</wd:ID>
</wd:Currency_Code>
<wd:Worker_Type wd:Descriptor="Regular">
<wd:ID wd:type="WID">bc914f6ed6aa101d83de5572d1e46422</wd:ID>
<wd:ID wd:type="Employee_Type_ID">Regular</wd:ID>
</wd:Worker_Type>
<wd:Req_Salary>0</wd:Req_Salary>
<wd:Req_Bonus>0</wd:Req_Bonus>
<wd:Req_Allowance>0</wd:Req_Allowance>
<wd:Bonus_Effective_Date>2017-01-19-08:00</wd:Bonus_Effective_Date>
</wd:Job_Requisition>
<wd:Salary_Assignments>
<wd:Salary_Amount>75000</wd:Salary_Amount>
<wd:Flsa_Status_Code_Salary wd:Descriptor="Base Salary">
<wd:ID wd:type="WID">32bd01e62b27101f0c622c47a20f911a</wd:ID>
<wd:ID wd:type="Compensation_Plan_ID">Base_Salary</wd:ID>
</wd:Flsa_Status_Code_Salary>
</wd:Salary_Assignments>
<wd:Bonus_Assignments>
<wd:Bonus_Amount>11199.56</wd:Bonus_Amount>
<wd:Flsa_Status_Code_Bonus wd:Descriptor="IBP Semi-Annual EMC">
<wd:ID wd:type="WID">32bd01e62b27101f0c6300373ce89192</wd:ID>
<wd:ID wd:type="Compensation_Plan_ID">IBP_Semi-Annual_EMC</wd:ID>
</wd:Flsa_Status_Code_Bonus>
</wd:Bonus_Assignments>
<wd:Bonus_Assignments>
<wd:Bonus_Amount>22399.11</wd:Bonus_Amount>
<wd:Flsa_Status_Code_Bonus wd:Descriptor="IBP Corporate">
<wd:ID wd:type="WID">32bd01e62b27101f0c631f69f96491bd</wd:ID>
<wd:ID wd:type="Compensation_Plan_ID">IBP_Corporate</wd:ID>
</wd:Flsa_Status_Code_Bonus>
</wd:Bonus_Assignments>
</wd:Report_Entry>
</wd:Report_Data>
Current result:
"OfferSalary" R00010 AUD 0 "Base Salary" 75000
"OfferBonus" R00010 AUD "IBP Semi-Annual IBP Corporate" 11199.56 22399.11
"OffersAutoAllowance" R00010 AUD "USA Car Allowance" 700
Expected -
"OfferSalary" R00010 AUD 0 "Base Salary" 75000
"OfferBonus" R00010 AUD "IBP Semi-Annual" 11199.56
"OfferBonus" R00010 AUD "IBP Corporate" 22399.11
"OffersAutoAllowance" R00010 AUD "USA Car Allowance" 700
Current XSLT
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wd="urn:com.workday.report/XMLNAME_1475b_-_Amit" version="1.0">
<xsl:output method="text" encoding="UTF-8" indent="yes" />
<xsl:variable name="Tab" select="' '" />
<xsl:variable name="quote" select="'"'" />
<xsl:variable name="newline" select="'
'" />
<xsl:template match="/*">
<xsl:for-each select="wd:Report_Entry">
<xsl:value-of select="$quote" />
<xsl:choose>
<xsl:when test="wd:Employee_Compensation_Type_OfferSalary">
<xsl:value-of select="wd:Employee_Compensation_Type_OfferSalary" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="Employee_Compensation_Type_TOfferSalary" />
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$quote" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Job_Requisition/wd:Employee_Num" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Job_Requisition/wd:Currency_Code/#wd:Descriptor" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="0" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="$quote" />
<xsl:value-of select="wd:Salary_Assignments/wd:Flsa_Status_Code_Salary/#wd:Descriptor" />
<xsl:value-of select="$quote" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="''" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Salary_Assignments/wd:Salary_Amount" />
<xsl:value-of select="$newline" />
<xsl:if test="wd:Bonus_Assignments/wd:Flsa_Status_Code_Bonus/#wd:Descriptor != ''">
<xsl:value-of select="$quote" />
<xsl:choose>
<xsl:when test="wd:Employee_Compensation_Type_OfferBonus">
<xsl:value-of select="wd:Employee_Compensation_Type_OfferBonus" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="Employee_Compensation_Type_TOfferBonus" />
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$quote" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Job_Requisition/wd:Employee_Num" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Job_Requisition/wd:Currency_Code/#wd:Descriptor" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Bonus_Incl_Excl_Bonus" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="$quote" />
<xsl:value-of select="wd:Bonus_Assignments/wd:Flsa_Status_Code_Bonus/#wd:Descriptor" />
<xsl:value-of select="$quote" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Bonus_Effective_Date" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Bonus_Assignments/wd:Bonus_Amount" />
<xsl:value-of select="$newline" />
</xsl:if>
<xsl:if test="wd:Allowance_Assignments/wd:Flsa_Status_Code_Allowance/#wd:Descriptor != ''">
<xsl:value-of select="$quote" />
<xsl:choose>
<xsl:when test="(wd:Allowance_Assignments/wd:Employee_Compensation_Type_OfferAllowance) and (wd:Job_Requisition/wd:Worker_Type/#wd:Descriptor != 'Fixed Term')">
<xsl:value-of select="wd:Allowance_Assignments/wd:Employee_Compensation_Type_OfferAllowance" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="wd:Allowance_Assignments/Employee_Compensation_Type_TOfferAllowance" />
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$quote" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Job_Requisition/wd:Employee_Num" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Job_Requisition/wd:Currency_Code/#wd:Descriptor" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="''" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Bonus_Incl_Excl_Allowance" />
<xsl:value-of select="$Tab" />
<xsl:value-of select="$quote" />
<xsl:value-of select="wd:Allowance_Assignments/wd:Flsa_Status_Code_Allowance/#wd:Descriptor" />
<xsl:value-of select="$quote" />
<xsl:value-of select="$Tab" />
<xsl:text />
<xsl:value-of select="$Tab" />
<xsl:value-of select="wd:Allowance_Assignments/wd:Allowance_Amount" />
<xsl:value-of select="$newline" />
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:transform>
The trick is to use template matching.
You have multiple wd:Bonus_Assignments elements that each should be transformed into an output line.
That's the perfect situation for an <xsl:apply-templates>/<xsl:template> pair.
Compare (also note the slightly changed relative paths in the templates):
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wd="urn:com.workday.report/XMLNAME_1475b_-_Amit" version="1.0">
<xsl:output method="text" encoding="UTF-8" indent="yes" />
<xsl:variable name="Tab" select="' '" />
<xsl:variable name="quote" select="'"'" />
<xsl:variable name="newline" select="'
'" />
<xsl:template match="/*">
<xsl:apply-templates select="wd:Report_Entry" />
</xsl:template>
<xsl:template match="wd:Report_Entry">
<xsl:value-of select="$quote" />
<xsl:choose>
<xsl:when test="wd:Employee_Compensation_Type_OfferSalary">
<xsl:value-of select="wd:Employee_Compensation_Type_OfferSalary" />
</xsl:when>
<xsl:otherwise>l
<xsl:value-of select="Employee_Compensation_Type_TOfferSalary" />
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$quote" />
<xsl:value-of select="concat($Tab, wd:Job_Requisition/wd:Employee_Num)" />
<xsl:value-of select="concat($Tab, wd:Job_Requisition/wd:Currency_Code/#wd:Descriptor)" />
<xsl:value-of select="concat($Tab, 0)" />
<xsl:value-of select="concat($Tab, $quote, wd:Salary_Assignments/wd:Flsa_Status_Code_Salary/#wd:Descriptor, $quote)" />
<xsl:value-of select="concat($Tab, '')" />
<xsl:value-of select="concat($Tab, wd:Salary_Assignments/wd:Salary_Amount)" />
<xsl:value-of select="$newline" />
<!-- here ****************************************************** -->
<xsl:apply-templates select="wd:Bonus_Assignments[wd:Flsa_Status_Code_Bonus/#wd:Descriptor != '']" />
<xsl:apply-templates select="wd:Allowance_Assignments[wd:Flsa_Status_Code_Allowance/#wd:Descriptor != '']" />
</xsl:template>
<xsl:template match="wd:Bonus_Assignments">
<xsl:value-of select="$quote" />
<xsl:choose>
<xsl:when test="../wd:Employee_Compensation_Type_OfferBonus">
<xsl:value-of select="../wd:Employee_Compensation_Type_OfferBonus" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="../Employee_Compensation_Type_TOfferBonus" />
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$quote" />
<xsl:value-of select="concat($Tab, ../wd:Job_Requisition/wd:Employee_Num)" />
<xsl:value-of select="concat($Tab, ../wd:Job_Requisition/wd:Currency_Code/#wd:Descriptor)" />
<xsl:value-of select="concat($Tab, ../wd:Bonus_Incl_Excl_Bonus)" />
<xsl:value-of select="concat($Tab, $quote, wd:Flsa_Status_Code_Bonus/#wd:Descriptor, $quote)" />
<xsl:value-of select="concat($Tab, ../wd:Bonus_Effective_Date)" />
<xsl:value-of select="concat($Tab, wd:Bonus_Amount)" />
<xsl:value-of select="$newline" />
</xsl:template>
<xsl:template match="wd:Allowance_Assignments">
<xsl:value-of select="$quote" />
<xsl:choose>
<xsl:when test="wd:Employee_Compensation_Type_OfferAllowance and ../wd:Job_Requisition/wd:Worker_Type/#wd:Descriptor != 'Fixed Term'">
<xsl:value-of select="wd:Employee_Compensation_Type_OfferAllowance" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="wd:Employee_Compensation_Type_TOfferAllowance" />
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$quote" />
<xsl:value-of select="concat($Tab, ../wd:Job_Requisition/wd:Employee_Num)" />
<xsl:value-of select="concat($Tab, ../wd:Job_Requisition/wd:Currency_Code/#wd:Descriptor)" />
<xsl:value-of select="concat($Tab, ../wd:Bonus_Incl_Excl_Allowance)" />
<xsl:value-of select="concat($Tab, $quote, wd:Flsa_Status_Code_Allowance/#wd:Descriptor, $quote)" />
<xsl:value-of select="concat($Tab, '')" />
<xsl:value-of select="concat($Tab, wd:Allowance_Amount)" />
<xsl:value-of select="$newline" />
</xsl:template>
</xsl:transform>
Result:
"OfferSalary" R00010 AUD 0 "Base Salary" 75000
"OfferBonus" R00010 AUD "IBP Semi-Annual EMC" 11199.56
"OfferBonus" R00010 AUD "IBP Corporate" 22399.11
"OffersAutoAllowance" R00010 AUD "USA Car Allowance" 700

How can I "reuse" code in XSL

I'm using XSLT node. My question is about XSL. I have searched on Internet, but I've only found info about XML processing for show it on a web page. Info I'm searching is how "reuse" some code in this scenario:
My XSL is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsb="http://tempuri.org/">
<xsl:import href="HubGenerico.xsl" />
<xsl:template match="/RCECSTD2">
<xsl:element name="soap:Envelope">
<xsl:element name="soap:Header" />
<xsl:element name="soap:Body" />
<xsl:choose>
<xsl:when test="STDR2_DATOS_CONTROL/STDR2_CODOPE = 1010">
<xsl:element name="wsb:ConsultarSC">
<xsl:element name="wsb:codigoOperacion">
<xsl:value-of select="STDR2_DATOS_CONTROL/STDR2_CODOPE" />
</xsl:element>
<xsl:element name="wsb:numeroOperacion">
<xsl:value-of select="STDR2_DATOS_CONTROL/STDR2_NUMOPEBCO" />
</xsl:element>
<xsl:element name="wsb:codigoBanco">
<xsl:value-of select="STDR2_DATOS_EMPRESA/STDR2_CODBCO" />
</xsl:element>
<xsl:element name="wsb:codigoConvenio">
<xsl:value-of select="STDR2_DATOS_EMPRESA/STDR2_CODCONTR" />
</xsl:element>
<xsl:element name="wsb:numeroReferenciaDeuda">
<xsl:value-of
select="normalize-space(STDR2_DATOS_TRANSACCION/STDR2_NUMREF_DEUDA)" />
</xsl:element>
<xsl:element name="wsb:canalOperacion">
<xsl:value-of select="STDR2_DATOS_OPERACION/STDR2_CANAL" />
</xsl:element>
<xsl:element name="wsb:codigoOficina">
<xsl:value-of select="STDR2_DATOS_OPERACION/STDR2_CODOFI" />
</xsl:element>
<xsl:element name="wsb:fechaOperacion">
<xsl:value-of select="STDR2_DATOS_OPERACION/STDR2_FECOPE" />
</xsl:element>
<xsl:element name="wsb:horaOperacion">
<xsl:value-of select="STDR2_DATOS_OPERACION/STDR2_HOROPE" />
</xsl:element>
<xsl:element name="wsb:datosEmpresa" />
</xsl:element>
</xsl:when>
<xsl:when test="STDR2_DATOS_CONTROL/STDR2_CODOPE = 2010">
<xsl:element name="wsb:NotificarPago">
<xsl:element name="wsb:codigoOperacion">
<xsl:value-of select="STDR2_DATOS_CONTROL/STDR2_CODOPE" />
</xsl:element>
<xsl:element name="wsb:numeroOperacion">
<xsl:value-of select="STDR2_DATOS_CONTROL/STDR2_NUMOPEBCO" />
</xsl:element>
<xsl:element name="wsb:codigoBanco">
<xsl:value-of select="STDR2_DATOS_EMPRESA/STDR2_CODBCO" />
</xsl:element>
<xsl:element name="wsb:codigoConvenio">
<xsl:value-of select="STDR2_DATOS_EMPRESA/STDR2_CODCONTR" />
</xsl:element>
<xsl:element name="wsb:otrosDatosEmpresa" />
<xsl:element name="wsb:numeroReferenciaDeuda">
<xsl:value-of
select="normalize-space(STDR2_DATOS_TRANSACCION/STDR2_NUMREF_DEUDA)" />
</xsl:element>
<xsl:element name="wsb:NumDocDeuda">
<xsl:text>1</xsl:text>
</xsl:element>
<xsl:element name="wsb:formaPago">
<xsl:value-of select="STDR2_DATOS_TRANSACCION/STDR2_FORPAG" />
</xsl:element>
<xsl:element name="wsb:codigoMoneda">
<xsl:value-of select="STDR2_DATOS_TRANSACCION/STDR2_CODMON" />
</xsl:element>
<xsl:element name="wsb:importeTotalPagado">
<xsl:variable name="importeTotalPagado">
<xsl:call-template name="string-replace">
<xsl:with-param name="text"
select="string(STDR2_DATOS_TRANSACCION/STDR2_IMPTOT_PAG)" />
<xsl:with-param name="pattern" select="'.'" />
<xsl:with-param name="replace-with" select="''" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$importeTotalPagado" />
</xsl:element>
<xsl:element name="wsb:detallePagoBBVA" />
<xsl:element name="wsb:canalOperacion">
<xsl:value-of select="STDR2_DATOS_OPERACION/STDR2_CANAL" />
</xsl:element>
<xsl:element name="wsb:codigoOficina">
<xsl:value-of select="STDR2_DATOS_OPERACION/STDR2_CODOFI" />
</xsl:element>
<xsl:element name="wsb:fechaOperacion">
<xsl:value-of select="STDR2_DATOS_OPERACION/STDR2_FECOPE" />
</xsl:element>
<xsl:element name="wsb:horaOperacion">
<xsl:value-of select="STDR2_DATOS_OPERACION/STDR2_HOROPE" />
</xsl:element>
<xsl:element name="wsb:datosEmpresa" />
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
You see that some created elements are "repeated" (same code) but they have differents parents (ConsultarSC and NotificarPago). So my question is how can I reuse that "repeated code", I don't if exists some "tag" that can I use how a "reference" (like ESQL).
In your example, "named" templates are you friend here. For example, if you wanted to create the same two elements in different places, create a named template like so
<xsl:template name="exampleTemplate">
<xsl:element name="wsb:codigoOperacion">
<xsl:value-of select="STDR2_DATOS_CONTROL/STDR2_CODOPE" />
</xsl:element>
<xsl:element name="wsb:numeroOperacion">
<xsl:value-of select="STDR2_DATOS_CONTROL/STDR2_NUMOPEBCO" />
</xsl:element>
</xsl:template>
Then, to create these two elements, just call the template like so:
<xsl:call-template name="exampleTemplate" />
And that's it!
Note, just like functions, you can pass parameters to templates too.
<xsl:template name="example">
<xsl:param name="value" />
Value is <xsl:value-of select="$value" />
</xsl:template>
<xsl:call-template name="example">
<xsl:with-param name="value" select="element" />
</xsl:call-template>
As an aside, there is no need to use xsl:element to create an element if you are using a static name, just write out element name directly. So, instead of this
<xsl:element name="wsb:numeroOperacion">
<xsl:value-of select="STDR2_DATOS_CONTROL/STDR2_NUMOPEBCO" />
</xsl:element>
Just do this
<wsb:numeroOperacion>
<xsl:value-of select="STDR2_DATOS_CONTROL/STDR2_NUMOPEBCO" />
</wsb:numeroOperacion>
Since your output follows the same pattern with some extra stuff added in certain situations, you can have one template for the main layout, and then call off to other conditional templates for the extra stuff:
<xsl:import href="HubGenerico.xsl" />
<xsl:template match="/RCECSTD2">
<soap:Envelope>
<soap:Header />
<soap:Body />
<xsl:apply-templates select="STDR2_DATOS_CONTROL/STDR2_CODOPE" />
</soap:Envelope>
</xsl:template>
<!-- Override the default handling of text nodes -->
<xsl:template match="text()" />
<xsl:template match="STDR2_CODOPE[. = 1010]">
<wsb:ConsultarSC>
<xsl:call-template name="Content" />
</wsb:ConsultarSC>
</xsl:template>
<xsl:template match="STDR2_CODOPE[. = 2010]">
<wsb:NotificarPago>
<xsl:call-template name="Content" />
</wsb:NotificarPago>
</xsl:template>
<xsl:template name="Content">
<xsl:call-template name="Basic" />
<xsl:apply-templates select="." mode="otrosDatos" />
<xsl:call-template name="NumeroDeuda" />
<xsl:apply-templates select="." mode="deudaPagado" />
<xsl:call-template name="Operacion" />
</xsl:template>
<!-- Context node is STDR2_CODOPE -->
<xsl:template name="Basic">
<xsl:variable name="empresa" select="../../STDR2_DATOS_EMPRESA" />
<wsb:codigoOperacion>
<xsl:value-of select="."/>
</wsb:codigoOperacion>
<wsb:numeroOperacion>
<xsl:value-of select="../STDR_NUMOPEBCO" />
</wsb:numeroOperacion>
<wsb:codigoBanco>
<xsl:value-of select="$empresa/STDR2_CODBCO"/>
</wsb:codigoBanco>
<wsb:codigoConvenio>
<xsl:value-of select="$empresa/STDR2_CODCONTR"/>
</wsb:codigoConvenio>
</xsl:template>
<xsl:template name="NumeroDeuda">
<wsb:numeroReferenciaDeuda>
<xsl:value-of
select="normalize-space(../../STDR2_DATOS_TRANSACCION/STDR2_NUMREF_DEUDA)"/>
</wsb:numeroReferenciaDeuda>
</xsl:template>
<xsl:template name="Operacion">
<xsl:variable name="operacion" select="../../STDR2_DATOS_OPERACION" />
<wsb:canalOperacion>
<xsl:value-of select="$operacion/STDR2_CANAL" />
</wsb:canalOperacion>
<wsb:codigoOficina>
<xsl:value-of select="$operacion/STDR2_CODOFI" />
</wsb:codigoOficina>
<wsb:fechaOperacion>
<xsl:value-of select="$operacion/STDR2_FECOPE" />
</wsb:fechaOperacion>
<wsb:horaOperacion>
<xsl:value-of select="$operacion/STDR2_HOROPE" />
</wsb:horaOperacion>
<wsb:datosEmpresa />
</xsl:template>
<xsl:template match="STDR2_CODOPE[. = 2010]" mode="otrosDatos">
<wsb:otrosDatosEmpresa />
</xsl:template>
<xsl:template match="STDR2_CODOPE[. = 2010]" mode="deudaPagado">
<xsl:variable name="transaccion" select="../../STDR2_DATOS_TRANSACCION" />
<wsb:NumDocDeuda>1</wsb:NumDocDeuda>
<wsb:formaPago>
<xsl:value-of select="$transaccion/STDR2_FORPAG" />
</wsb:formaPago>
<wsb:codigoMoneda>
<xsl:value-of select="$transaccion/STDR2_CODMON" />
</wsb:codigoMoneda>
<wsb:importeTotalPagado>
<xsl:call-template name="string-replace">
<xsl:with-param name="text"
select="string($transaccion/STDR2_IMPTOT_PAG)" />
<xsl:with-param name="pattern" select="'.'" />
<xsl:with-param name="replace-with" select="''" />
</xsl:call-template>
</wsb:importeTotalPagado>
<wsb:detallePagoBBVA />
</xsl:template>
Thanks "JLRishe" and "Tim C". My XSL files result in:
Principal:
`
<xsl:import href="HubGenerico.xsl" />
<xsl:variable name="myns" select="'http://tempuri.org/'" />
<xsl:template match="/RCECSTD2">
<soap:Envelope>
<soap:Header />
<soap:Body />
<xsl:variable name="codOperacion" select="STDR2_DATOS_CONTROL/STDR2_CODOPE" />
<xsl:choose>
<xsl:when test="$codOperacion = 1010">
<wsb:ConsultarSC xmlns:wsb="'http://tempuri.org/'">
<xsl:call-template name="STDR2_DATOS_CONTROL">
<xsl:with-param name="ns" select="$myns" />
</xsl:call-template>
<xsl:call-template name="STDR2_DATOS_EMPRESA">
<xsl:with-param name="ns" select="$myns" />
</xsl:call-template>
<wsb:numeroReferenciaDeuda>
<xsl:value-of
select="normalize-space(STDR2_DATOS_TRANSACCION/STDR2_NUMREF_DEUDA)" />
</wsb:numeroReferenciaDeuda>
<xsl:call-template name="STDR2_DATOS_OPERACION">
<xsl:with-param name="ns" select="$myns" />
</xsl:call-template>
<wsb:datosEmpresa />
</wsb:ConsultarSC>
</xsl:when>
<xsl:when test="$codOperacion = 2010">
<wsb:NotificarPago xmlns:wsb="$ns">
<xsl:call-template name="STDR2_DATOS_CONTROL">
<xsl:with-param name="ns" select="$myns" />
</xsl:call-template>
<xsl:call-template name="STDR2_DATOS_EMPRESA">
<xsl:with-param name="ns" select="$myns" />
</xsl:call-template>
<wsb:otrosDatosEmpresa />
<wsb:numeroReferenciaDeuda>
<xsl:value-of
select="normalize-space(STDR2_DATOS_TRANSACCION/STDR2_NUMREF_DEUDA)" />
</wsb:numeroReferenciaDeuda>
<wsb:NumDocDeuda>
<xsl:text>1</xsl:text>
</wsb:NumDocDeuda>
<wsb:formaPago>
<xsl:value-of select="STDR2_DATOS_TRANSACCION/STDR2_FORPAG" />
</wsb:formaPago>
<wsb:codigoMoneda>
<xsl:value-of select="STDR2_DATOS_TRANSACCION/STDR2_CODMON" />
</wsb:codigoMoneda>
<wsb:importeTotalPagado>
<xsl:variable name="importeTotalPagado">
<xsl:call-template name="string-replace">
<xsl:with-param name="text"
select="string(STDR2_DATOS_TRANSACCION/STDR2_IMPTOT_PAG)" />
<xsl:with-param name="pattern" select="'.'" />
<xsl:with-param name="replace-with" select="''" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$importeTotalPagado" />
</wsb:importeTotalPagado>
<wsb:detallePagoBBVA />
<xsl:call-template name="STDR2_DATOS_OPERACION">
<xsl:with-param name="ns" select="$myns" />
</xsl:call-template>
<wsb:datosEmpresa />
</wsb:NotificarPago>
</xsl:when>
</xsl:choose>
</soap:Envelope>
</xsl:template>
`
Imported XSL:
`
<xsl:template name="string-replace">
<xsl:param name="text" />
<xsl:param name="pattern" />
<xsl:param name="replace-with" />
<xsl:choose>
<xsl:when test="contains($text, $pattern)">
<xsl:value-of select="substring-before($text, $pattern)" />
<xsl:value-of select="$replace-with" />
<xsl:call-template name="string-replace">
<xsl:with-param name="text"
select="substring-after($text, $pattern)" />
<xsl:with-param name="pattern" select="$pattern" />
<xsl:with-param name="replace-with" select="$replace-with" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Datos de Control -->
<xsl:template name="STDR2_DATOS_CONTROL">
<xsl:param name="ns" />
<xsl:variable name="datControl" select="STDR2_DATOS_CONTROL" />
<xsl:element name="wsb:codigoOperacion" namespace="{$ns}">
<xsl:value-of select="$datControl/STDR2_CODOPE" />
</xsl:element>
<xsl:element name="wsb:numeroOperacion" namespace="{$ns}">
<xsl:value-of select="$datControl/STDR2_NUMOPEBCO" />
</xsl:element>
</xsl:template>
<!-- Datos de Empresa -->
<xsl:template name="STDR2_DATOS_EMPRESA">
<xsl:param name="ns" />
<xsl:variable name="datEmpresa" select="STDR2_DATOS_EMPRESA" />
<xsl:element name="wsb:codigoBanco" namespace="{$ns}">
<xsl:value-of select="$datEmpresa/STDR2_CODBCO" />
</xsl:element>
<xsl:element name="wsb:codigoConvenio" namespace="{$ns}">
<xsl:value-of select="$datEmpresa/STDR2_CODCONTR" />
</xsl:element>
</xsl:template>
<!-- Datos de Operacion -->
<xsl:template name="STDR2_DATOS_OPERACION">
<xsl:param name="ns" />
<xsl:variable name="datOperacion" select="STDR2_DATOS_OPERACION" />
<xsl:element name="wsb:canalOperacion" namespace="{$ns}">
<xsl:value-of select="$datOperacion/STDR2_CANAL" />
</xsl:element>
<xsl:element name="wsb:codigoOficina" namespace="{$ns}">
<xsl:value-of select="$datOperacion/STDR2_CODOFI" />
</xsl:element>
<xsl:element name="wsb:fechaOperacion" namespace="{$ns}">
<xsl:value-of select="$datOperacion/STDR2_FECOPE" />
</xsl:element>
<xsl:element name="wsb:horaOperacion" namespace="{$ns}">
<xsl:value-of select="$datOperacion/STDR2_HOROPE" />
</xsl:element>
</xsl:template>
`

Extreme XSLT XML Flattening

I have the following input XML file:
<root>
<a>
<b>1</b>
</a>
<c>
<d>
<e>2</e>
<f>3</f> or <e>3</e>
</d>
<g h="4"/>
<i>
<j>
<k>
<l m="5" n="6" o="7" />
<l m="8" n="9" o="0" />
</k>
</j>
</i>
</c>
</root>
I would like to use XSLT to transform it into the follow outputs:
OUTPUT 1
<root>
<row b="1" e="2" f="3" h="4" m="5" n="6" o="7" />
<row b="1" e="2" f="3" h="4" m="8" n="9" o="0" />
<root>
OUTPUT 2
<root>
<row b="1" e="2" h="4" m="5" n="6" o="7" />
<row b="1" e="2" h="4" m="8" n="9" o="0" />
<row b="1" e="3" h="4" m="5" n="6" o="7" />
<row b="1" e="3" h="4" m="8" n="9" o="0" />
<root>
Can anyone help my XSLT isn't very strong. Thanks.
It will be easier if you let the occurrence of <e> determine the outer loop constructing your <row>s and have a inner loop iterating over all <l>s.
Try something like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="root">
<root>
<xsl:apply-templates />
</root>
</xsl:template>
<xsl:template match="e">
<!-- store values of 'e' and 'f' in params -->
<xsl:param name="value_of_e" select="." />
<xsl:param name="value_of_f" select="ancestor::d[1]/f" />
<!-- iterate over all 'l's -->
<xsl:for-each select="//l">
<xsl:element name="row">
<xsl:attribute name="b">
<xsl:value-of select="//b" />
</xsl:attribute>
<xsl:attribute name="e">
<xsl:value-of select="$value_of_e" />
</xsl:attribute>
<!-- only include 'f' if it has a value -->
<xsl:if test="$value_of_f != ''">
<xsl:attribute name="f">
<xsl:value-of select="$value_of_f" />
</xsl:attribute>
</xsl:if>
<xsl:attribute name="h">
<xsl:value-of select="ancestor::c[1]/g/#h" />
</xsl:attribute>
<xsl:attribute name="m">
<xsl:value-of select="./#m" />
</xsl:attribute>
<xsl:attribute name="n">
<xsl:value-of select="./#n" />
</xsl:attribute>
<xsl:attribute name="o">
<xsl:value-of select="./#o" />
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="b" />
<xsl:template match="f" />
<xsl:template match="g" />
</xsl:stylesheet>

How do I Emit Escaped XML representation of a Node in my XSLT's HTML Output

I'm transforming XML to an HTML document. In this document I want to embed XML markup for a node that was just transformed (the HTML document is a technical spec).
For example, if my XML was this:
<transform-me>
<node id="1">
<stuff type="floatsam">
<details>Various bits</details>
</stuff>
</node>
</transform-me>
I'd want my XSLT output to look something like this:
<h1>Node 1</h1>
<h2>Stuff (floatsam)</h2>
Various bits
<h2>The XML</h2>
<stuff type="floatsam">
<details>Various bits</details>
</stuff>
I'm hoping there is an XSLT function that I can call in my <stuff> template to which I can pass the current node (.) and get back escaped XML markup for <stuff> and all its descendants. I have a feeling unparsed-text() might be the way to go but can't get it to work.
Very simple template
<xsl:template match="node()" mode="print">
<xsl:choose>
<!-- is it element? -->
<xsl:when test="name()">
<!-- start tag -->
<xsl:text><</xsl:text>
<xsl:value-of select="name()" />
<!-- attributes -->
<xsl:apply-templates select="#*" mode="print" />
<xsl:choose>
<!-- has children -->
<xsl:when test="node()">
<!-- closing bracket -->
<xsl:text>></xsl:text>
<!-- children -->
<xsl:apply-templates mode="print" />
<!-- end tag -->
<xsl:text></</xsl:text>
<xsl:value-of select="name()" />
<xsl:text>></xsl:text>
</xsl:when>
<!-- is empty -->
<xsl:otherwise>
<!-- closing bracket -->
<xsl:text>/></xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<!-- text -->
<xsl:otherwise>
<xsl:copy />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*" mode="print">
<xsl:text> </xsl:text>
<xsl:value-of select="name()" />
<xsl:text>="</xsl:text>
<xsl:value-of select="." />
<xsl:text>"</xsl:text>
</xsl:template>
Used
<xsl:apply-templates mode="print" />
You can even add pretty printing if you want.
The answer to this is more complex that you would at first think. Proper "double-escaping" of attribute values and text nodes must occur.
This XSLT 1.0 template does a correct (though not complete) printing of an XML node, including (an attempt to do) proper pretty-printing with configurable indentation:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="html" encoding="utf-8" />
<!-- defaults and configurable parameters -->
<xsl:param name="NL" select="'
'" /><!-- newline sequence -->
<xsl:param name="INDENTSEQ" select="' '" /><!-- indent sequence -->
<xsl:variable name="LT" select="'<'" />
<xsl:variable name="GT" select="'>'" />
<xsl:template match="transform-me">
<html>
<body>
<!-- this XML-escapes an entire sub-structure -->
<pre><xsl:apply-templates select="*" mode="XmlEscape" /></pre>
</body>
</html>
</xsl:template>
<!-- element nodes will be handled here, incl. proper indenting -->
<xsl:template match="*" mode="XmlEscape">
<xsl:param name="indent" select="''" />
<xsl:value-of select="concat($indent, $LT, name())" />
<xsl:apply-templates select="#*" mode="XmlEscape" />
<xsl:variable name="HasChildNode" select="node()[not(self::text())]" />
<xsl:variable name="HasChildText" select="text()[normalize-space()]" />
<xsl:choose>
<xsl:when test="$HasChildNode or $HasChildText">
<xsl:value-of select="$GT" />
<xsl:if test="not($HasChildText)">
<xsl:value-of select="$NL" />
</xsl:if>
<!-- render child nodes -->
<xsl:apply-templates mode="XmlEscape" select="node()">
<xsl:with-param name="indent" select="concat($INDENTSEQ, $indent)" />
</xsl:apply-templates>
<xsl:if test="not($HasChildText)">
<xsl:value-of select="$indent" />
</xsl:if>
<xsl:value-of select="concat($LT, '/', name(), $GT, $NL)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' /', $GT, $NL)" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- comments will be handled here -->
<xsl:template match="comment()" mode="XmlEscape">
<xsl:param name="indent" select="''" />
<xsl:value-of select="concat($indent, $LT, '!--', ., '--', $GT, $NL)" />
</xsl:template>
<!-- text nodes will be printed XML-escaped -->
<xsl:template match="text()" mode="XmlEscape">
<xsl:if test="not(normalize-space() = '')">
<xsl:call-template name="XmlEscapeString">
<xsl:with-param name="s" select="." />
<xsl:with-param name="IsAttribute" select="false()" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- attributes become a string: '{name()}="{escaped-value()}"' -->
<xsl:template match="#*" mode="XmlEscape">
<xsl:value-of select="concat(' ', name(), '="')" />
<xsl:call-template name="XmlEscapeString">
<xsl:with-param name="s" select="." />
<xsl:with-param name="IsAttribute" select="true()" />
</xsl:call-template>
<xsl:value-of select="'"'" />
</xsl:template>
<!-- template to XML-escape a string -->
<xsl:template name="XmlEscapeString">
<xsl:param name="s" select="''" />
<xsl:param name="IsAttribute" select="false()" />
<!-- chars &, < and > are never allowed -->
<xsl:variable name="step1">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$s" />
<xsl:with-param name="search" select="'&'" />
<xsl:with-param name="replace" select="'&amp;'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="step2">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step1" />
<xsl:with-param name="search" select="'<'" />
<xsl:with-param name="replace" select="'&lt;'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="step3">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step2" />
<xsl:with-param name="search" select="'>'" />
<xsl:with-param name="replace" select="'&gt;'" />
</xsl:call-template>
</xsl:variable>
<!-- chars ", TAB, CR and LF are never allowed in attributes -->
<xsl:choose>
<xsl:when test="$IsAttribute">
<xsl:variable name="step4">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step3" />
<xsl:with-param name="search" select="'"'" />
<xsl:with-param name="replace" select="'&quot;'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="step5">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step4" />
<xsl:with-param name="search" select="' '" />
<xsl:with-param name="replace" select="'&#x9;'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="step6">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step5" />
<xsl:with-param name="search" select="'
'" />
<xsl:with-param name="replace" select="'&#xD;'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="step7">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$step6" />
<xsl:with-param name="search" select="'
'" />
<xsl:with-param name="replace" select="'&#xD;'" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$step7" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$step3" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- generic string replace template -->
<xsl:template name="StringReplace">
<xsl:param name="s" select="''" />
<xsl:param name="search" select="''" />
<xsl:param name="replace" select="''" />
<xsl:choose>
<xsl:when test="contains($s, $search)">
<xsl:value-of select="substring-before($s, $search)" />
<xsl:value-of select="$replace" />
<xsl:variable name="rest" select="substring-after($s, $search)" />
<xsl:if test="$rest">
<xsl:call-template name="StringReplace">
<xsl:with-param name="s" select="$rest" />
<xsl:with-param name="search" select="$search" />
<xsl:with-param name="replace" select="$replace" />
</xsl:call-template>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$s" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When applied to this test XML:
<transform-me>
<node id="1">
<!-- a comment -->
<stuff type="fl"&apos;
oatsam">
<details>Various bits & pieces</details>
<details>
</details>
<details attr="value">
<childnode>text and <escaped-text /></childnode>
</details>
</stuff>
</node>
</transform-me>
The following output is produced (source code):
<html>
<body>
<pre><node id="1">
<!-- a comment -->
<stuff type="fl&quot;'&#xD;&#x9;oatsam">
<details>Various bits &amp; pieces</details>
<details />
<details attr="value">
<childnode>text and &lt;escaped-text /&lt;</childnode>
</details>
</stuff>
</node>
</pre>
</body>
</html>
and when viewed in the browser you see:
<node id="1">
<!-- a comment -->
<stuff type="fl"'
 oatsam">
<details>Various bits & pieces</details>
<details />
<details attr="value">
<childnode>text and <escaped-text /<</childnode>
</details>
</stuff>
</node>
Note that by "not complete" I mean that things like namespaces and processing instructions for example are currently unhandled. But its easy to make a template for them.