1

I am converting same multi tag elements into different records. In this i am using XSLT. I need help in XSLT. i am getting first tag result is correct, nut idont know why second tags not getting.

Explanation:: *In First CD :: Single atrist to many titles. I want to split this into defferent tags based on title. In Second CD:: First title by First artist and second title by second artist. I need this also in different(two) CD tags based on both title and artist.[First title-first artist and second title-second aritst].*

My Source XML is Like Following::

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<catalog>
    <cd>
        <title>Burlesque</title>        
        <title>Empire</title>
        <title>Emp</title>
        <title>Em</title>
        <artist>Bob Dylan</artist>
        <country>USA</country>
        <company>Columbia</company>
        <price>10.90</price>
        <year>1985</year>
    </cd>
    <cd>
        <title>Hide your</title>
        <title>heart</title>
        <artist>Bonnie</artist>
        <artist> Tyler</artist>
        <country>UK</country>
        <company>CBS Records</company>
        <price>9.90</price>
        <year>1988</year>
    </cd>
    <cd>
        <title/>
        <artist>Dolly Parton</artist>
        <country>USA</country>
        <company>RCA</company>
        <price>9.90</price>
        <year>1982</year>
    </cd>
</catalog>

XSLT ::

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  >
    <xsl:output method="xml" indent="yes"/>
    <xsl:key name="k" match="title" use="text()"/>
    <xsl:key name="l" match="artist" use="text()"/>
            <xsl:template match="/">
        <catalog>
            <xsl:apply-templates select="//cd/title | artist[not(node() = preceding-sibling::node())]"/>
        </catalog>
    </xsl:template>
    <xsl:template match="//cd">
        <xsl:param name="title" select="title"/>
        <xsl:param name="artist" select="artist"/>
        <cd>
            <xsl:copy-of select="key('k', $title)[not(node() = preceding-sibling::node())]"/>
            <xsl:copy-of select="key('l', $artist)[not(node() = preceding-sibling::node())]"/>
            <xsl:copy-of select="./*[name() != 'title' and 'artist']"/>
        </cd>
    </xsl:template>
    <xsl:template match="title">
            <xsl:apply-templates select="..">
            <xsl:with-param name="title" select="."/>
        </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

What i am getting ::

<?xml version="1.0" encoding="utf-8"?>
<catalog>
  <cd>
    <title>Burlesque</title>
    <artist>Bob Dylan</artist>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Empire</title>
    <artist>Bob Dylan</artist>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Emp</title>
    <artist>Bob Dylan</artist>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Em</title>
    <artist>Bob Dylan</artist>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Hide your</title>
    <artist>Bonnie</artist>
    <artist> Tyler</artist>
    <artist>Bonnie</artist>
    <artist> Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  <cd>
    <title>heart</title>
    <artist>Bonnie</artist>
    <artist> Tyler</artist>
    <artist>Bonnie</artist>
    <artist> Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  <cd>
    <artist>Dolly Parton</artist>
    <artist>Dolly Parton</artist>
    <country>USA</country>
    <company>RCA</company>
    <price>9.90</price>
    <year>1982</year>
  </cd>
 </catalog>

What I need ::

<?xml version="1.0" encoding="utf-8"?>
<catalog>
  <cd>
    <title>Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Empire</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Emp</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Em</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Hide your</title>
    <artist>Bonnie</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  <cd>
    <title>heart</title>
    <artist> Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  <cd>
    <artist>Dolly Parton</artist>
    <artist>Dolly Parton</artist>
    <country>USA</country>
    <company>RCA</company>
    <price>9.90</price>
    <year>1982</year>
  </cd>
</catalog>

Can any one help me in this ???

1 Answer 1

1

This is an application of a "generic XML shredding" transformation, where the parameter is set to the wanted leaf nodes and a minor post-processing is done to remove two top wrapping nodes from the result and re-wrap:

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext"
>
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>

     <xsl:param name="pLeafNodes" select="/*/*/title"/>

     <xsl:template match="/">
      <xsl:variable name="vrtfResult">
        <xsl:call-template name="StructRepro"/>
      </xsl:variable>

      <catalog>
       <xsl:copy-of select="ext:node-set($vrtfResult)/catalog/node()"/>
      </catalog>
     </xsl:template>

     <xsl:template name="StructRepro">
       <xsl:param name="pLeaves" select="$pLeafNodes"/>

       <xsl:for-each select="$pLeaves">
         <xsl:apply-templates mode="build" select="/*">
          <xsl:with-param name="pChild" select="."/>
          <xsl:with-param name="pLeaves" select="$pLeaves"/>
         </xsl:apply-templates>
       </xsl:for-each>
     </xsl:template>

      <xsl:template mode="build" match="node()|@*">
          <xsl:param name="pChild"/>
          <xsl:param name="pLeaves"/>

         <xsl:copy>
           <xsl:apply-templates mode="build" select="@*"/>

           <xsl:variable name="vLeafChild" select=
             "*[count(.|$pChild) = count($pChild)]"/>

           <xsl:choose>
            <xsl:when test="$vLeafChild">
             <xsl:apply-templates mode="build"
                 select="$vLeafChild
                        |
                          node()[not(count(.|$pLeaves) = count($pLeaves))]">
                 <xsl:with-param name="pChild" select="$pChild"/>
                 <xsl:with-param name="pLeaves" select="$pLeaves"/>
             </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
             <xsl:apply-templates mode="build" select=
             "node()[not(.//*[count(.|$pLeaves) = count($pLeaves)])
                    or
                     .//*[count(.|$pChild) = count($pChild)]
                    ]
             ">

                 <xsl:with-param name="pChild" select="$pChild"/>
                 <xsl:with-param name="pLeaves" select="$pLeaves"/>
             </xsl:apply-templates>
            </xsl:otherwise>
           </xsl:choose>
         </xsl:copy>
     </xsl:template>
     <xsl:template match="text()"/>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<catalog>
    <cd>
        <title>Burlesque</title>
        <title>Empire</title>
        <title>Emp</title>
        <title>Em</title>
        <artist>Bob Dylan</artist>
        <country>USA</country>
        <company>Columbia</company>
        <price>10.90</price>
        <year>1985</year>
    </cd>
    <cd>
        <title>Hide your</title>
        <title>heart</title>
        <artist>Bonnie</artist>
        <artist> Tyler</artist>
        <country>UK</country>
        <company>CBS Records</company>
        <price>9.90</price>
        <year>1988</year>
    </cd>
    <cd>
        <title/>
        <artist>Dolly Parton</artist>
        <country>USA</country>
        <company>RCA</company>
        <price>9.90</price>
        <year>1982</year>
    </cd>
</catalog>

the wanted, correct result is produced:

<catalog>
   <cd>
      <title>Burlesque</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Empire</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Emp</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Em</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Hide your</title>
      <artist>Bonnie</artist>
      <artist> Tyler</artist>
      <country>UK</country>
      <company>CBS Records</company>
      <price>9.90</price>
      <year>1988</year>
   </cd>
   <cd>
      <title>heart</title>
      <artist>Bonnie</artist>
      <artist> Tyler</artist>
      <country>UK</country>
      <company>CBS Records</company>
      <price>9.90</price>
      <year>1988</year>
   </cd>
   <cd>
      <title/>
      <artist>Dolly Parton</artist>
      <country>USA</country>
      <company>RCA</company>
      <price>9.90</price>
      <year>1982</year>
   </cd>
</catalog>

Update: The OP has changed his question and he is asking that for CDs with more than one artist there should be an additional split by artist.

Here is the solution -- it is a two-pass-processing. In the first pass the document is converted to a catalog in which each cd has a single artist. Then the second pass is the solution I already gave above:

<xsl:stylesheet version="1.0"
         xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
         xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
         <xsl:output omit-xml-declaration="yes" indent="yes"/>
         <xsl:strip-space elements="*"/>

         <xsl:template match="/">

          <xsl:variable name="vrtfPass1">
            <xsl:apply-templates mode="pass1"/>
          </xsl:variable>

          <xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/>

          <xsl:variable name="pLeafNodes" select="$vPass1/*/*/title"/>

          <xsl:variable name="vrtfResult">
            <xsl:call-template name="StructRepro">
              <xsl:with-param name="pLeaves" select="$pLeafNodes"/>
            </xsl:call-template>
          </xsl:variable>

          <catalog>
            <xsl:copy-of select="ext:node-set($vrtfResult)/catalog/node()"/>
          </catalog>
         </xsl:template>

         <xsl:template name="StructRepro">
           <xsl:param name="pLeaves"/>

           <xsl:variable name="vDoc" select=
               "$pLeaves[1]/ancestor::node()[last()]"/>

           <xsl:for-each select="$pLeaves">
             <xsl:apply-templates mode="build" select="$vDoc/*">
              <xsl:with-param name="pChild" select="."/>
              <xsl:with-param name="pLeaves" select="$pLeaves"/>
             </xsl:apply-templates>
           </xsl:for-each>
         </xsl:template>

          <xsl:template mode="build" match="node()|@*">
              <xsl:param name="pChild"/>
              <xsl:param name="pLeaves"/>

             <xsl:copy>
               <xsl:apply-templates mode="build" select="@*"/>

               <xsl:variable name="vLeafChild" select=
                 "*[count(.|$pChild) = count($pChild)]"/>

               <xsl:choose>
                <xsl:when test="$vLeafChild">
                 <xsl:apply-templates mode="build"
                     select="$vLeafChild
                            |
                              node()[not(count(.|$pLeaves) = count($pLeaves))]">
                     <xsl:with-param name="pChild" select="$pChild"/>
                     <xsl:with-param name="pLeaves" select="$pLeaves"/>
                 </xsl:apply-templates>
                </xsl:when>
                <xsl:otherwise>
                 <xsl:apply-templates mode="build" select=
                 "node()[not(.//*[count(.|$pLeaves) = count($pLeaves)])
                        or
                         .//*[count(.|$pChild) = count($pChild)]
                        ]
                 ">

                     <xsl:with-param name="pChild" select="$pChild"/>
                     <xsl:with-param name="pLeaves" select="$pLeaves"/>
                 </xsl:apply-templates>
                </xsl:otherwise>
               </xsl:choose>
             </xsl:copy>
         </xsl:template>

 <xsl:template match="node()|@*" mode="pass1">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*" mode="pass1"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="cd[artist[2]]" mode="pass1">
  <xsl:for-each select="artist">
   <xsl:apply-templates select=".." mode="singleArtist">
     <xsl:with-param name="pArtistPos" select="position()"/>
   </xsl:apply-templates>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="cd" mode="singleArtist">
   <xsl:param name="pArtistPos"/>

   <cd>
     <xsl:apply-templates mode="pass1" select=
        "title[position() = $pArtistPos]"/>
     <xsl:apply-templates mode="pass1" select=
         "artist[position() = $pArtistPos]"/>
     <xsl:apply-templates mode="pass1" select=
         "node()[not(self::title or self::artist)]"/>
   </cd>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

When this transformation is applied on the same XML document (above), the result now satisfies the additional requirements:

<catalog>
   <cd>
      <title>Burlesque</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Empire</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Emp</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Em</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Hide your</title>
      <artist>Bonnie</artist>
      <country>UK</country>
      <company>CBS Records</company>
      <price>9.90</price>
      <year>1988</year>
   </cd>
   <cd>
      <title>heart</title>
      <artist> Tyler</artist>
      <country>UK</country>
      <company>CBS Records</company>
      <price>9.90</price>
      <year>1988</year>
   </cd>
   <cd>
      <title/>
      <artist>Dolly Parton</artist>
      <country>USA</country>
      <company>RCA</company>
      <price>9.90</price>
      <year>1982</year>
   </cd>
</catalog>
Sign up to request clarification or add additional context in comments.

8 Comments

Once please see <artist> tag. It is coming two times. <cd> <title>Hide your</title> <artist>Bonnie</artist> <artist> Tyler</artist> <country>UK</country> <company>CBS Records</company> <price>9.90</price> <year>1988</year> </cd> <cd> <title>heart</title> <artist>Bonnie</artist> <artist> Tyler</artist> <country>UK</country> <company>CBS Records</company> <price>9.90</price> <year>1988</year> </cd>
@ram.bi: I see, so what you want is even more complicated than simple shredding and different artists need to be combined with different titles. But then you must edit the question and expain which artist should get matched to which title. It is possible that both artists sing the same song (which I assumed), or that both artists sing both songs, or that the first artist sings the second song (imagine artists and songs are ordered alphabetically by name. Please, edit the question and specify the exact rules.
Hi Dimitre, Thanks for responce i added explanation, can please put me in the right deirection.
@ram.bi: Sure, do have a look at my update at the end of this answer :)
I am using that XML and XSLT in SQL Server Integration Services XML Task at that time i am getting an error like:: Cannot find the script or external object that implements prefix 'ext'. i dont have much knowledge on XSLT can please help me ?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.