Cannot achieve the desired result when converting the original document:
<fact>
<id>84f4ab12-64e5-4905-9a4f-8935addf7b31</id>
<decisionDate>2021-12-01</decisionDate>
<receiver>
<surname>Kim</surname>
<firstname>Alla</firstname>
<addressInfo>
<type>
<code>03</code>
<title>Actual residence</title>
</type>
<country>
<code>033</code>
<title>Actual residence country</title>
</country>
<postIndex>333333</postIndex>
<region>Region3</region>
</addressInfo>
<addressInfo>
<type>
<code>01</code>
<title>Permanent residence</title>
</type>
<country>
<code>011</code>
<title>Permanent residence country</title>
</country>
<postIndex>111111</postIndex>
<region>Region1</region>
</addressInfo>
<addressInfo>
<type>
<code>02</code>
<title>Temporary residence</title>
</type>
<country>
<code>022</code>
<title>Temporary residence country</title>
</country>
<postIndex>222222</postIndex>
<region>Region2</region>
</addressInfo>
</receiver>
</fact>
Requirements:
- convert each address block into a block with a name corresponding to the address type
- wrap the converted address blocks into one "Address"
- ensure that the blocks follow in a certain order in accordance with the xsd (PermanentResidence -> TemporaryResidence -> ActualResidence)
It was possible to achieve separate conversion of address blocks and wrapping into a common tag (based on xsl:key):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:key name="addressFollowing" match="addressInfo[preceding-sibling::*[1][self::addressInfo]]"
use="generate-id(preceding-sibling::addressInfo
[not(preceding-sibling::*[1][self::addressInfo])][1])"/>
<xsl:template match="addressInfo[not(preceding-sibling::*[1][self::addressInfo])]">
<xsl:element name="Address">
<xsl:call-template name="address"/>
<xsl:apply-templates mode="copy" select="key('addressFollowing', generate-id())">
<xsl:sort select="type/code" />
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<xsl:template match="addressInfo[preceding-sibling::*[1][self::addressInfo]]"/>
<xsl:template match="addressInfo" mode="copy" name="address">
<xsl:variable name="addressType">
<xsl:if test="(.//type//code)=01">
<xsl:value-of select="'PermanentResidence'"/>
</xsl:if>
<xsl:if test="(.//type//code)=02">
<xsl:value-of select="'TemporaryResidence'"/>
</xsl:if>
<xsl:if test="(.//type//code)=03">
<xsl:value-of select="'ActualResidence'"/>
</xsl:if>
</xsl:variable>
<xsl:element name="{$addressType}">
<xsl:element name="country">
<xsl:value-of select=".//country//code"/>
</xsl:element>
<xsl:element name="postIndex">
<xsl:value-of select=".//postIndex"/>
</xsl:element>
<xsl:element name="region">
<xsl:value-of select=".//region"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
and sorting of address blocks in the source document:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()">
<xsl:sort select="type/code" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The combined transformation ignores the sorting of the address blocks.
Please help to achieve the combined result in one xsl.
I tried to implement double transformation based on variables and modes, but nothing.
Main idea: sorting the document in first transform, then transform based on key.
Sorry for my English
UPDATE:
Desired result:
<fact>
<id>84f4ab12-64e5-4905-9a4f-8935addf7b31</id>
<decisionDate>2021-12-01</decisionDate>
<receiver>
<surname>Kim</surname>
<firstname>Alla</firstname>
<Address>
<PermanentResidence>
<country>011</country>
<postIndex>111111</postIndex>
<region>Region1</region>
</PermanentResidence>
<TemporaryResidence>
<country>022</country>
<postIndex>222222</postIndex>
<region>Region2</region>
</TemporaryResidence>
<ActualResidence>
<country>033</country>
<postIndex>333333</postIndex>
<region>Region3</region>
</ActualResidence>
</Address>
</receiver>
</fact>
>Solution :
Assuming XSLT 2, I think you could use
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="receiver">
<xsl:copy>
<xsl:for-each-group select="*" group-adjacent="boolean(self::addressInfo)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<Address>
<xsl:apply-templates select="current-group()">
<xsl:sort select="type/code"/>
</xsl:apply-templates>
</Address>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="addressInfo">
<xsl:variable name="addressType">
<xsl:if test="(.//type//code)=01">
<xsl:value-of select="'PermanentResidence'"/>
</xsl:if>
<xsl:if test="(.//type//code)=02">
<xsl:value-of select="'TemporaryResidence'"/>
</xsl:if>
<xsl:if test="(.//type//code)=03">
<xsl:value-of select="'ActualResidence'"/>
</xsl:if>
</xsl:variable>
<xsl:element name="{$addressType}">
<xsl:apply-templates select="country/code, postIndex, region"/>
</xsl:element>
</xsl:template>
<xsl:output indent="yes"/>
</xsl:stylesheet>