XSLT Parent child Organization Hierarchy not working properly - xslt

I have an XML structure with an organization hierarchy. The <pkEntity> element is a parent ID of org and <entityParent> is the child of org. I don't know the depth of the parent/child combination. I need to transform in below mentioned state:
<EntityDimCollection>
<EntityDim>
<pkEntity>-9</pkEntity>
<entityParent>-7</entityParent>
<entityCode>Own_CP</entityCode>
<entityType>OT</entityType>
<essEntityCode>un.Own_CP</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>-8</pkEntity>
<entityParent>-7</entityParent>
<entityCode>Alternatives</entityCode>
<entityType>OT</entityType>
<essEntityCode>un.Alternatives</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>8555</pkEntity>
<entityParent>-8</entityParent>
<entityCode>Ex_BABRO</entityCode>
<entityType>CF</entityType>
<essEntityCode>un.Ex_BABRO</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>8752</pkEntity>
<entityParent>-8</entityParent>
<entityCode>Ex_SY</entityCode>
<entityType>CF</entityType>
<essEntityCode>un.Ex_SY</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>9731</pkEntity>
<entityParent>-8</entityParent>
<entityCode>NOR</entityCode>
<entityType>LE</entityType>
<essEntityCode>un.NOR</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>1649940</pkEntity>
<entityParent>9731</entityParent>
<entityCode>NO</entityCode>
<entityType>CG</entityType>
<essEntityCode>un.NOR.NO</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>1838293</pkEntity>
<entityParent>1649940</entityParent>
<entityCode>UKONORWAY</entityCode>
<entityType>CG</entityType>
<essEntityCode>un.NOR.UKONORWAY</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>1127251</pkEntity>
<entityParent>1838293</entityParent>
<entityCode>2BUS</entityCode>
<entityType>CG</entityType>
<essEntityCode>un.NOR.2BUS</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>1127274</pkEntity>
<entityParent>1127251</entityParent>
<entityCode>3BUS_B</entityCode>
<entityType>CG</entityType>
<essEntityCode>un.NOR.3BUS_B</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>1988187</pkEntity>
<entityParent>1127274</entityParent>
<entityCode>4BUS_B</entityCode>
<entityType>CG</entityType>
<essEntityCode>un.NOR.4BUS_B</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>1988188</pkEntity>
<entityParent>1988187</entityParent>
<entityCode>5ADM_B</entityCode>
<entityType>CG</entityType>
<essEntityCode>un.NOR.5ADM_B</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>1988189</pkEntity>
<entityParent>1988187</entityParent>
<entityCode>5FVK_B</entityCode>
<entityType>CG</entityType>
<essEntityCode>un.NOR.5FVK_B</essEntityCode>
</EntityDim>
<EntityDim>
<pkEntity>1988190</pkEntity>
<entityParent>1988189</entityParent>
<entityCode>61_FVK_B</entityCode>
<entityType>CG</entityType>
<essEntityCode>un.NOR.61_FVK_B</essEntityCode>
</EntityDim>
</EntityDimCollection>
and with Mark Veenstra solution I got output like this
<?xml version="1.0" encoding="UTF-8"?>
<client:LMSDetails xmlns:client="http://foo/bar">
<client:ParentID>-9</client:ParentID>
<client:ChildID>-7</client:ChildID>
<client:name/>
<client:identifier>OT</client:identifier>
<client:isActive/>
<client:ParentID>-8</client:ParentID>
<client:ChildID>-7</client:ChildID>
<client:name/>
<client:identifier>OT</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>8555</client:ParentID>
<client:ChildID>-8</client:ChildID>
<client:name/>
<client:identifier>CF</client:identifier>
<client:isActive/>
</client:children>
<client:children>
<client:ParentID>8752</client:ParentID>
<client:ChildID>-8</client:ChildID>
<client:name/>
<client:identifier>CF</client:identifier>
<client:isActive/>
</client:children>
<client:children>
<client:ParentID>9731</client:ParentID>
<client:ChildID>-8</client:ChildID>
<client:name/>
<client:identifier>LE</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1649940</client:ParentID>
<client:ChildID>9731</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1838293</client:ParentID>
<client:ChildID>1649940</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1127251</client:ParentID>
<client:ChildID>1838293</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1127274</client:ParentID>
<client:ChildID>1127251</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1988187</client:ParentID>
<client:ChildID>1127274</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1988188</client:ParentID>
<client:ChildID>1988187</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
</client:children>
<client:children>
<client:ParentID>1988189</client:ParentID>
<client:ChildID>1988187</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1988190</client:ParentID>
<client:ChildID>1988189</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
</client:children>
</client:children>
</client:children>
</client:children>
</client:children>
</client:children>
</client:children>
</client:children>
<client:ParentID>8555</client:ParentID>
<client:ChildID>-8</client:ChildID>
<client:name/>
<client:identifier>CF</client:identifier>
<client:isActive/>
<client:ParentID>8752</client:ParentID>
<client:ChildID>-8</client:ChildID>
<client:name/>
<client:identifier>CF</client:identifier>
<client:isActive/>
<client:ParentID>9731</client:ParentID>
<client:ChildID>-8</client:ChildID>
<client:name/>
<client:identifier>LE</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1649940</client:ParentID>
<client:ChildID>9731</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1838293</client:ParentID>
<client:ChildID>1649940</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1127251</client:ParentID>
<client:ChildID>1838293</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1127274</client:ParentID>
<client:ChildID>1127251</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1988187</client:ParentID>
<client:ChildID>1127274</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1988188</client:ParentID>
<client:ChildID>1988187</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
</client:children>
<client:children>
<client:ParentID>1988189</client:ParentID>
<client:ChildID>1988187</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
<client:children>
<client:ParentID>1988190</client:ParentID>
<client:ChildID>1988189</client:ChildID>
<client:name/>
<client:identifier>CG</client:identifier>
<client:isActive/>
</client:children>
</client:children>
</client:children>
</client:children>
</client:children>
</client:children>
</client:children>
</client:LMSDetails>

XSLT 1.0 (and XSLT 2.0) solution
It is based on use the <xsl:key> to arrange the parents and children while boosting the performance of the solution.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:client="http://client.org">
<xsl:output method="xml" indent="yes" />
<!-- Use keys to boost performance -->
<xsl:key name="entity-key" match="EntityDim" use="pkEntity" />
<xsl:key name="parent-key" match="EntityDim" use="entityParent" />
<!-- Process root element -->
<xsl:template match="EntityDimCollection">
<client:LMSDetails>
<!-- Apply template to root entities, i.e. entities with no parent nodes -->
<xsl:apply-templates select="EntityDim[not(key('entity-key', entityParent))]" />
</client:LMSDetails>
</xsl:template>
<!-- First one to use for the real parent -->
<xsl:template match="EntityDim">
<!-- Obtain the relevant information -->
<client:ParentID><xsl:value-of select="pkEntity" /></client:ParentID>
<client:ChildID><xsl:value-of select="entityParent" /></client:ChildID>
<client:name/>
<client:identifier><xsl:value-of select="entityType" /></client:identifier>
<client:isActive/>
<client:costCenter>
<!-- Obtain the expression after the last dot in essEntityCode -->
<xsl:call-template name="get-suffix">
<xsl:with-param name="text" select="essEntityCode" />
</xsl:call-template>
</client:costCenter>
<!-- Outputs the children for this node : we just search which entities have the
current pkEntity as their entityParent-->
<xsl:for-each select="key('parent-key', pkEntity)">
<client:children>
<xsl:apply-templates select="." />
</client:children>
</xsl:for-each>
</xsl:template>
<!-- Recursive template to obtain the suffix from essEntityCode (the part after
the last dot ) -->
<xsl:template name="get-suffix">
<xsl:param name="text" />
<!-- Check whether the current text contains a dot -->
<xsl:choose>
<!-- Case CONTAINS_DOT: recurse until there is not more dots in the string -->
<xsl:when test="contains($text, '.')">
<xsl:call-template name="get-suffix">
<xsl:with-param name="text" select="substring-after($text, '.')" />
</xsl:call-template>
</xsl:when>
<!-- Case WITHOUT_DOTS : output suffix -->
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
XSLT 2.0 only solution
The way of building the hierarchy is the same, the only difference is the way of obtaining the suffix from essEntityCode. In XSLT 1.0 we had to build a recursive template to detect the last dot and extract the suffix. However in XSLT 2.0 we can use the function tokenize which splits the given string using the given regular expresion.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:client="http://client.org">
<xsl:output method="xml" indent="yes" />
<xsl:key name="entity-key" match="EntityDim" use="pkEntity" />
<xsl:key name="parent-key" match="EntityDim" use="entityParent" />
<xsl:template match="EntityDimCollection">
<client:LMSDetails>
<xsl:apply-templates select="EntityDim[not(key('entity-key', entityParent))]" />
</client:LMSDetails>
</xsl:template>
<xsl:template match="EntityDim">
<client:ParentID><xsl:value-of select="pkEntity" /></client:ParentID>
<client:ChildID><xsl:value-of select="entityParent" /></client:ChildID>
<client:name/>
<client:identifier><xsl:value-of select="entityType" /></client:identifier>
<client:isActive/>
<client:costCenter>
<!-- This is the only difference from the XSLT 1.0 solution, instead
of calling the recursive template we tokenize the expression and
obtain the last element (which is the suffix) -->
<xsl:value-of select="tokenize(essEntityCode, '\.')[last()]" />
</client:costCenter>
<xsl:for-each select="key('parent-key', pkEntity)">
<client:children>
<xsl:apply-templates select="." />
</client:children>
</xsl:for-each>
</xsl:template>
</xsl:stelesheet>
EDIT: I have assumed that for each children a new <client:children> is created, that behavior can be changed by adjusting the code in the <for-each> loop.

Related

How to COPY same context nodes inside another nodes with xslt

I trying to group several nodes that have the same contexts inside a existing nodes, by example my case is try to group node Desglose within Detalle, so by example one Node Detalle could have N nodes Desglose.
My Actual XML
<?xml version="1.0" encoding="UTF-8"?>
<MT_Request_Respuesta_plano>
<Cabecera>
<TipoRegistro>C</TipoRegistro>
<TipoFichero>Facturas</TipoFichero>
<CCAA>12</CCAA>
<FechaFichero>20221124</FechaFichero>
<DescripProceso>DESCARGAS DEL FICHERO DE FACTURAS DEL SERMAS </DescripProceso>
<CodigoResultado/>
<DescripResultado/>
<espacios/>
</Cabecera>
<Certificacion>
<TipoRegistro>R</TipoRegistro>
<NroCertificacion>121100</NroCertificacion>
<NombreSPS>SERVICIO MADRILEÑO DE SALUD </NombreSPS>
<DireccionSPS>PLAZA DE CARLOS TRÍAS BERTRÁN, Nº 7, EDIFICIO SOLLUBE </DireccionSPS>
<LocalidadSPS>MADRID </LocalidadSPS>
<CodPostalSPS>28020 </CodPostalSPS>
<FecDesdeLiquidacion>20210316</FecDesdeLiquidacion>
<FecHastaLiquidacion>20210330</FecHastaLiquidacion>
<NroLiquidacionDesde>000000000000000</NroLiquidacionDesde>
<NroLiquidacionHasta>999999999999999</NroLiquidacionHasta>
<TotalLiquidacionesMes>0000075</TotalLiquidacionesMes>
<ImporteTotal>000000001498200</ImporteTotal>
<Observaciones/>
<Libre>914555222 </Libre>
</Certificacion>
<Detalle>
<TipoRegistro>D</TipoRegistro>
<ProvinciaOrigen>28</ProvinciaOrigen>
<CodCentroGrabacion>2803 </CodCentroGrabacion>
<CodCentroAsistencia/>
<NumeroFactura> 2110100556</NumeroFactura>
<ProvinciaDestino>28</ProvinciaDestino>
<FechaGrabacion>20210316</FechaGrabacion>
<Nombre>AURORA </Nombre>
<PrimerApellido>DE </PrimerApellido>
<SegundoApellido>BLAS GUTIERREZ </SegundoApellido>
<NSS>390050482793</NSS>
<IPF>113740356H </IPF>
<IndicadorRecaida>N</IndicadorRecaida>
<FechaAccidente>20171107</FechaAccidente>
<FechaInicioAs>20171107</FechaInicioAs>
<FechaFinAs>20171107</FechaFinAs>
<TipoContingencia>AT</TipoContingencia>
<CodigoContingencia>3</CodigoContingencia>
<espacios/>
</Detalle>
<Desglose>
<TipoRegistro>T</TipoRegistro>
<CodigoPrestaciones>0</CodigoPrestaciones>
<CodConceptoCargo>E03.1.1.2.2.1.1 </CodConceptoCargo>
<FechaTecnica>20171107</FechaTecnica>
<CodHospitalizacion>0</CodHospitalizacion>
<CodTipoTarifa/>
<Unidades>001</Unidades>
<PrecioConcepto>00017500</PrecioConcepto>
<ImporteTotal>0000000017500</ImporteTotal>
<FechaPublicacionBOCA>20170821</FechaPublicacionBOCA>
<espacios/>
</Desglose>
<Desglose>
<TipoRegistro>T</TipoRegistro>
<CodigoPrestaciones>0</CodigoPrestaciones>
<CodConceptoCargo>E03.1.1.2.1.1 </CodConceptoCargo>
<FechaTecnica>20171108</FechaTecnica>
<CodHospitalizacion>0</CodHospitalizacion>
<CodTipoTarifa/>
<Unidades>001</Unidades>
<PrecioConcepto>00011500</PrecioConcepto>
<ImporteTotal>0000000011500</ImporteTotal>
<FechaPublicacionBOCA>20170821</FechaPublicacionBOCA>
<espacios/>
</Desglose>
<Detalle>
<TipoRegistro>D</TipoRegistro>
<ProvinciaOrigen>28</ProvinciaOrigen>
<CodCentroGrabacion>2803 </CodCentroGrabacion>
<CodCentroAsistencia/>
<NumeroFactura> 2110100559</NumeroFactura>
<ProvinciaDestino>28</ProvinciaDestino>
<FechaGrabacion>20210316</FechaGrabacion>
<Nombre>CLAUDIA </Nombre>
<PrimerApellido>JIMENEZ </PrimerApellido>
<SegundoApellido>TORIJA </SegundoApellido>
<NSS>281208193843</NSS>
<IPF>111862836B </IPF>
<IndicadorRecaida>N</IndicadorRecaida>
<FechaAccidente>20171213</FechaAccidente>
<FechaInicioAs>20171213</FechaInicioAs>
<FechaFinAs>20171214</FechaFinAs>
<TipoContingencia>AT</TipoContingencia>
<CodigoContingencia>3</CodigoContingencia>
<espacios/>
</Detalle>
<Desglose>
<TipoRegistro>T</TipoRegistro>
<CodigoPrestaciones>0</CodigoPrestaciones>
<CodConceptoCargo>E03.1.1.2.2.1.1 </CodConceptoCargo>
<FechaTecnica>20171213</FechaTecnica>
<CodHospitalizacion>0</CodHospitalizacion>
<CodTipoTarifa/>
<Unidades>001</Unidades>
<PrecioConcepto>00017500</PrecioConcepto>
<ImporteTotal>0000000017500</ImporteTotal>
<FechaPublicacionBOCA>20170821</FechaPublicacionBOCA>
<espacios/>
</Desglose>
<Detalle>
<TipoRegistro>D</TipoRegistro>
<ProvinciaOrigen>28</ProvinciaOrigen>
<CodCentroGrabacion>2803 </CodCentroGrabacion>
<CodCentroAsistencia/>
<NumeroFactura> 2110100562</NumeroFactura>
<ProvinciaDestino>28</ProvinciaDestino>
<FechaGrabacion>20210316</FechaGrabacion>
<Nombre>SUSANA SARA </Nombre>
<PrimerApellido>MACHO </PrimerApellido>
<SegundoApellido>LOPEZ </SegundoApellido>
<NSS>280343142847</NSS>
<IPF>170164060F </IPF>
<IndicadorRecaida>N</IndicadorRecaida>
<FechaAccidente>20171030</FechaAccidente>
<FechaInicioAs>20171030</FechaInicioAs>
<FechaFinAs>20171031</FechaFinAs>
<TipoContingencia>AT</TipoContingencia>
<CodigoContingencia>3</CodigoContingencia>
<espacios/>
</Detalle>
<Desglose>
<TipoRegistro>T</TipoRegistro>
<CodigoPrestaciones>0</CodigoPrestaciones>
<CodConceptoCargo>E03.1.1.2.2.1.1 </CodConceptoCargo>
<FechaTecnica>20171030</FechaTecnica>
<CodHospitalizacion>0</CodHospitalizacion>
<CodTipoTarifa/>
<Unidades>001</Unidades>
<PrecioConcepto>00017500</PrecioConcepto>
<ImporteTotal>0000000017500</ImporteTotal>
<FechaPublicacionBOCA>20170821</FechaPublicacionBOCA>
<espacios/>
</Desglose>
<Desglose>
<TipoRegistro>T</TipoRegistro>
<CodigoPrestaciones>0</CodigoPrestaciones>
<CodConceptoCargo>E03.1.1.2.1.1 </CodConceptoCargo>
<FechaTecnica>20171102</FechaTecnica>
<CodHospitalizacion>0</CodHospitalizacion>
<CodTipoTarifa/>
<Unidades>001</Unidades>
<PrecioConcepto>00011500</PrecioConcepto>
<ImporteTotal>0000000011500</ImporteTotal>
<FechaPublicacionBOCA>20170821</FechaPublicacionBOCA>
<espacios/>
</Desglose>
<Totales>
<TipoRegistro>X</TipoRegistro>
<TotalRegGrabados>0000184</TotalRegGrabados>
<espacios/>
</Totales>
</MT_Request_Respuesta_plano>
The result I expect is to be able to group the "Desglose" nodes inside the "Detalle" nodes, assuming the sequence in which the "Desglose" nodes arrive to me, after each "Detail" node, could be N "Detalle" with M "Desglose" associated
<?xml version="1.0" encoding="UTF-8"?>
<MT_Request_Respuesta_plano>
<Cabecera>
<TipoRegistro>C</TipoRegistro>
<TipoFichero>Facturas</TipoFichero>
<CCAA>12</CCAA>
<FechaFichero>20221124</FechaFichero>
<DescripProceso>DESCARGAS DEL FICHERO DE FACTURAS DEL SERMA</DescripProceso>
<CodigoResultado/>
<DescripResultado/>
<espacios/>
</Cabecera>
<Certificacion>
<TipoRegistro>R</TipoRegistro>
<NroCertificacion>121100</NroCertificacion>
<NombreSPS>SERVICIO MADRILEÑO DE SALUD </NombreSPS>
<DireccionSPS>PLAZA DE CARLOS TRÍAS BERTRÁN, Nº 7, EDIFICIO SOLLUBE</DireccionSPS>
<LocalidadSPS>MADRID </LocalidadSPS>
<CodPostalSPS>28020 </CodPostalSPS>
<FecDesdeLiquidacion>20210316</FecDesdeLiquidacion>
<FecHastaLiquidacion>20210330</FecHastaLiquidacion>
<NroLiquidacionDesde>000000000000000</NroLiquidacionDesde>
<NroLiquidacionHasta>999999999999999</NroLiquidacionHasta>
<TotalLiquidacionesMes>0000075</TotalLiquidacionesMes>
<ImporteTotal>000000001498200</ImporteTotal>
<Observaciones/>
<Libre>914555222 </Libre>
</Certificacion>
<Detalle>
<TipoRegistro>D</TipoRegistro>
<ProvinciaOrigen>28</ProvinciaOrigen>
<CodCentroGrabacion>2803 </CodCentroGrabacion>
<CodCentroAsistencia/>
<NumeroFactura> 2110100556</NumeroFactura>
<ProvinciaDestino>28</ProvinciaDestino>
<FechaGrabacion>20210316</FechaGrabacion>
<Nombre>AURORA </Nombre>
<PrimerApellido>DE </PrimerApellido>
<SegundoApellido>BLAS GUTIERREZ </SegundoApellido>
<NSS>390050482793</NSS>
<IPF>113740356H </IPF>
<IndicadorRecaida>N</IndicadorRecaida>
<FechaAccidente>20171107</FechaAccidente>
<FechaInicioAs>20171107</FechaInicioAs>
<FechaFinAs>20171107</FechaFinAs>
<TipoContingencia>AT</TipoContingencia>
<CodigoContingencia>3</CodigoContingencia>
<espacios/>
<Desglose>
<TipoRegistro>T</TipoRegistro>
<CodigoPrestaciones>0</CodigoPrestaciones>
<CodConceptoCargo>E03.1.1.2.2.1.1 </CodConceptoCargo>
<FechaTecnica>20171107</FechaTecnica>
<CodHospitalizacion>0</CodHospitalizacion>
<CodTipoTarifa/>
<Unidades>001</Unidades>
<PrecioConcepto>00017500</PrecioConcepto>
<ImporteTotal>0000000017500</ImporteTotal>
<FechaPublicacionBOCA>20170821</FechaPublicacionBOCA>
<espacios/>
</Desglose>
<Desglose>
<TipoRegistro>T</TipoRegistro>
<CodigoPrestaciones>0</CodigoPrestaciones>
<CodConceptoCargo>E03.1.1.2.1.1 </CodConceptoCargo>
<FechaTecnica>20171108</FechaTecnica>
<CodHospitalizacion>0</CodHospitalizacion>
<CodTipoTarifa/>
<Unidades>001</Unidades>
<PrecioConcepto>00011500</PrecioConcepto>
<ImporteTotal>0000000011500</ImporteTotal>
<FechaPublicacionBOCA>20170821</FechaPublicacionBOCA>
<espacios/>
</Desglose>
</Detalle>
<Detalle>
<TipoRegistro>D</TipoRegistro>
<ProvinciaOrigen>28</ProvinciaOrigen>
<CodCentroGrabacion>2803 </CodCentroGrabacion>
<CodCentroAsistencia/>
<NumeroFactura> 2110100559</NumeroFactura>
<ProvinciaDestino>28</ProvinciaDestino>
<FechaGrabacion>20210316</FechaGrabacion>
<Nombre>CLAUDIA </Nombre>
<PrimerApellido>JIMENEZ </PrimerApellido>
<SegundoApellido>TORIJA </SegundoApellido>
<NSS>281208193843</NSS>
<IPF>111862836B </IPF>
<IndicadorRecaida>N</IndicadorRecaida>
<FechaAccidente>20171213</FechaAccidente>
<FechaInicioAs>20171213</FechaInicioAs>
<FechaFinAs>20171214</FechaFinAs>
<TipoContingencia>AT</TipoContingencia>
<CodigoContingencia>3</CodigoContingencia>
<espacios/>
<Desglose>
<TipoRegistro>T</TipoRegistro>
<CodigoPrestaciones>0</CodigoPrestaciones>
<CodConceptoCargo>E03.1.1.2.2.1.1 </CodConceptoCargo>
<FechaTecnica>20171213</FechaTecnica>
<CodHospitalizacion>0</CodHospitalizacion>
<CodTipoTarifa/>
<Unidades>001</Unidades>
<PrecioConcepto>00017500</PrecioConcepto>
<ImporteTotal>0000000017500</ImporteTotal>
<FechaPublicacionBOCA>20170821</FechaPublicacionBOCA>
<espacios/>
</Desglose>
</Detalle>
<Detalle>
<TipoRegistro>D</TipoRegistro>
<ProvinciaOrigen>28</ProvinciaOrigen>
<CodCentroGrabacion>2803 </CodCentroGrabacion>
<CodCentroAsistencia/>
<NumeroFactura> 2110100562</NumeroFactura>
<ProvinciaDestino>28</ProvinciaDestino>
<FechaGrabacion>20210316</FechaGrabacion>
<Nombre>SUSANA SARA </Nombre>
<PrimerApellido>MACHO </PrimerApellido>
<SegundoApellido>LOPEZ </SegundoApellido>
<NSS>280343142847</NSS>
<IPF>170164060F </IPF>
<IndicadorRecaida>N</IndicadorRecaida>
<FechaAccidente>20171030</FechaAccidente>
<FechaInicioAs>20171030</FechaInicioAs>
<FechaFinAs>20171031</FechaFinAs>
<TipoContingencia>AT</TipoContingencia>
<CodigoContingencia>3</CodigoContingencia>
<espacios/>
<Desglose>
<TipoRegistro>T</TipoRegistro>
<CodigoPrestaciones>0</CodigoPrestaciones>
<CodConceptoCargo>E03.1.1.2.2.1.1 </CodConceptoCargo>
<FechaTecnica>20171030</FechaTecnica>
<CodHospitalizacion>0</CodHospitalizacion>
<CodTipoTarifa/>
<Unidades>001</Unidades>
<PrecioConcepto>00017500</PrecioConcepto>
<ImporteTotal>0000000017500</ImporteTotal>
<FechaPublicacionBOCA>20170821</FechaPublicacionBOCA>
<espacios/>
</Desglose>
<Desglose>
<TipoRegistro>T</TipoRegistro>
<CodigoPrestaciones>0</CodigoPrestaciones>
<CodConceptoCargo>E03.1.1.2.1.1 </CodConceptoCargo>
<FechaTecnica>20171102</FechaTecnica>
<CodHospitalizacion>0</CodHospitalizacion>
<CodTipoTarifa/>
<Unidades>001</Unidades>
<PrecioConcepto>00011500</PrecioConcepto>
<ImporteTotal>0000000011500</ImporteTotal>
<FechaPublicacionBOCA>20170821</FechaPublicacionBOCA>
<espacios/>
</Desglose>
</Detalle>
<Totales>
<TipoRegistro>X</TipoRegistro>
<TotalRegGrabados>0000184</TotalRegGrabados>
<espacios/>
</Totales>
</MT_Request_Respuesta_plano>
I've tried this XSLT, one with for-each-group, but no result.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"
version="1.0"
encoding="UTF-8"
indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Detalle">
<xsl:for-each-group select="Detalle | Desglose" group-by="#Detalle">
<Detalle>
<xsl:copy-of select="node()"/>
<xsl:for-each-group select="current-group()" group-by="#Desglose">
<Desglose>
<xsl:for-each select="current-group()">
<xsl:copy-of select="node()"/>
</xsl:for-each>
</Desglose>
</xsl:for-each-group>
</Detalle>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
I also have tried this another XSLT, more nice, but just result with values without label...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="Kgrupo" match="Desglose" use="generate-id(preceding-sibling::Detalle[NumeroFactura][1])" />
<xsl:template match="Detalle">
<xsl:copy>
<xsl:for-each select="Desglose">
<xsl:for-each select="key('Kgrupo', generate-id())" >
<Desglose>
<xsl:value-of select="normalize-space(main)" />
</Desglose>
</xsl:for-each>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I would really appreciate any help to solve this transformation that seems easy, but it is not, at least for me =)
The xsl:for-each-group instruction requires XSLT 2.0 or higher. To do the equivalent of group-starting-with in XSLT 1.0 you need a different method. Your 2nd attempt is close, but it should be:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="Kgrupo" match="Desglose" use="generate-id(preceding-sibling::Detalle[1])" />
<xsl:template match="/MT_Request_Respuesta_plano">
<xsl:copy>
<xsl:copy-of select="Cabecera | Certificacion"/>
<xsl:for-each select="Detalle">
<xsl:copy>
<xsl:copy-of select="*"/>
<xsl:copy-of select="key('Kgrupo', generate-id())"/>
</xsl:copy>
</xsl:for-each>
<xsl:copy-of select="Totales"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This XSLT 2.0 answer creates the required output with a recursive named template 'iterate-elements'. The recursive template calls will be 'tail-call-optimised' by an XSLT processor such as Saxon.
I would generally recommend the accepted answer's approach using xsl:for-each and xsl:key as it it simpler and more concise. This alternate approach is a bit more generic however and may be useful if you find the grouping logic becomes more complex later on.
This XSLT template uses 'recursive-sibling` approach to iterate child element of the root element.
Details on the state that is tracked when iterating each element:
$currentElement the current element for each iteration
$targetElement the last previous instance of the Detalle element in the iteration
$sourceElements the Desglose elements following the $targetElement - later injected into $targetElement with the fn:injectElements function
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="com.group-elements.sample"
exclude-result-prefixes="#all"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="#*" mode="#current"/>
<xsl:call-template name="iterate-elements"/>
</xsl:copy>
</xsl:template>
<xsl:template name="iterate-elements">
<xsl:param name="elements" as="element()*" select="*"/>
<xsl:param name="currentElement" select="$elements[1]"/>
<xsl:param name="targetElement" as="element()?" select="()"/>
<xsl:param name="sourceElements" as="element()*"/>
<xsl:choose>
<xsl:when test="empty($currentElement)">
<!-- in case there are any source elements left over: -->
<xsl:sequence select="fn:injectElements($targetElement, $sourceElements)"/>
</xsl:when>
<xsl:when test="fn:isTarget($currentElement)">
<!-- on a new target element so process previous target element and set the new target element -->
<xsl:sequence select="fn:injectElements($targetElement, $sourceElements)"/>
<xsl:call-template name="iterate-elements">
<xsl:with-param name="currentElement" select="$currentElement/following-sibling::*[1]"/>
<xsl:with-param name="sourceElements" select="()"/>
<xsl:with-param name="targetElement" select="$currentElement"/>
</xsl:call-template>
</xsl:when>
<!-- on a new source element so concatanate to the sequence of source elements -->
<xsl:when test="fn:isSource($currentElement)">
<xsl:call-template name="iterate-elements">
<xsl:with-param name="currentElement" select="$currentElement/following-sibling::*[1]"/>
<xsl:with-param name="sourceElements" select="$sourceElements, $currentElement"/>
<xsl:with-param name="targetElement" select="$targetElement"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- not on source/target element - process any target element then emit current element -->
<xsl:sequence select="fn:injectElements($targetElement, $sourceElements), $currentElement"/>
<xsl:call-template name="iterate-elements">
<xsl:with-param name="currentElement" select="$currentElement/following-sibling::*[1]"/>
<xsl:with-param name="sourceElements" select="()"/>
<xsl:with-param name="targetElement" select="()"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:function name="fn:isTarget" as="xs:boolean">
<xsl:param name="element" as="element()"/>
<xsl:sequence select="exists($element[self::Detalle])"/>
</xsl:function>
<xsl:function name="fn:isSource" as="xs:boolean">
<xsl:param name="element" as="element()"/>
<xsl:sequence select="exists($element[self::Desglose])"/>
</xsl:function>
<xsl:function name="fn:injectElements" as="element()?">
<xsl:param name="target" as="element()?"/>
<xsl:param name="sourceElements" as="element()*"/>
<xsl:if test="exists($target)">
<xsl:copy select="$target">
<xsl:copy-of select="$target/#*, $target/node()"/>
<xsl:copy-of select="$sourceElements"/>
</xsl:copy>
</xsl:if>
</xsl:function>
</xsl:stylesheet>[enter link description here][1]

Flat database records to parent Child XML transformation

I have records in database which has same order header with all lines records
Could anybody please help me how to use xsl to transform
<?xml version='1.0' encoding='UTF-8'?>
<getParentChildOutputCollection xmlns="http://xmlns.oracle.com/cloud/adapter/dbaasdatabase/getParentChild_REQUEST/types">
<getParentChildOutput>
<PID>1</PID>
<PNAME>Xerox</PNAME>
<CID>101</CID>
<CNAME>Order 101</CNAME>
<CDESC>Order Paper</CDESC>
</getParentChildOutput>
<getParentChildOutput>
<PID>1</PID>
<PNAME>Xerox</PNAME>
<CID>102</CID>
<CNAME>Order 102</CNAME>
<CDESC>Order Black Ink</CDESC>
</getParentChildOutput>
<getParentChildOutput>
<PID>1</PID>
<PNAME>Xerox</PNAME>
<CID>103</CID>
<CNAME>Order 103</CNAME>
<CDESC>Order Staple Pin</CDESC>
</getParentChildOutput>
<getParentChildOutput>
<PID>2</PID>
<PNAME>HP</PNAME>
<CID>230</CID>
<CNAME>Order 230</CNAME>
<CDESC>Order Red Ink</CDESC>
</getParentChildOutput>
<getParentChildOutput>
<PID>2</PID>
<PNAME>HP</PNAME>
<CID>231</CID>
<CNAME>Order 231</CNAME>
<CDESC>Order Blue Ink</CDESC>
</getParentChildOutput>
</getParentChildOutputCollection>
I want to transform above sml using xsl to below output
<?xml version="1.0" encoding="utf-8"?>
<request-wrapper>
<TransmissionID>1234</TransmissionID>
<DeliveryOrders>
<OrderCode>1</OrderCode>
<Company>Xerox</Company>
<Lines>
<c1d>101</c1d>
<cname>Order 101</cname>
<cdesc>Order Paper</cdesc>
</Lines>
<Lines>
<c1d>102</c1d>
<cname>Order 102</cname>
<cdesc>Order Black Ink</cdesc>
</Lines>
<Lines>
<c1d>3</c1d>
<cname>Order 103</cname>
<cdesc>Order Staple Pin</cdesc>
</Lines>
</DeliveryOrders>
<DeliveryOrders>
<OrderCode>2</OrderCode>
<Company>p2</Company>
<Lines>
<c1d>230</c1d>
<cname>Order 230</cname>
<cdesc>Order Red Ink</cdesc>
</Lines>
<Lines>
<c1d>231</c1d>
<cname>Order 231</cname>
<cdesc>Order Blue Ink</cdesc>
</Lines>
</DeliveryOrders>
</request-wrapper>
I have used the below xsl but in the application I am working it is not working, it is not recognize the key and generate-id commands. Are there any other way I can acheive this?
<?xml version = '1.0' encoding = 'UTF-8'?>
<xsl:stylesheet version="2.0" xml:id="id_1" xmlns:nssrcmpr="http://www.oracle.com/2014/03/ics/schedule" xmlns:nstrgdfl="http://xmlns.oracle.com/cloud/adapter/ftp/writejson/types" xmlns:oraext="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.ExtFunc" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/" xmlns:xp20="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.Xpath20" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:nstrgmpr="http://xmlns.oracle.com/cloud/adapter/ftp/writejson_REQUEST/types" xmlns:ora="http://schemas.oracle.com/xpath/extension" xmlns:oracle-xsl-mapper="http://www.oracle.com/xsl/mapper/schemas" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:ns1="http://xml.oracle.com/adapters/extension" xmlns:ns5="http://xmlns.oracle.com/cloud/adapter/ftp/writejson_REQUEST" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes=" nssrcmpr oraext xsd xp20 ora oracle-xsl-mapper xsi fn ns1 xsl ignore01" xmlns:ignore01="http://www.oracle.com/XSL/Transform/java" ignore01:ignorexmlids="true" xmlns:nsmpr0="http://xmlns.oracle.com/cloud/adapter/dbaasdatabase/getParentChild_REQUEST/types" xmlns:nsmpr1="http://www.oracle.com/2014/03/ic/integration/metadata" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:ns23="http://xmlns.oracle.com/cloud/ftp/write/response/pull" xmlns:flt="http://xmlns.oracle.com/cloud/generic/service/fault" xmlns:dvm="http://www.oracle.com/XSL/Transform/java/com.bea.wli.sb.functions.dvm.DVMFunctions" xmlns:orajs0="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath1453381219" xmlns:ns22="http://xml.oracle.com/types" xmlns:orajs6="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath1211296200" xmlns:orajs3="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath213937888" xmlns:orajs1="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath562866038" xmlns:orajs7="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath86288" xmlns:tns="http://xmlns.oracle.com/cloud/adapter/dbaasdatabase/getParentChild_REQUEST" xmlns:ns2="http://www.oracle.com/XSL/Transform/java/com.bea.wli.sb.resources.icsxpathfunctions.ICSInstanceTrackingFunctions" xmlns:orajs2="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath1600802978" xmlns:orajs5="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath2113524327" xmlns:orajs4="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath1290874520" xmlns:ns0="http://www.oracle.com/XSL/Transform/java/oracle.tip.dvm.LookupValue">
<oracle-xsl-mapper:schema xml:id="id_2">
<!--SPECIFICATION OF MAP SOURCES AND TARGETS, DO NOT MODIFY.-->
<oracle-xsl-mapper:mapSources xml:id="id_3">
<oracle-xsl-mapper:source type="XSD" xml:id="id_4">
<oracle-xsl-mapper:schema location="../../processor_13/resourcegroup_14/ICSSchedule_1.xsd" xml:id="id_5"/>
<oracle-xsl-mapper:rootElement name="schedule" namespace="http://www.oracle.com/2014/03/ics/schedule" xml:id="id_6"/>
</oracle-xsl-mapper:source>
<oracle-xsl-mapper:source type="WSDL" xml:id="id_13">
<oracle-xsl-mapper:schema location="../../application_27/inbound_28/resourcegroup_29/getParentChild_REQUEST.wsdl" xml:id="id_14"/>
<oracle-xsl-mapper:rootElement name="getParentChildOutputCollection" namespace="http://xmlns.oracle.com/cloud/adapter/dbaasdatabase/getParentChild_REQUEST/types" xml:id="id_15"/>
<oracle-xsl-mapper:param name="getParentChild" xml:id="id_16"/>
</oracle-xsl-mapper:source>
</oracle-xsl-mapper:mapSources>
<oracle-xsl-mapper:mapTargets xml:id="id_7">
<oracle-xsl-mapper:target type="WSDL" xml:id="id_8">
<oracle-xsl-mapper:schema location="../../application_49/inbound_50/resourcegroup_51/writejson_REQUEST.wsdl" xml:id="id_9"/>
<oracle-xsl-mapper:rootElement name="WriteFile" namespace="http://xmlns.oracle.com/cloud/adapter/ftp/writejson_REQUEST/types" xml:id="id_10"/>
</oracle-xsl-mapper:target>
</oracle-xsl-mapper:mapTargets>
<!--GENERATED BY ORACLE XSL MAPPER 12.1.2.0.0-->
</oracle-xsl-mapper:schema>
<!--User Editing allowed BELOW this line - DO NOT DELETE THIS LINE-->
<xsl:param name="getParentChild" xml:id="id_25"/>
<xsl:key name="keyHeader" match="nsmpr0:getParentChildOutput" use="nsmpr0:PID" />
<xsl:key name="keyLines" match="nsmpr0:getParentChildOutput" use="concat(nsmpr0:PID,'#',nsmpr0:CID)" />
<xsl:template match="/" xml:id="id_11">
<nstrgmpr:WriteFile xml:id="id_12">
<nstrgdfl:request-wrapper xml:id="id_31">
<!--<xsl:for-each xml:id="id_33" select="$getParentChild/nsmpr0:getParentChildOutputCollection/nsmpr0:getParentChildOutput">-->
<xsl:for-each xml:id="id_33" select="row[generate-id() = generate-id(key('keyHeader', nsmpr0:PID)[1])]"> <!--Sreejit 1 -->
<nstrgdfl:DeliveryOrders xml:id="id_34">
<nstrgdfl:OrderCode xml:id="id_40">
<xsl:value-of xml:id="id_41" select="nsmpr0:PID"/>
</nstrgdfl:OrderCode>
<nstrgdfl:Company xml:id="id_38">
<xsl:value-of xml:id="id_42" select="nsmpr0:PNAME"/>
</nstrgdfl:Company>
<!-- <xsl:for-each xml:id="id_36" select="."> -->
<xsl:for-each xml:id="id_36" select="key('keyHeader', nsmpr0:PID)[generate-id() = generate-id(key('keyLines', concat(nsmpr0:PID,'#',nsmpr0:CID))[1])]"> <!--Sreejit 2 -->
<nstrgdfl:Lines xml:id="id_37">
<nstrgdfl:c1d xml:id="id_43">
<xsl:value-of xml:id="id_44" select="nsmpr0:CID"/>
</nstrgdfl:c1d>
<nstrgdfl:cname xml:id="id_45">
<xsl:value-of xml:id="id_46" select="nsmpr0:CNAME"/>
</nstrgdfl:cname>
<nstrgdfl:cdesc xml:id="id_47">
<xsl:value-of xml:id="id_48" select="nsmpr0:CDESC"/>
</nstrgdfl:cdesc>
</nstrgdfl:Lines>
</xsl:for-each>
</nstrgdfl:DeliveryOrders>
</xsl:for-each>
</nstrgdfl:request-wrapper>
</nstrgmpr:WriteFile>
</xsl:template>
</xsl:stylesheet>
Regards,
Sree
Your instruction:
<xsl:for-each xml:id="id_33" select="row[generate-id() = generate-id(key('keyHeader', nsmpr0:PID)[1])]">
selects nothing because there is no element named row in the current context (or in the entire input document, for that matter).
Side note: your stylesheet is tagged version="2.0" yet you are attempting to use Muenchian grouping - which makes very little sense.
I pasted the code which worked, just for others in case anybody want the code
<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version="2.0" xml:id="id_1" xmlns:nssrcmpr="http://www.oracle.com/2014/03/ics/schedule" xmlns:nstrgdfl="http://xmlns.oracle.com/cloud/adapter/ftp/writejson/types" xmlns:oraext="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.ExtFunc" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/" xmlns:xp20="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.Xpath20" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:nstrgmpr="http://xmlns.oracle.com/cloud/adapter/ftp/writejson_REQUEST/types" xmlns:ora="http://schemas.oracle.com/xpath/extension" xmlns:oracle-xsl-mapper="http://www.oracle.com/xsl/mapper/schemas" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:ns1="http://xml.oracle.com/adapters/extension" xmlns:ns5="http://xmlns.oracle.com/cloud/adapter/ftp/writejson_REQUEST" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes=" nssrcmpr oraext xsd xp20 ora oracle-xsl-mapper xsi fn ns1 xsl ignore01" xmlns:ignore01="http://www.oracle.com/XSL/Transform/java" ignore01:ignorexmlids="true" xmlns:nsmpr0="http://xmlns.oracle.com/cloud/adapter/dbaasdatabase/getParentChild_REQUEST/types" xmlns:nsmpr1="http://www.oracle.com/2014/03/ic/integration/metadata" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:ns23="http://xmlns.oracle.com/cloud/ftp/write/response/pull" xmlns:flt="http://xmlns.oracle.com/cloud/generic/service/fault" xmlns:dvm="http://www.oracle.com/XSL/Transform/java/com.bea.wli.sb.functions.dvm.DVMFunctions" xmlns:orajs0="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath1453381219" xmlns:ns22="http://xml.oracle.com/types" xmlns:orajs6="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath1211296200" xmlns:orajs3="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath213937888" xmlns:orajs1="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath562866038" xmlns:orajs7="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath86288" xmlns:tns="http://xmlns.oracle.com/cloud/adapter/dbaasdatabase/getParentChild_REQUEST" xmlns:ns2="http://www.oracle.com/XSL/Transform/java/com.bea.wli.sb.resources.icsxpathfunctions.ICSInstanceTrackingFunctions" xmlns:orajs2="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath1600802978" xmlns:orajs5="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath2113524327" xmlns:orajs4="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.JsExecutor_xpath1290874520" xmlns:ns0="http://www.oracle.com/XSL/Transform/java/oracle.tip.dvm.LookupValue">
<oracle-xsl-mapper:schema xml:id="id_2">
<!--SPECIFICATION OF MAP SOURCES AND TARGETS, DO NOT MODIFY.-->
<oracle-xsl-mapper:mapSources xml:id="id_3">
<oracle-xsl-mapper:source type="XSD" xml:id="id_4">
<oracle-xsl-mapper:schema location="../../processor_13/resourcegroup_14/ICSSchedule_1.xsd" xml:id="id_5"/>
<oracle-xsl-mapper:rootElement name="schedule" namespace="http://www.oracle.com/2014/03/ics/schedule" xml:id="id_6"/>
</oracle-xsl-mapper:source>
<oracle-xsl-mapper:source type="WSDL" xml:id="id_13">
<oracle-xsl-mapper:schema location="../../application_27/inbound_28/resourcegroup_29/getParentChild_REQUEST.wsdl" xml:id="id_14"/>
<oracle-xsl-mapper:rootElement name="getParentChildOutputCollection" namespace="http://xmlns.oracle.com/cloud/adapter/dbaasdatabase/getParentChild_REQUEST/types" xml:id="id_15"/>
<oracle-xsl-mapper:param name="getParentChild" xml:id="id_16"/>
</oracle-xsl-mapper:source>
</oracle-xsl-mapper:mapSources>
<oracle-xsl-mapper:mapTargets xml:id="id_7">
<oracle-xsl-mapper:target type="WSDL" xml:id="id_8">
<oracle-xsl-mapper:schema location="../../application_49/inbound_50/resourcegroup_51/writejson_REQUEST.wsdl" xml:id="id_9"/>
<oracle-xsl-mapper:rootElement name="WriteFile" namespace="http://xmlns.oracle.com/cloud/adapter/ftp/writejson_REQUEST/types" xml:id="id_10"/>
</oracle-xsl-mapper:target>
</oracle-xsl-mapper:mapTargets>
<!--GENERATED BY ORACLE XSL MAPPER 12.1.2.0.0-->
</oracle-xsl-mapper:schema>
<!--User Editing allowed BELOW this line - DO NOT DELETE THIS LINE-->
<xsl:param name="getParentChild" xml:id="id_25"/>
<xsl:output method="xml" version="1.0" indent="yes" omit-xml-declaration="no"/>
<xsl:template match="/" xml:id="id_11">
<nstrgmpr:WriteFile xml:id="id_12">
<nstrgdfl:request-wrapper xml:id="id_31">
<xsl:for-each-group xml:id="id_33" select="$getParentChild/nsmpr0:getParentChildOutputCollection/nsmpr0:getParentChildOutput" group-by="nsmpr0:PID">
<nstrgdfl:DeliveryOrders xml:id="id_34">
<nstrgdfl:OrderCode xml:id="id_40">
<xsl:value-of xml:id="id_41" select="nsmpr0:PID"/>
</nstrgdfl:OrderCode>
<nstrgdfl:Company xml:id="id_38">
<xsl:value-of xml:id="id_42" select="nsmpr0:PNAME"/>
</nstrgdfl:Company>
<xsl:for-each select="fn:current-group()">
<nstrgdfl:Lines xml:id="id_37">
<nstrgdfl:c1d xml:id="id_43">
<xsl:value-of xml:id="id_44" select="nsmpr0:CID"/>
</nstrgdfl:c1d>
<nstrgdfl:cname xml:id="id_45">
<xsl:value-of xml:id="id_46" select="nsmpr0:CNAME"/>
</nstrgdfl:cname>
<nstrgdfl:cdesc xml:id="id_47">
<xsl:value-of xml:id="id_48" select="nsmpr0:CDESC"/>
</nstrgdfl:cdesc>
</nstrgdfl:Lines>
</xsl:for-each>
</nstrgdfl:DeliveryOrders>
</xsl:for-each-group>
</nstrgdfl:request-wrapper>
</nstrgmpr:WriteFile>
</xsl:template>
</xsl:stylesheet>

XSLT3 Streaming for appending integer position of node

I have a large XML file to transform using XSLT to append the integer position of sibling node . I’m using XSLT3 streaming and accumulators. I did get desired output. However, my code looks so lengthy that I’m unable to simplify my code. I also need to group same sibling nodes as sibling nodes in the source xml is not grouped always. Could someone help me here please?
Requirement: Sibling nodes such as Positions, Payments etc.. need to be appended with their corresponding integer position such as <Locations1>, <Locations2>etc.<Payments1>,< Payments2> etc..
Now that I have declared two accumulators, each for each sibling nodes. However, my source XML has many sibling nodes.. I’m not sure if I need to use as many accumulators and template match as my sibling nodes.
Input XML
``
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Payments>
<amount>1000</amount>
<currency>USD</currency>
</Payments>
<Payments>
<amount>1000</amount>
<currency>USD</currency>
</Payments>
<Locations>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations>
<Locations>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Payments>
<amount>1500</amount>
<currency>USD</currency>
</Payments>
<Payments>
<amount>1800</amount>
<currency>USD</currency>
</Payments>
</Member>
</Members>
``
Expected Output
``
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations_1>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Locations_3>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations_3>
<Locations_4>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations_4>
<Payments_1>
<amount>1000</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1000</amount>
<currency>USD</currency>
</Payments_2>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations_1>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1500</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1800</amount>
<currency>USD</currency>
</Payments_2>
</Member>
</Members>
``
Current code
``
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:mode streamable="yes" on-no-match="shallow-copy" use-accumulators="#all"/>
<xsl:accumulator name="loc-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="Member" select="0"/>
<xsl:accumulator-rule match="Member/Locations" select="$value + 1"/>
</xsl:accumulator>
<xsl:accumulator name="pay-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="Member" select="0"/>
<xsl:accumulator-rule match="Member/Payments" select="$value + 1"/>
</xsl:accumulator>
<xsl:template match="Locations">
<xsl:element name="Locations_{accumulator-before('loc-count')}">
<xsl:copy-of select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="Payments">
<xsl:element name="Payments_{accumulator-before('pay-count')}">
<xsl:copy-of select="#* | node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
``
Current Output
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations_1>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1000</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1000</amount>
<currency>USD</currency>
</Payments_2>
<Locations_3>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations_3>
<Locations_4>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations_4>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations_1>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1500</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1800</amount>
<currency>USD</currency>
</Payments_2>
</Member>
</Members>
If you want to group the Member child elements by node-name() then I think you need to wrap the xsl:for-each-group into xsl:fork:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy" streamable="yes" use-accumulators="counters"/>
<xsl:accumulator name="counters" as="map(xs:QName, xs:integer)" initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="Member" select="map{}"/>
<xsl:accumulator-rule match="Member/*"
select="map:put($value, node-name(), if (map:contains($value, node-name())) then map:get($value, node-name()) + 1 else 1)"/>
</xsl:accumulator>
<xsl:template match="Member">
<xsl:copy>
<xsl:fork>
<xsl:for-each-group select="*" group-by="node-name()">
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</xsl:fork>
</xsl:copy>
</xsl:template>
<xsl:template match="Member/*">
<xsl:element name="{node-name()}_{accumulator-before('counters')(node-name())}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This approach only shows the grouping, it doesn't try to special case Name elements or some other way to not output an index if there is only one such element.
Firstly, my sympathy. XML that uses names like Payments_1 and Payments_2 is really bad news, someone is going to hate you for generating it like this. But if that's the kind of XML you've been told to produce, I guess it's not your job to question it.
As far as the requirements are concerned, you haven't made it clear whether the various kinds of sibling nodes are always grouped as in your example (all Locations, then all Payments, etc), or whether they can be interleaved.
One way you might be able to reduce the volume of code is by having a single accumulator holding a map. The map would use element names as the key and the current sibling count for that element as the value.
<accumulator name="counters" as="map(xs:QName, xs:integer)" initial-value="map{}">
<xsl:accumulator-rule match="Member" select="map{}"/>
<xsl:accumulator-rule match="Member/*" select="map:put($value, node-name(.), if (map:contains($value, node-name(.)) then map:get($value, node-name(.))+1 else 1"/>
</accumulator>
<xsl:template match="Members/*">
<xsl:element name="{name()}_{accumulator-before('counters')(node-name(.))}">
....
Another way to do the conditional map:put is
map:put($value, node-name(.), ($value(node-name(.)), 0)[1] + 1)

Trying to understand XSLT Muenchian Method transformation

I came across the Muenchian Method when looking for a way of grouping elements in an XML file that has been produced by converted a CSV file.
Source
<file>
<patient>
<Lab_Specimen_Number>L,18.1342718.Y</Lab_Specimen_Number>
<Patient_Number>LOC0000015</Patient_Number>
<ORG/>
<Specimen/>
<Antibiotic_Amox_Ampicillin/>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1342727.V</Lab_Specimen_Number>
<Patient_Number>LOC0000001</Patient_Number>
<ORG>Coliform</ORG>
<Specimen>L,18.1342727.VA</Specimen>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</patient>
<patient>
<Lab_Specimen_Number/>
<Patient_Number/>
<ORG>Staphylococcus aureus</ORG>
<Specimen>L,18.1342727.VA</Specimen>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1346290.T</Lab_Specimen_Number>
<Patient_Number>LOC0000001</Patient_Number>
<ORG>Coliform</ORG>
<Specimen>L,18.1346290.TA</Specimen>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1342713.X</Lab_Specimen_Number>
<Patient_Number>LOC0000009</Patient_Number>
<ORG/>
<Specimen/>
<Antibiotic_Amox_Ampicillin/>
</patient>
</file>
Based on the article I have changed the match when assigning a key to patient[Specimen != ''] instead of just patient as it is possible for the Specimen value to be blank and these would be missing from the final output if just patient was used.
Transformation
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="patients-by-specimen" match="patient[Specimen != '']" use="Specimen" />
<xsl:template match="file">
<file>
<xsl:for-each select="patient[count(. | key('patients-by-specimen', Specimen)[1]) = 1]">
<patient>
<xsl:copy-of select="Lab_Specimen_Number" />
<xsl:copy-of select="Patient_Number" />
<Specimen>
<xsl:copy-of select="Specimen" />
<Organisms>
<xsl:for-each select="key('patients-by-specimen', Specimen)">
<Organism>
<xsl:copy-of select="ORG"/>
<xsl:copy-of select="Antibiotic_Amox_Ampicillin"/>
</Organism>
</xsl:for-each>
</Organisms>
</Specimen>
</patient>
</xsl:for-each>
</file>
</xsl:template>
</xsl:stylesheet>
Whilst the transformation above gives me the desired output I don't fully understand how this line is working:
<xsl:for-each select="patient[count(. | key('patients-by-specimen', Specimen)[1]) = 1]">
Could someone explain this iteration in the context of my source file?
Output
<file>
<patient>
<Lab_Specimen_Number>L,18.1342718.Y</Lab_Specimen_Number>
<Patient_Number>LOC0000015</Patient_Number>
<Specimen>
<Specimen/>
<Organisms/>
</Specimen>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1342727.V</Lab_Specimen_Number>
<Patient_Number>LOC0000001</Patient_Number>
<Specimen>
<Specimen>L,18.1342727.VA</Specimen>
<Organisms>
<Organism>
<ORG>Coliform</ORG>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</Organism>
<Organism>
<ORG>Staphylococcus aureus</ORG>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</Organism>
</Organisms>
</Specimen>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1346290.T</Lab_Specimen_Number>
<Patient_Number>LOC0000001</Patient_Number>
<Specimen>
<Specimen>L,18.1346290.TA</Specimen>
<Organisms>
<Organism>
<ORG>Coliform</ORG>
<Antibiotic_Amox_Ampicillin>S</Antibiotic_Amox_Ampicillin>
</Organism>
</Organisms>
</Specimen>
</patient>
<patient>
<Lab_Specimen_Number>L,18.1342713.X</Lab_Specimen_Number>
<Patient_Number>LOC0000009</Patient_Number>
<Specimen>
<Specimen/>
<Organisms/>
</Specimen>
</patient>
</file>

Group and split Xmldocument on nth level descendant

I want to split an XmlDocument into an array of XmlDocuments, where each splitted XmlDocument contains records of a certain period (Year/Month combination). The complicating factor, imo, is that the grouping should occur on nested elements.
Example input:
<?xml version="1.0" encoding="utf-8"?>
<Example>
<RecordA>
<RecordA1>
<RecordA11>
<ElementA11></ElementA11>
</RecordA11>
</RecordA1>
<RecordA2>
<ElementA2></ElementA2>
</RecordA2>
</RecordA>
<RecordB>
<RecordB1>
<ElementB1></ElementB1>
<RecordB11>
<ElementB11></ElementB11>
<RecordB111>
<RecordB1111>
<RecordB11111>
<ElementB11111></ElementB11111>
</RecordB11111>
<ElementB1111></ElementB1111>
<RecordB11112>
<Dates>
<StartDate>2014-05-29</StartDate>
<EndDate>2014-05-29</EndDate>
</Dates>
</RecordB11112>
<RecordB11112>
<Dates>
<StartDate>2014-06-02</StartDate>
<EndDate>2014-06-02</EndDate>
</Dates>
</RecordB11112>
<RecordB11112>
<Dates>
<StartDate>2014-05-21</StartDate>
<EndDate>2014-05-21</EndDate>
</Dates>
</RecordB11112>
<RecordB11112>
<Dates>
<StartDate>2014-04-09</StartDate>
<EndDate>2014-04-09</EndDate>
</Dates>
</RecordB11112>
<RecordB11112>
<Dates>
<StartDate>2014-06-05</StartDate>
<EndDate>2014-06-05</EndDate>
</Dates>
</RecordB11112>
</RecordB1111>
</RecordB111>
</RecordB11>
</RecordB1>
</RecordB>
</Example>
Wanted output:
<?xml version="1.0" encoding="utf-8"?>
<Examples>
<Example>
<RecordA>
<RecordA1>
<RecordA11>
<ElementA11></ElementA11>
</RecordA11>
</RecordA1>
<RecordA2>
<ElementA2></ElementA2>
</RecordA2>
</RecordA>
<RecordB>
<RecordB1>
<ElementB1></ElementB1>
<RecordB11>
<ElementB11></ElementB11>
<RecordB111>
<RecordB1111>
<RecordB11111>
<ElementB11111></ElementB11111>
</RecordB11111>
<ElementB1111></ElementB1111>
<RecordB11112>
<Dates>
<StartDate>2014-05-29</StartDate>
<EndDate>2014-05-29</EndDate>
</Dates>
</RecordB11112>
<RecordB11112>
<Dates>
<StartDate>2014-05-21</StartDate>
<EndDate>2014-05-21</EndDate>
</Dates>
</RecordB11112>
</RecordB1111>
</RecordB111>
</RecordB11>
</RecordB1>
</RecordB>
</Example>
<Example>
<RecordA>
<RecordA1>
<RecordA11>
<ElementA11></ElementA11>
</RecordA11>
</RecordA1>
<RecordA2>
<ElementA2></ElementA2>
</RecordA2>
</RecordA>
<RecordB>
<RecordB1>
<ElementB1></ElementB1>
<RecordB11>
<ElementB11></ElementB11>
<RecordB111>
<RecordB1111>
<RecordB11111>
<ElementB11111></ElementB11111>
</RecordB11111>
<ElementB1111></ElementB1111>
<RecordB11112>
<Dates>
<StartDate>2014-04-09</StartDate>
<EndDate>2014-04-09</EndDate>
</Dates>
</RecordB11112>
</RecordB1111>
</RecordB111>
</RecordB11>
</RecordB1>
</RecordB>
</Example>
<Example>
<RecordA>
<RecordA1>
<RecordA11>
<ElementA11></ElementA11>
</RecordA11>
</RecordA1>
<RecordA2>
<ElementA2></ElementA2>
</RecordA2>
</RecordA>
<RecordB>
<RecordB1>
<ElementB1></ElementB1>
<RecordB11>
<ElementB11></ElementB11>
<RecordB111>
<RecordB1111>
<RecordB11111>
<ElementB11111></ElementB11111>
</RecordB11111>
<ElementB1111></ElementB1111>
<RecordB11112>
<Dates>
<StartDate>2014-06-02</StartDate>
<EndDate>2014-06-02</EndDate>
</Dates>
</RecordB11112>
<RecordB11112>
<Dates>
<StartDate>2014-06-05</StartDate>
<EndDate>2014-06-05</EndDate>
</Dates>
</RecordB11112>
</RecordB1111>
</RecordB111>
</RecordB11>
</RecordB1>
</RecordB>
</Example>
</Examples>
I think you can use Muenchian grouping to identify the first item in each group, then you need to recreate the tree for each group:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="group" match="RecordB11112" use="substring(Dates/StartDate, 1, 7)"/>
<xsl:template match="/">
<Examples>
<xsl:apply-templates select="//RecordB11112[generate-id() = generate-id(key('group', substring(Dates/StartDate, 1, 7))[1])]"/>
</Examples>
</xsl:template>
<xsl:template match="RecordB11112">
<xsl:variable name="to-be-copied" select="key('group', substring(Dates/StartDate, 1, 7))"/>
<xsl:apply-templates select="/*" mode="recreate">
<xsl:with-param name="to-be-copied" select="$to-be-copied"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="#* | node()" mode="recreate">
<xsl:param name="to-be-copied"/>
<xsl:copy>
<xsl:apply-templates select="#*" mode="recreate"/>
<xsl:apply-templates mode="recreate">
<xsl:with-param name="to-be-copied" select="$to-be-copied"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="RecordB11112" mode="recreate">
<xsl:param name="to-be-copied"/>
<xsl:if test="$to-be-copied[generate-id() = generate-id(current())]">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>