Search code examples
xmlxsltrecursionxml-parsingxslt-grouping

parsing though xml recursively using xslt


I have an Xml as follows:

<Soap:Envelope>
<Soap:Body>
<A>
   <B>Text</B>
   <C>Text</C>
   <D>
      <D1>Text</D1>
      <D2>
          <D3>Text</D3>
          <D4>Text</D4>
      </D2>
   </D>
   <E>
      <E1>
          <E2>
              <E3>Text</E3>
          </E2>
      </E1>
   </E>
</A>
</Soap:Body>
</Soap:Envelope>

How do I recursively parse through all the tags(I only know that I will be receiving an xml template), know the tag names and the "Text" in them using XSLT? I need to store the data in the format as below.Below answer works fine when I don't have and tags. How to get the output now?

A_B = Text1
A_C = Text2
A_D_D1 = Text3
A_D_D2_D3 = Text4
A_D_D2_D4 = Text5
A_E_E1_E2_E3 = Text6

I know that we need to write a recursive function as below. That is where I need some help.

<for-each select="./*">
   //Recursive Function
</for-each>

Thanks in Advance


Solution

  • The following stylesheet:

    XSLT 1.0

    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" encoding="UTF-8"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="*[text()]">
        <xsl:for-each select="ancestor-or-self::*">
            <xsl:value-of select="name()" />
            <xsl:if test="position()!=last()">
                <xsl:text>_</xsl:text>
            </xsl:if>
        </xsl:for-each>
        <xsl:text> = </xsl:text>
        <xsl:value-of select="." />
        <xsl:text>&#10;</xsl:text>
        <xsl:apply-templates select="*"/>
    </xsl:template>
    
    </xsl:stylesheet>
    

    applied to your example input, will return:

    A_B = Text
    A_C = Text
    A_D_D1 = Text
    A_D_D2_D3 = Text
    A_D_D2_D4 = Text
    A_E_E1_E2_E3 = Text
    

    Added:

    Your new requirement is not defined well enough (and your new XML is not well-formed). If I am guessing correctly, you want to exclude any namespaced element names from the concatenated path. If so, replace:

    <xsl:for-each select="ancestor-or-self::*">
    

    with:

    <xsl:for-each select="ancestor-or-self::*[not(namespace-uri())]">