4

I am writing xsl stylesheet to extract information from iTunes Music Library. xml file.

I want to store track information of playlists in an array and later iterate over them to get more information. I am confused how to store values in an array in xslt?

My attempt is here:

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" /> 
    <xsl:template match="/"> 

    <xsl:variable name="tracks"
           select="plist/dict/array/dict[integer[preceding-sibling::key[1]='Playlist ID']=6711]/array/dict/integer[preceding-sibling::key[1]='Track ID']" />

    <!-- I want to iterate over that array outside for-each loop and gather more information, The below code is not working.-->

   <xsl:for-each select="$tracks">
  <xsl:value-of select="." />
  <xsl:value-of select="plist/dict/dict/dict[integer[preceding-sibling::key[1]='Track ID']="."]/integer[preceding-sibling::key[1]='Track ID']" />
  <xsl:value-of select="plist/dict/dict/dict[integer[preceding-sibling::key[1]='Track ID']="."]/string[preceding-sibling::key[1]='Name']" />
  <xsl:value-of select="plist/dict/dict/dict[integer[preceding-sibling::key[1]='Track ID']="."]/string[preceding-sibling::key[1]='Total Time']" />
  <xsl:value-of select="plist/dict/dict/dict[integer[preceding-sibling::key[1]='Track ID']="."]/string[preceding-sibling::key[1]='Location']" />
  <xsl:text>&#xa;</xsl:text>
</xsl:for-each>


    </xsl:template>
    </xsl:stylesheet>

A typical Track Ids which are populated in array variable "tracks", are represented in below manner in itunes list. I want to list Name, Location, time infor for each Track Ids stored in the array. Something wrong with my conditions.

<plist>
  <dict>
    <dict>
      <dict>
        <key>Track ID</key>
        <integer>1633</integer>
        <key>Name</key>
        <string>Right here</string>
        <key>Kind</key>
        <string>MPEG audio file</string>
        <key>Total Time</key>
        <integer>358870</integer>
        <key>Location</key>
        <string>/Users/rakesh/Music/iTunes/iTunes%20Media/Music/track1633.mp3</string>
      </dict>
      <dict> 
        <!-- Next Track info -->
      </dict>
    </dict>
  </dict>
</plist>

Here I am stuck. Can any XSLT experts here to help me out?

Rakesh Singh
  • 858
  • 1
  • 12
  • 31
  • I've edited your answer to change some of your to so that the xml looks correct – Bob Vale Aug 15 '11 at 11:38
  • Before submitting a question it would be wise to express it in unambiguous, non-confusing way. This is a badly formulated question: 1) there is no `key` element in the provided XML document with string value `'Playlist ID'`. 2) There is no wanted result provided. Even if the wanted result is the contents of a variable, it should be specified (as an XML fragment) -- not left unspecified. Based on this I am quite tempted to *downvote* this question and am not doing this immediately with the hope that you'd make the necessary improvements. No wonder you got only a single, convoluted answer. – Dimitre Novatchev Aug 22 '11 at 13:22

1 Answers1

3

You could just create a variable and populate its results with an XPath expression all in one go rather than interate through to build it.

<xsl:variable name="tracks"
  select="plist/dict/array/dict[integer[preceding-sibling::key[1]='Playlist ID']=6711]/array/dict/integer[preceding-sibling::key[1]='Track ID']" />

Alternatively you could use xsl:keys at the begining of your document, I'm assuming that the path to the track information is /plist/dict/array/dict with the first dict key being Tracks

<xsl:key 
        name="playlists"
        match="plist/dict/array/dict/array/dict/integer[preceding-sibling::key[1]='Track ID']"
        use="../../../integer[preceding-sibling::key[1]='Playlist ID']"
/>
<xsl:key 
       name="tracks" 
       match="/plist/dict/array[preceding-sibling::key[1]='Tracks']/dict"
       use="integer[preceding-sibling::key[1]='Track ID']"
/>

This allows you to do key('playlists','4555') to return all the trackids associated with playlist id 4555 and also key('tracks','1234') to get the dict object associated with track id 1234

You can then combine the two together to do

<xsl:variable "mytracks" select="key('tracks',key('playlists','6711'))" />

That will set $mytracks equal to the array of dict objects for the tracks in playlist 6711. It aslo has the benefits of the speed enhancement granted by xsl:key

EDIT UPDATE----

I'm guessing you are trying to make a CSV from this so this code should do that

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="text" />
  <xsl:key
    name="playlists"
    match="plist/dict/array/dict/array/dict/integer[preceding-sibling::key[1]='Track ID']"
    use="../../../integer[preceding-sibling::key[1]='Playlist ID']"
  />
  <xsl:key
    name="tracks"
    match="/plist/dict/dict/dict"
    use="integer[preceding-sibling::key[1]='Track ID']"
  />

  <xsl:template match="/">
    <xsl:variable name="myplaylist" select="'6711'"/>
    <xsl:for-each select="key('tracks',key('playlists',$myplaylist))">
      <xsl:value-of select="integer[preceding-sibling::key[1]='Track ID']"/>
      <xsl:text>,</xsl:text>
      <xsl:value-of select="string[preceding-sibling::key[1]='Name']"/>
      <xsl:text>,</xsl:text>
      <xsl:value-of select="integer[preceding-sibling::key[1]='Total Time']"/>
      <xsl:text>,</xsl:text>
      <xsl:value-of select="string[preceding-sibling::key[1]='Location']"/>
      <xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

to match a different playlist id just change the value of myplaylist

--EDIT Version without xsl:key ,again just alter the value of the myplaylist variable

--EDIT Now mofified to original sort order of playlist --EDIT Attempt to work around Qt Limitations

  <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="text" />

  <xsl:template match="/">
    <xsl:variable name="myplaylist" select="'4053'"/>
    <xsl:variable name="playlist_tracks" select="/plist/dict/array/dict[integer[preceding-sibling::key[1]='Playlist ID']=$myplaylist]/array/dict/integer[preceding-sibling::key[1]='Track ID']" />

    <xsl:for-each select="$playlist_tracks">
      <xsl:variable select="." name="current" />
      <xsl:for-each select ="/plist/dict/dict/dict[integer[preceding-sibling::key[1]='Track ID']=$current]" >
        <xsl:value-of select="integer[preceding-sibling::key[1]='Track ID']"/>
        <xsl:text>,</xsl:text>
        <xsl:value-of select="string[preceding-sibling::key[1]='Name']"/>
        <xsl:text>,</xsl:text>
        <xsl:value-of select="integer[preceding-sibling::key[1]='Total Time']"/>
        <xsl:text>,</xsl:text>
        <xsl:value-of select="string[preceding-sibling::key[1]='Location']"/>
        <xsl:text>&#xa;</xsl:text>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
Bob Vale
  • 18,094
  • 1
  • 42
  • 49
  • Doh I made a typo, I've now corrected it to search for the playlist. – Bob Vale Aug 15 '11 at 09:28
  • Could you update the question to include info as to the xml path to the track information – Bob Vale Aug 15 '11 at 09:53
  • 1
    I've updated my answer again taking a guess about how to access the track info – Bob Vale Aug 15 '11 at 10:08
  • Updated to hopefully better answer your question – Bob Vale Aug 15 '11 at 11:38
  • Thanks Bob, You are awesome, hats off!! Its working perfectly fine. – Rakesh Singh Aug 15 '11 at 18:09
  • Even though all information are coming but the order issue still persists. So in a playlists if tracks are in order (7 3 1 2), I want information in that order. Currently as we are extracting information for plist/dict/dict/dict .. the order comes as (1 2 3 7). Assume numbers are track ids. – Rakesh Singh Aug 22 '11 at 10:54
  • Thanks for reply. Your solution is not working. Only 1st element of the playlist is getting printed. Any clue. – Rakesh Singh Aug 22 '11 at 13:42
  • No, because when I cut and paste this style sheet I get multiple results :( – Bob Vale Aug 22 '11 at 23:40
  • 1
    The pasted code works perfectly for me when I try, I've done a minor rewrite that removes one element of nesting - see if that works around qt's limitation – Bob Vale Aug 23 '11 at 09:14