Flatten nested xml using xslt

Viewed 38

I am facing challenging to flatten a nested xml file using xslt.

Input xml:

<?xml version="1.0" encoding="UTF-8"?>
<group xmlns="http://group.com/group">
    <sub-group>
        <group-name>group1</group-name>
        <group-parameter1>
            <group-parameter11>param11</group-parameter11>
            <group-parameter21>param21</group-parameter21>
        </group-parameter1>
        <group-parameter2>
            <group-parameter21>param21</group-parameter21>
        </group-parameter2>
    </sub-group>
    <sub-group>
        <group-name>group2</group-name>
        <group-parameter1>
            <group-parameter11>param11</group-parameter11>
            <group-parameter21>param21</group-parameter21>
        </group-parameter1>
        <group-parameter3>
            <group-parameter31>param31</group-parameter31>
        </group-parameter3>
    </sub-group>
</group>

Expected output:

<?xml version="1.0" encoding="UTF-8"?>
<group xmlns="http://group.com/group">
    <sub-group>
        <group-name>group1</group-name>
        <sub-group.group-parameter1.group-parameter11 xmlns="">param11</sub-group.group-parameter1.group-parameter11>
        <sub-group.group-parameter1.group-parameter21 xmlns="">param21</sub-group.group-parameter1.group-parameter21>
        <sub-group.group-parameter2.group-parameter21 xmlns="">param21</sub-group.group-parameter2.group-parameter21>
    </sub-group>
    <sub-group>
        <group-name>group2</group-name>
        <sub-group.group-parameter1.group-parameter11 xmlns="">param11</sub-group.group-parameter1.group-parameter11>
        <sub-group.group-parameter1.group-parameter21 xmlns="">param21</sub-group.group-parameter1.group-parameter21>
        <sub-group.group-parameter3.group-parameter31 xmlns="">param31</sub-group.group-parameter3.group-parameter31>
    </sub-group>
</group>

If I use the below xslt file to transform the input xml, it does not provide the expected output: xsl file:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">
    
    <xsl:mode streamable="no"/>
    
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates select="descendant::*[not(*)]"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="text()">
        <xsl:element name="{string-join(ancestor::*[position() lt last()]/name(), '.')}">
            <xsl:value-of select="."/>
        </xsl:element>
    </xsl:template>
    
</xsl:stylesheet>

output using the above xsl file:

<?xml version="1.0" encoding="UTF-8"?>
<group xmlns="http://group.com/group">
   <sub-group.group-name xmlns="">group1</sub-group.group-name>
   <sub-group.group-parameter1.group-parameter11 xmlns="">param11</sub-group.group-parameter1.group-parameter11>
   <sub-group.group-parameter1.group-parameter21 xmlns="">param21</sub-group.group-parameter1.group-parameter21>
   <sub-group.group-parameter2.group-parameter21 xmlns="">param21</sub-group.group-parameter2.group-parameter21>
   <sub-group.group-name xmlns="">group2</sub-group.group-name>
   <sub-group.group-parameter1.group-parameter11 xmlns="">param11</sub-group.group-parameter1.group-parameter11>
   <sub-group.group-parameter1.group-parameter21 xmlns="">param21</sub-group.group-parameter1.group-parameter21>
   <sub-group.group-parameter3.group-parameter31 xmlns="">param31</sub-group.group-parameter3.group-parameter31>
</group>

Any idea on how to fix this. By the way, I want to use a general code so that it can be used for other type of input file as well.

Regards, Ashish

1 Answers

Please try the following solution.

It is using a so called Identity Transform pattern.

XSLT Fiddle

Input XML

<?xml version="1.0" encoding="UTF-8"?>
<group xmlns="http://group.com/group">
    <sub-group>
        <group-name>group1</group-name>
        <group-parameter1>
            <group-parameter11>param11</group-parameter11>
            <group-parameter21>param21</group-parameter21>
        </group-parameter1>
        <group-parameter2>
            <group-parameter21>param21</group-parameter21>
        </group-parameter2>
    </sub-group>
    <sub-group>
        <group-name>group2</group-name>
        <group-parameter1>
            <group-parameter11>param11</group-parameter11>
            <group-parameter21>param21</group-parameter21>
        </group-parameter1>
        <group-parameter3>
            <group-parameter31>param31</group-parameter31>
        </group-parameter3>
    </sub-group>
</group>

XSLT

<?xml version='1.0'?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xpath-default-namespace="http://group.com/group"
    exclude-result-prefixes="#all">
    <xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="no"/>
   <xsl:strip-space elements="*"/>

   <xsl:mode on-no-match="shallow-copy"/>

    <xsl:template match="sub-group/*[starts-with(local-name(.), 'group-parameter')]">
        <xsl:for-each select="./*">
            <xsl:element name="{string-join((ancestor::*[position() lt last()]/local-name(), local-name()), '.')}" namespace="http://group.com/group">
                <xsl:value-of select="."/>
            </xsl:element>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

Output XML

<?xml version='1.0' encoding='utf-8' ?>
<group xmlns="http://group.com/group">
  <sub-group>
    <group-name>group1</group-name>
    <sub-group.group-parameter1.group-parameter11>param11</sub-group.group-parameter1.group-parameter11>
    <sub-group.group-parameter1.group-parameter21>param21</sub-group.group-parameter1.group-parameter21>
    <sub-group.group-parameter2.group-parameter21>param21</sub-group.group-parameter2.group-parameter21>
  </sub-group>
  <sub-group>
    <group-name>group2</group-name>
    <sub-group.group-parameter1.group-parameter11>param11</sub-group.group-parameter1.group-parameter11>
    <sub-group.group-parameter1.group-parameter21>param21</sub-group.group-parameter1.group-parameter21>
    <sub-group.group-parameter3.group-parameter31>param31</sub-group.group-parameter3.group-parameter31>
  </sub-group>
</group>
Related