I am trying to transform data using XSLT with element name used as an attribute and then group by it. I also need to reset the position value based on the occurrence of the element. I can't figure out how to group by element name when performing for-each Request:
<SOH>
<SOH3_4>
<DIE>A</DIE>
<NAMDIE>string</NAMDIE>
<CCE>string</CCE>
</SOH3_4>
<SOH3_4>
<DIE>AB</DIE>
<NAMDIE>string</NAMDIE>
<CCE>string</CCE>
</SOH3_4>
<SOH3_5>
<SHO>string</SHO>
<INVDTAAMT>1873960.2349058</INVDTAAMT>
<INVDTATYP>03:ENG:%</INVDTATYP>
<SFISSTCOD>string</SFISSTCOD>
</SOH3_5>
<SOH3_5>
<SHO>string</SHO>
<INVDTAAMT>2280630.2349058</INVDTAAMT>
<INVDTATYP>01:ENG:Tax excluded</INVDTATYP>
<SFISSTCOD>string</SFISSTCOD>
</SOH3_5>
Result:
<PARAM>
<TAB ID="SOH3_4">
<LIN ID="1">
<FLD NAME="DIE">A</FLD>
<FLD NAME="NAMDIE">string</FLD>
<FLD NAME="CCE">string</FLD>
</LIN>
<LIN ID="2">
<FLD NAME="DIE">AB</FLD>
<FLD NAME="NAMDIE">string</FLD>
<FLD NAME="CCE">string</FLD>
</LIN>
</TAB>
<TAB ID="SOH3_5">
<LIN ID="1">
<FLD NAME="SHO">string</FLD>
<FLD NAME="INVDTAAMT">1873960.2349058</FLD>
<FLD NAME="INVDTATYP">03:ENG:%</FLD>
<FLD NAME="INVDTAAMT">string</FLD>
</LIN>
<LIN ID="2">
<FLD NAME="SHO">string</FLD>
<FLD NAME="INVDTAAMT">2280630.2349058</FLD>
<FLD NAME="INVDTATYP">01:ENG:Tax excluded</FLD>
<FLD NAME="SFISSTCOD">string</FLD>
</LIN>
</TAB>
CodePudding user response:
Your question is a bit unclear about the sort order, but you could use the following template to get the desired output:
<xsl:template match="SOH">
<xsl:element name="TAB">
<xsl:attribute name="ID"><xsl:value-of select="name(*[1])" /></xsl:attribute>
<xsl:for-each select="*">
<LIN ID="{position()}">
<xsl:for-each select="*">
<xsl:element name="FLD">
<xsl:attribute name="NAME"><xsl:value-of select="name()" /></xsl:attribute>
<xsl:value-of select="." />
</xsl:element>
</xsl:for-each>
</LIN>
</xsl:for-each>
</xsl:element>
</xsl:template>
The output is as desired
<TAB ID="SOH3_5">
<LIN ID="1">
<FLD NAME="SHO">string</FLD>
<FLD NAME="INVDTAAMT">1873960.2349058</FLD>
<FLD NAME="INVDTATYP">03:ENG:%</FLD>
<FLD NAME="SFISSTCOD">string</FLD>
</LIN>
<LIN ID="2">
<FLD NAME="SHO">string</FLD>
<FLD NAME="INVDTAAMT">2280630.2349058</FLD>
<FLD NAME="INVDTATYP">01:ENG:Tax excluded</FLD>
<FLD NAME="SFISSTCOD">string</FLD>
</LIN>
</TAB>
CodePudding user response:
Your edit suggests you could benefit from using XSLT 2 or 3 and for-each-group select="*" group-adjacent="node-name()"
e.g. in XSLT 3 (supported with SaxonJS2, Saxon 9.8 HE or later/higher, SaxonC, SaxonCS, AltovaXML 2017 R3 and later) it boils down to:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="SOH">
<PARAM>
<xsl:for-each-group select="*" group-adjacent="node-name()">
<TAB ID="{current-grouping-key()}">
<xsl:apply-templates select="current-group()"/>
</TAB>
</xsl:for-each-group>
</PARAM>
</xsl:template>
<xsl:template match="SOH/*">
<LIN ID="{position()}">
<xsl:apply-templates/>
</LIN>
</xsl:template>
<xsl:template match="SOH/*/*">
<FLD NAME="{local-name()}">{.}</FLD>
</xsl:template>
</xsl:stylesheet>