Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

XSL: sorting the document in first transform and then transform based on key

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:

  1. convert each address block into a block with a name corresponding to the address type
  2. wrap the converted address blocks into one "Address"
  3. 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):

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

<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>
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading