Search code examples
xsltattributeselement

Output element names and attributes via XSLT


I am working with an xml file and want to output the element names and attributes. I have it working to a point but have hit a brick wall with some nested elements. The issue I have is that my transform is not looking at any elements contained within the 'Subreport' section. It is these elements that I would like to include. Below you can find my xml data, my current transform, the output I have, and the output I would like to be creating. Any help would be very much appreciated. Many thanks in advance!

My xml file:

<?xml version="1.0" encoding="UTF-8" ?>
<CrystalReport>
    <Details Level="2">
        <Section SectionNumber="0">
            <Field Name="IDNUMBER1">
                <Value>xxx</Value>
            </Field>
            <Field Name="TITLE1">
                <Value>aaa</Value>
            </Field>
            <Subreport Name="Subreport1">
                <ReportHeader> </ReportHeader>
                <Details Level="1">
                    <Section SectionNumber="0">
                        <Field Name="LASTSUFFNAME1">
                            <Value>bbb</Value>
                        </Field>
                        <Field Name="FIRSTMIDNAME1">
                            <Value>ccc</Value>
                        </Field>
                    </Section>
                </Details>
                <Details Level="1">
                    <Section SectionNumber="0">
                        <Field Name="LASTSUFFNAME1">
                            <Value>ddd</Value>
                        </Field>
                        <Field Name="FIRSTMIDNAME1">
                            <Value>eee</Value>
                        </Field>
                    </Section>
                </Details>
            </Subreport>
            <Field Name="MKEY1">
                <FormattedValue>111</FormattedValue>
                <Value>111</Value>
            </Field>
            <Field Name="DATE21">
                <Value>2023-07-13</Value>
            </Field>
        </Section>
    </Details>
</CrystalReport>

My xsl so far:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>
  
    <xsl:template match="CrystalReport">
        <set>
            
            <xsl:apply-templates/>
            
        </set>
    </xsl:template>

    <xsl:template match="Details|Details/Section/Subreport/node()">
        <xsl:apply-templates select="Section/*"/>
        
    </xsl:template>
  
    <xsl:template match="Section/*">
        <xsl:for-each select=".">
        <attributeName>
            <xsl:value-of select="local-name()"/>
            <xsl:text> </xsl:text>
            <xsl:value-of select="@*"/>
          
        </attributeName>
        </xsl:for-each>
    </xsl:template> 
 
</xsl:stylesheet>

The output I am getting:

<set>
   <attributeName>Field IDNUMBER1</attributeName>
   <attributeName>Field TITLE1</attributeName>
   <attributeName>Subreport </attributeName>
   <attributeName>Field MKEY1</attributeName>
   <attributeName>Field DATE21</attributeName>
</set>

The output I would like:

<set>
   <attributeName>Field IDNUMBER1</attributeName>
   <attributeName>Field TITLE1</attributeName>
   <attributeName>Field LASTSUFFNAME1</attributeName>
   <attributeName>Field FIRSTMIDNAME1</attributeName>
   <attributeName>Field LASTSUFFNAME1</attributeName>
   <attributeName>Field FIRSTMIDNAME1</attributeName>
   <attributeName>Field MKEY1</attributeName>
   <attributeName>Field DATE21</attributeName>
</set>

Solution

  • Try this:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output omit-xml-declaration="yes" indent="yes"/>
      <xsl:strip-space elements="*"/>
      
      <xsl:template match="CrystalReport">
        <set>
          <xsl:apply-templates/>
        </set>
      </xsl:template>
      
      <!-- Match all node template: just apply-templates -->
      <xsl:template match="node()">
        <xsl:apply-templates select="node()"/>
      </xsl:template>
      
      <xsl:template match="Field">
        <attributeName>
          <xsl:value-of select="local-name()"/>
          <xsl:text> </xsl:text>
          <xsl:value-of select="@*"/>
        </attributeName>
      </xsl:template> 
      
    </xsl:stylesheet>
    

    Or even shorter:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output omit-xml-declaration="yes" indent="yes"/>
      <xsl:strip-space elements="*"/>
      
      <xsl:template match="CrystalReport">
        <set>
          <xsl:apply-templates select=".//Field"/>
        </set>
      </xsl:template>
      
      <xsl:template match="Field">
        <attributeName>
          <xsl:value-of select="local-name()"/>
          <xsl:text> </xsl:text>
          <xsl:value-of select="@*"/>
        </attributeName>
      </xsl:template> 
      
    </xsl:stylesheet>
    

    Both will result in:

    <set>
       <attributeName>Field IDNUMBER1</attributeName>
       <attributeName>Field TITLE1</attributeName>
       <attributeName>Field LASTSUFFNAME1</attributeName>
       <attributeName>Field FIRSTMIDNAME1</attributeName>
       <attributeName>Field LASTSUFFNAME1</attributeName>
       <attributeName>Field FIRSTMIDNAME1</attributeName>
       <attributeName>Field MKEY1</attributeName>
       <attributeName>Field DATE21</attributeName>
    </set>