Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old November 30th, 2011, 04:50 AM
Registered User
 
Join Date: Nov 2011
Posts: 6
Thanks: 3
Thanked 0 Times in 0 Posts
Default Recursive XLST

Hi All,

I am trying to achieve one XSLT mapping requirement for the below source structure.The values under "Level" and "Parent" source field dynamically determine the target structure node "SPM" occurrence and its hierachy on the target side.
Code:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:MT_Test xmlns:ns0="http://test">
   <Header>
      <Name>xx</Name>
      <Age>12</Age>
      <Item>
         <Level>1</Level>
         <Parent>0</Parent>
         <Company>A</Company>
      </Item>
      <Item>
         <Level>2</Level>
         <Parent>1</Parent>
         <Company>B</Company>
      </Item>
      <Item>
         <Level>3</Level>
         <Parent>2</Parent>
         <Company>C</Company>
      </Item>
      <Item>
         <Level>2</Level>
         <Parent>1</Parent>
         <Company>D</Company>
      </Item>
      <Item>
         <Level>3</Level>
         <Parent>2</Parent>
         <Company>E</Company>
      </Item>
      <Item>
         <Level>3</Level>
         <Parent>2</Parent>
         <Company>F</Company>
      </Item>
   </Header>
</ns0:MT_Test>
Target:

Code:
<?xml version="1.0" encoding="UTF-8"?>
<MT_Target xmlns:ns0="http://test">
   <Records>
      <Name>xx</Name>
      <Age>12</Age>
      <SPM>
         <Level>1</Level>
         <Parent>0</Parent>
         <Company>A</Company>
              <SPM>
            <Level>2</Level>
            <Parent>1</Parent>
            <Company>B</Company>
                   <SPM>
                   <Level>3</Level>
                   <Parent>2</Parent>
                   <Company>C</Company>
                    </SPM>
              </SPM>
              <SPM>
                  <Level>2</Level>
                  <Parent>1</Parent>
                  <Company>D</Company>
                  <SPM>
                     <Level>3</Level>
                     <Parent>2</Parent>
                     <Company>E</Company>
                  </SPM>
                  <SPM>
                    <Level>3</Level>
                     <Parent>2</Parent>
                     <Company>F</Company>
                 </SPM> 
             </SPM>
      </SPM>
   </Records>
</MT_Target>


Please help.

Thanks!!
 
Old November 30th, 2011, 10:26 AM
Registered User
 
Join Date: Nov 2011
Posts: 6
Thanks: 3
Thanked 0 Times in 0 Posts
Default

Any inputs will highly be appreciated??

Thanks in advance!!
 
Old November 30th, 2011, 11:45 AM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

I am trying to understand your requirements. I run your target output through a pretty printer, then it looks like this:
Code:
<MT_Target xmlns:ns0="http://test">
  <Records>
    <Name>xx</Name>
    <Age>12</Age>
    <SPM>
      <Level>1</Level>
      <Parent>0</Parent>
      <Company>A</Company>
      <SPM>
        <Level>2</Level>
        <Parent>1</Parent>
        <Company>B</Company>
        <SPM>
          <Level>3</Level>
          <Parent>2</Parent>
          <Company>C</Company>
        </SPM>
      </SPM>
      <SPM>
        <Level>2</Level>
        <Parent>1</Parent>
        <Company>D</Company>
        <SPM>
          <Level>3</Level>
          <Parent>2</Parent>
          <Company>E</Company>
        </SPM>
        <SPM>
          <Level>3</Level>
          <Parent>2</Parent>
          <Company>F</Company>
        </SPM>
      </SPM>
    </SPM>
  </Records>
</MT_Target>
How is the parent -> child relationship determined? As the "Item" elements in the input XML don't have any id I am not sure how to determine to which parent "Item" a child "Item" belongs. Is that simply by sibling relation in the XML input?

And please state whether you want an XSLT 2.0 or 1.0 solution as grouping is much easier in XSLT 2.0.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
The Following User Says Thank You to Martin Honnen For This Useful Post:
amit_sri24 (December 1st, 2011)
 
Old November 30th, 2011, 11:49 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

You will find an example of a stylesheet that tackles a similar problem (converting a hierarchy expressed through level numbers into one expressed through XML nesting) here:

http://www.saxonica.com/papers/ideadb-1.1/mhk-paper.xml
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
 
Old November 30th, 2011, 12:38 PM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

Assuming you can use XSLT 2.0 as support by Saxon 9 or AltovaXML and the parent "Item" is the nearest preceding sibling "Item" with a "Level" being one less the actual level you can use code like the following:
Code:
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="mf xs"
  version="2.0">
  
  <xsl:param name="start-level" as="xs:integer" select="1"/>
  
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  
  <xsl:function name="mf:group" as="element(SMP)*">
    <xsl:param name="items" as="element(Item)*"/>
    <xsl:param name="level" as="xs:integer"/>
    <xsl:for-each-group select="$items" group-starting-with="Item[Level = $level]">
      <SMP>
        <xsl:apply-templates select="*"/>
        <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
      </SMP>
    </xsl:for-each-group>
  </xsl:function>
  
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Header">
    <xsl:copy>
      <xsl:apply-templates select="Name, Age"/>
      <xsl:sequence select="mf:group(Item, $start-level)"/>
    </xsl:copy>
  </xsl:template>
  
</xsl:stylesheet>
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
The Following User Says Thank You to Martin Honnen For This Useful Post:
amit_sri24 (December 1st, 2011)
 
Old December 1st, 2011, 02:50 AM
Registered User
 
Join Date: Nov 2011
Posts: 6
Thanks: 3
Thanked 0 Times in 0 Posts
Default

Hi Martin/Michael,

Thanks for ur reply.
I am using XSLT 1.0
>>How is the parent -> child relationship determined?

In the source structure the values under Level and Parent elements(under Item) will determine the hirearchy of SPM node on the target structure.

Please allow me to explain this using one example...
1) under first Item occ Level=1 and Parent=0
so the output will be :
<SPM>
<Level>1</Level>
<Parent>0</Parent>
<Company>A</Company>
</SPM>

2)If we have one more item i.e under Second Item node values under element Level=2 and Parent=1 respectively
so the output will be :
Code:
   <SPM>
         <Level>1</Level>
         <Parent>0</Parent>
         <Company>A</Company>
         <SPM>
            <Level>2</Level>
            <Parent>1</Parent>
             <Company>B</Company>
         </SPM>
      </SPM>
3) Now consider if i have third Item occurrence on the source side having Level=3 and Parent=2 as the element values
Output will be:
Code:
 <SPM>
         <Level>1</Level>
         <Parent>0</Parent>
         <Company>A</Company>
         <SPM>
            <Level>2</Level>
            <Parent>1</Parent>
            <Company>B</Company>
            <SPM>
               <Level>3</Level>
               <Parent>2</Parent>
               <Company>C</Company>
            </SPM>
         </SPM>
      </SPM>
4) (Now the catch is here) Fourth Item occurrence on the source side having Level=2 and Parent=1 (again) as the element values
Output will be:


Code:
 <SPM>
         <Level>1</Level>
         <Parent>0</Parent>
          <Company>A</Company>
         <SPM>
            <Level>2</Level>
            <Parent>1</Parent>
             <Company>B</Company>
            <SPM>
               <Level>3</Level>
               <Parent>2</Parent>
                <Company>C</Company>
            </SPM>
         </SPM>
          <SPM>
               <Level>2</Level>
               <Parent>1</Parent>
               <Company>D</Company>
            </SPM>
      </SPM>
The values in Level and Parent will always be having (n,n-1) relation....

And one more thing please let me know how to assign points??

Thanks!!
 
Old December 1st, 2011, 05:10 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

>I am using XSLT 1.0

Then you are a brave man (or woman). I'm afraid I don't have the time or patience to help you solve this using XSLT 1.0.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
 
Old December 1st, 2011, 05:41 AM
Registered User
 
Join Date: Nov 2011
Posts: 6
Thanks: 3
Thanked 0 Times in 0 Posts
Default

Hi,
I can able to make a basic skelton of xslt but cant able to make the logic work when the Level and Parent starts repeating again for instance the case number 4 explained in my previous post
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="http://test">
   <xsl:template match="/">
      <MT_Target>
         <Records>
            <Name>
               <xsl:value-of select="ns0:MT_Test/SPL_Header/Name"/>
            </Name>
            <Age>
               <xsl:value-of select="ns0:MT_Test/SPL_Header/Age"/>
            </Age>
            <xsl:variable name="Max_No">
               <xsl:value-of select="count(//SPL_Item)"/>
            </xsl:variable>
            <xsl:call-template name="Loop">
               <xsl:with-param name="i">1</xsl:with-param>
               <xsl:with-param name="k" select="$Max_No"/>
            </xsl:call-template>
         </Records>
      </MT_Target>
   </xsl:template>
   <xsl:template name="Loop">
      <xsl:param name="i"/>
      <xsl:param name="k"/>
      <xsl:if test="$i &lt;= $k">
         <xsl:variable name="kk">
            <xsl:value-of select="//SPL_Item[$i]/Parent"/>
         </xsl:variable>
         <xsl:variable name="kk1">
            <xsl:value-of select="//SPL_Item[$i+1]/Parent"/>
         </xsl:variable>
         <SPM>
            <Level>
               <xsl:value-of select="//SPL_Item[$i]/Level"/>
            </Level>
            <Parent>
               <xsl:value-of select="//SPL_Item[$i]/Parent"/>
            </Parent>
            <Company>
               <xsl:value-of select="//SPL_Item[$i]/Company"/>
            </Company>
            <xsl:if test="$kk1&gt;$kk">
               <xsl:call-template name="Loop">
                  <xsl:with-param name="i">
                     <xsl:value-of select="$i + 1"/>
                  </xsl:with-param>
                  <xsl:with-param name="k">
                     <xsl:value-of select="$k"/>
                  </xsl:with-param>
               </xsl:call-template>
            </xsl:if>
         </SPM>
      </xsl:if>
   </xsl:template>
</xsl:stylesheet>
 
Old December 1st, 2011, 05:54 AM
samjudson's Avatar
Friend of Wrox
 
Join Date: Aug 2007
Posts: 2,128
Thanks: 1
Thanked 189 Times in 188 Posts
Default

The following seems to work:

Code:
<xsl:template match="ns0:MT_Test">
	<MT_Test>
		<xsl:apply-templates/>
	</MT_Test>
</xsl:template>

<xsl:template match="Header">
	<Records>
		<Name><xsl:value-of select="Name"/></Name>
		<Age><xsl:value-of select="Age"/></Age>
    	<xsl:call-template name="process_items">
    		<xsl:with-param name="level" select="1"/>
    		<xsl:with-param name="items" select="Item"/>
    	</xsl:call-template>
	</Records>
</xsl:template>

<xsl:template name="process_items">
  <xsl:param name="level"/>
  <xsl:param name="items"/>

  <xsl:for-each select="$items[Level=$level]">
  	<xsl:variable name="this" select="."/>
  	<SPM>
  	  <Level><xsl:value-of select="Level"/></Level>
  	  <Parent><xsl:value-of select="Parent"/></Parent>
  	  <Company><xsl:value-of select="Company"/></Company>
  	  
  	  <xsl:call-template name="process_items">
  	  	<xsl:with-param name="level" select="$level+1"/>
  	  	<xsl:with-param name="items" select="following-sibling::Item[generate-id(preceding-sibling::Item[Level=$level][1])=generate-id($this)]"/>
  	  </xsl:call-template>
  	</SPM>
  </xsl:for-each>
</xsl:template>
__________________
/- Sam Judson : Wrox Technical Editor -/

Think before you post: What have you tried?
The Following User Says Thank You to samjudson For This Useful Post:
amit_sri24 (December 1st, 2011)
 
Old December 1st, 2011, 07:13 AM
Registered User
 
Join Date: Nov 2011
Posts: 6
Thanks: 3
Thanked 0 Times in 0 Posts
Default

Hi Sam,
That was an awesome answer...u made my day

Thanks!!





Similar Threads
Thread Thread Starter Forum Replies Last Post
Beginner Problem with XLST amendir XSLT 2 July 6th, 2011 02:26 PM
XLST Replace rangeshram XSLT 1 November 23rd, 2009 07:50 AM
how to use pagination in xsl or xlst vijayanmsc XSLT 1 June 6th, 2006 06:07 AM
XLST Multiple Foreach deepsweech XSLT 3 June 20th, 2005 09:46 AM
XLST vs Sever Controls skin XSLT 3 July 11th, 2003 01:29 PM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.