Sanjay Janardhan
Sanjay Janardhan

Reputation: 37

Generate XSL (Denormalise format) for Nested XML

I need to flatten my XML as the file size very huge and nested level, I am creating the XSL file manually. Below is my sample scenario of content of the XML file-

<StudentDetail>
  <SchoolName>SSHPS</SchoolName>
  <SchoolEstablishedYear>1990</SchoolEstablishedYear>
  <ClassDetails>
    <ClassDetail>
      <ClassStartedYear>1990</ClassStartedYear>
      <Section ID="12345">
        <SectioName>Section A</SectioName>
        <Students>
          <Student ID="1">
            <StudentName>John</StudentName>
            <Address>
              <HomeNumber>10</HomeNumber>
              <StreetName>Avenue</StreetName>
            </Address>
          </Student>
          <Student ID="2">
            <StudentName>Steve</StudentName>
          </Student>
        </Students>
      </Section>
      <Section ID="123456">
        <SectioName>Section B</SectioName>
        <Students>
          <Student ID="100">
            <StudentName>Dia</StudentName>
            <Age>6</Age>
          </Student>
          <Student ID="101">
            <StudentName>Kevin</StudentName>
          </Student>
        </Students>
      </Section>
    </ClassDetail>
    <ClassDetail>
      <ClassStartedYear>1995</ClassStartedYear>
      <Section ID="543466">
        <SectioName>Section A</SectioName>
        <Students>
          <Student ID="200">
            <StudentName>Dia</StudentName>
            <Muncipality>
              <AreaCode>100</AreaCode>
              <Areaname>GRAND</Areaname>
            </Muncipality>
          </Student>
          <Student ID="201">
            <StudentName>Liva</StudentName>
          </Student>
        </Students>
      </Section>
      <Section ID="7543466">
        <SectioName>Section A</SectioName>
        <Students>
          <Student ID="300">
            <StudentName>Zane</StudentName>
          </Student>
          <Student ID="301">
            <StudentName>Susan</StudentName>
          </Student>
        </Students>
      </Section>
    </ClassDetail>
  </ClassDetails>
</StudentDetail>

Below is the required format of the XML-

<StudentDetail>
    <Student>
        <SchoolName>SSHPS</SchoolName>
        <SchoolEstablishedYear>1990</SchoolEstablishedYear>
        <ClassStartedYear>1990</ClassStartedYear>
        <SectionID>12345</SectionID>
        <SectioName>Section A</SectioName>
        <StudentID>1</StudentID>
        <StudentName>John</StudentName>
        <Address_HomeNumber>10</Address_HomeNumber>
        <Address_StreetName>Avenue</Address_StreetName>
        <Age> </Age>
        <Muncipality_AreaCode></Muncipality_AreaCode>
        <Muncipality_Areaname></Muncipality_Areaname>
    </Student>
   .
   .
   .
    <Student>
        <SchoolName>SSHPS</SchoolName>
        <SchoolEstablishedYear>1990</SchoolEstablishedYear>
        <ClassStartedYear>1995</ClassStartedYear>
        <SectionID>7543466</SectionID>
        <SectioName>Section A</SectioName>
        <StudentID>100</StudentID>
        <StudentName>Dia</StudentName>
        <Address_HomeNumber></Address_HomeNumber>
        <Address_StreetName></Address_StreetName>
        <Age></Age>
        <Muncipality_AreaCode>100</Muncipality_AreaCode>
        <Muncipality_Areaname>GRAND</Muncipality_Areaname>
    </Student>
</StudentDetail>

I have generated the XSL template, I am not able to load this as there is some error within this-

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="/">
    <StudentDetail>
        <xsl:for-each select="StudentDetail/ClassDetails">
         <Student>
            <SchoolName><xsl:value-of select="StudentDetail/SchoolName"/></SchoolName>
            <SchoolEstablishedYear><xsl:value-of select="StudentDetail/SchoolEstablishedYear"/></SchoolEstablishedYear>
            <ClassStartedYear><xsl:value-of select="StudentDetail/ClassDetails/ClassDetail/ClassStartedYear"/></ClassStartedYear>
            <StudentID><xsl:value-of select="StudentDetail/ClassDetails/Section/@ID"/></StudentID>
            <SectioName><xsl:value-of select="StudentDetail/ClassDetails/ClassDetail/Section/SectionName"/></SectioName>
            <StudentID><xsl:value-of select="StudentDetail/ClassDetails/ClassDetail/Section/Students/Student/@ID"/></StudentID>
            <StudentName><xsl:value-of select="StudentDetail/ClassDetails/ClassDetail/Section/Students/Student"/></StudentName>
            <Address_HomeNumber><xsl:value-of select="StudentDetail/ClassDetails/ClassDetail/Section/Students/Student/Address/HomeNumber"/></Address_HomeNumber>
            <Address_StreetName><xsl:value-of select="StudentDetail/ClassDetails/ClassDetail/Section/Students/Student/Address/StreetName"/></Address_StreetName>
            <Age><xsl:value-of select="StudentDetail/ClassDetails/ClassDetail/Section/Students/Student/Age"/></Age>
            <Muncipality_AreaCode><xsl:value-of select="StudentDetail/ClassDetails/ClassDetail/Section/Students/Student/Muncipality/AreaCode"/></Muncipality_AreaCode>
            <Muncipality_Areaname><xsl:value-of select="StudentDetail/ClassDetails/ClassDetail/Section/Students/Student/Muncipality/Areaname"/></Muncipality_Areaname>
         </Student>
        </xsl:for-each>
    </StudentDetail>       
</xsl:template>

I am new to handling the XML, I am stuck with handling the nested XML

Upvotes: 0

Views: 50

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 116982

The main problem with your stylesheet is that you are creating a Student for each ClassDetails instead of for each Student.

Instead of:

<xsl:for-each select="StudentDetail/ClassDetails">
    <Student>
        <!-- data -->
    </Student>
</xsl:for-each>

you should be doing:

<xsl:for-each select="StudentDetail/ClassDetails/ClassDetail/Section/Students/Student">
    <Student>
        <!-- data -->
    </Student>
</xsl:for-each>

Then, within the Student element, you need to retrieve data from the ancestor School, ClassDetail and Section parts, as well as from the child elements of the current Student node.

In order to minimize the need to repeatedly navigate up and down the tree, I would suggest putting the ancestor parts details in variables and access them from there:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/StudentDetail">
    <xsl:copy>
        <xsl:variable name="school-details" select="SchoolName | SchoolEstablishedYear"/>
        <xsl:for-each select="ClassDetails/ClassDetail">
            <xsl:variable name="class-details" select="ClassStartedYear"/>
            <xsl:for-each select="Section">
                <xsl:variable name="section-details">
                    <SectionID>
                        <xsl:value-of select="@ID"/>
                    </SectionID>
                    <xsl:copy-of select="SectioName"/>
                </xsl:variable>
                <xsl:for-each select="Students/Student">
                    <xsl:copy>
                        <xsl:copy-of select="$school-details | $class-details"/>
                        <xsl:copy-of select="$section-details"/>
                        <StudentID>
                            <xsl:value-of select="@ID"/>
                        </StudentID>
                        <xsl:copy-of select="StudentName"/>
                        <Address_HomeNumber>
                            <xsl:value-of select="Address/HomeNumber"/>
                        </Address_HomeNumber>
                        <Address_StreetName>
                            <xsl:value-of select="Address/StreetName"/>
                        </Address_StreetName>
                        <Age>
                            <xsl:value-of select="Age"/>
                        </Age>
                        <Muncipality_AreaCode>
                            <xsl:value-of select="Muncipality/AreaCode"/>
                        </Muncipality_AreaCode>
                        <Muncipality_Areaname>
                            <xsl:value-of select="Muncipality/Areaname"/>
                        </Muncipality_Areaname>
                    </xsl:copy>
                </xsl:for-each>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Upvotes: 1

Michael Kay
Michael Kay

Reputation: 163302

Firstly, I'm surprised that anyone would want to turn this nicely structured input into this badly structured output. But we're not here to discuss that.

Secondly, I'm confused by the statement that you are "generating the XSL manually". I would have thought you were either creating it manually, or you were generating it programmatically, and it's not clear which of those is the case here.

Third, you've told us there is "some error" in your generated XSL, but you haven't told us what that error is. The only error I can see is a missing close tag for the xsl:stylesheet element, and that's presumably a typo. If you're getting an error, tell us what the error is.

Fourth, there seems to be a much simpler approach. As far as I can see, you can achieve the desired output by applying three rules:

  • If an element has child elements, just process its children

  • If an element has an ID attribute, change <X ID="x"/> to <XID>x</XID> and then process its children

  • If an element has a text node child, copy it unchanged.

The first rule corresponds to the default XSLT processing rule; the other two rules can be simply expessed in XSLT as:

<xsl:template match="*[@ID]">
  <xsl:element name="{name()}ID">
    <xsl:value-of select="@ID"/>
  </xsl:element>
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="*[text()]">
  <xsl:copy-of select="."/>
</xsl:template>

Upvotes: 1

Related Questions