Home > Net >  Need help converting to XML to TXT, without changing the sequence
Need help converting to XML to TXT, without changing the sequence

Time:06-10

I need help converting the XML tree structure to text format using XSLT but without altering the sequence of nodes in the XML

Sample XML is as follows

XML:

<File>
    <Record>
        <FH>
            <A>A1</A>
            <B>B1</B>
            <C>C1</C>
        </FH>
        <BH>
            <a>a1</a>
            <b>b1</b>
            <c>c1</c>
        </BH>
        <D>
            <d>d1</d>
            <e>e1</e>
            <f>f1</f>
        </D>
        <BCR>
            <g>g1</g>
            <h>h1</h>
            <i>i1</i>
        </BCR>
        <BH>
            <a>a2</a>
            <b>b2</b>
            <c>c2</c>
        </BH>
        <D>
            <d>d2</d>
            <e>e2</e>
            <f>f2</f>
        </D>
        <BCR>
            <g>g2</g>
            <h>h2</h>
            <i>i2</i>
        </BCR>
        <BH>
            <a>a3</a>
            <b>b3</b>
            <c>c3</c>
        </BH>
        <D>
            <d>d3</d>
            <e>e3</e>
            <f>f3</f>
        </D>
        <D>
            <d>d4</d>
            <e>e4</e>
            <f>f4</f>
        </D>
        <D>
            <d>d5</d>
            <e>e5</e>
            <f>f5</f>
        </D>
        <BCR>
            <g>g3</g>
            <h>h3</h>
            <i>i3</i>
        </BCR>
        <FCR>
            <D>D1</D>
            <E>E1</E>
            <F>F1</F>
        </FCR>
    </Record>
</File>

the XSLT I'm using is down below but it's changing the order of the nodes which is not desirable

XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" omit-xml-declaration="yes"/>    
    <xsl:template match="/">
        <xsl:for-each select="/File/Record/FH">                      
            <xsl:value-of select="A"/>
            <xsl:value-of select="B"/>
            <xsl:value-of select="C"/>
            <xsl:text>&#xa;</xsl:text>
        </xsl:for-each>
        <xsl:for-each select="/File/Record/BH">                       
            <xsl:value-of select="a"/>
            <xsl:value-of select="b"/>
            <xsl:value-of select="c"/>
            <xsl:text>&#xa;</xsl:text>
        </xsl:for-each>
        <xsl:for-each select="/File/Record/D">                      
            <xsl:value-of select="d"/>
            <xsl:value-of select="e"/>
            <xsl:value-of select="f"/>
            <xsl:text>&#xa;</xsl:text>
        </xsl:for-each>
        <xsl:for-each select="/File/Record/BCR">                        
            <xsl:value-of select="g"/>
            <xsl:value-of select="h"/>
            <xsl:value-of select="i"/>
            <xsl:text>&#xa;</xsl:text>
        </xsl:for-each>
        <xsl:for-each select="/File/Record/FCR">                    
            <xsl:value-of select="D"/>
            <xsl:value-of select="E"/>
            <xsl:value-of select="F"/>
            <xsl:text>&#xa;</xsl:text>            
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>
EXPECTED OUTPUT          |              ACTUAL OUTPUT 

A1B1C1                   |                  A1B1C1     
a1b1c1                   |                  a1b1c1     
d1e1f1                   |                  a2b2c2     
g1h1i1                   |                  a3b3c3     
a2b2c2                   |                  d1e1f1     
d2e2f2                   |                  d2e2f2     
g2h2i2                   |                  d3e3f3     
a3b3c3                   |                  d4e4f4     
d3e3f3                   |                  d5e5f5     
d4e4f4                   |                  g1h1i1     
d5e5f5                   |                  g2h2i2     
g3h3i3                   |                  g3h3i3     
D1E1F1                   |                  D1E1F1     

Is there any way we can alter the XSLT to preserve the sequence?

Thanks

CodePudding user response:

An XSLT-1.0 approach is the following. Its key feature is applying the templates in document order and not by tag name:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" omit-xml-declaration="yes"/>   
    <xsl:strip-space elements="*" />

    <xsl:template match="/File/Record">
        <xsl:apply-templates select="*" />
    </xsl:template>
    
    <xsl:template match="FH">
        <xsl:value-of select="A"/>
        <xsl:value-of select="B"/>
        <xsl:value-of select="C"/>
        <xsl:text>&#xa;</xsl:text>
    </xsl:template>
    
    <xsl:template match="BH">
        <xsl:value-of select="a"/>
        <xsl:value-of select="b"/>
        <xsl:value-of select="c"/>
        <xsl:text>&#xa;</xsl:text>
    </xsl:template>

    <xsl:template match="D">
        <xsl:value-of select="d"/>
        <xsl:value-of select="e"/>
        <xsl:value-of select="f"/>
        <xsl:text>&#xa;</xsl:text>
    </xsl:template>
    
    <xsl:template match="BCR">
        <xsl:value-of select="g"/>
        <xsl:value-of select="h"/>
        <xsl:value-of select="i"/>
        <xsl:text>&#xa;</xsl:text>
    </xsl:template>

    <xsl:template match="FCR">
        <xsl:value-of select="D"/>
        <xsl:value-of select="E"/>
        <xsl:value-of select="F"/>
        <xsl:text>&#xa;</xsl:text>            
    </xsl:template>
    
</xsl:stylesheet>

With XSLT-2.0, you can shorten this code:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" omit-xml-declaration="yes"/>    
    <xsl:strip-space elements="*" />
    
    <xsl:template match="/File/Record">
        <xsl:apply-templates select="*" />
    </xsl:template>
    
    <xsl:template match="FH">
        <xsl:value-of select="A,B,C,'&#xa;'" separator="" />
    </xsl:template>
    
    <xsl:template match="BH">
        <xsl:value-of select="a,b,c,'&#xa;'" separator="" />
    </xsl:template>

    <xsl:template match="D">
        <xsl:value-of select="d,e,f,'&#xa;'" separator="" />
    </xsl:template>
    
    <xsl:template match="BCR">
        <xsl:value-of select="g,h,i,'&#xa;'" separator="" />
    </xsl:template>

    <xsl:template match="FCR">
        <xsl:value-of select="D,E,F,'&#xa;'" separator="" />
    </xsl:template>
    
</xsl:stylesheet>

The result should be as desired in both cases.


And if you don't want to output by element name, but rather all children, you could simplify it further:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" omit-xml-declaration="yes"/>    
    <xsl:strip-space elements="*" />
    
    <xsl:template match="/File/Record">
        <xsl:apply-templates select="*" />
    </xsl:template>
    
    <xsl:template match="FH|BH|D|BCR|FCR">
        <xsl:value-of select="*,'&#xa;'" separator="" />
    </xsl:template>
    
</xsl:stylesheet>
  • Related