In iTunes, when you export a playlist in XML it automatically exports it in a way where it is sorted by "date added" rather than the order the user orders it. Towards the end of the XML file, it lists the order of the playlist the user orders it by Song ID like this:
<key>Playlist Items</key>
<array>
<dict>
<key>Track ID</key><integer>5365</integer>
</dict>
<dict>
<key>Track ID</key><integer>5317</integer>
</dict>
<dict>
<key>Track ID</key><integer>5235</integer>
</dict>
<array>
I transformed the XML document using this XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<table>
<tr>
<th>Name</th>
<th>Artist</th>
<th>Year</th>
</tr>
<xsl:call-template name="records" />
</table>
</xsl:template>
<xsl:template name="records">
<xsl:for-each select="/*/*/dict[1]/dict">
<xsl:element name="tr">
<xsl:call-template name="songs" />
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template name="songs">
<td>
<xsl:value-of select="child::*[preceding-sibling::* = 'Track ID']" />
</td>
<td>
<xsl:value-of select="child::*[preceding-sibling::* = 'Name']" />
</td>
<td>
<xsl:value-of select="child::*[preceding-sibling::* = 'Artist']" />
</td>
</xsl:template>
</xsl:stylesheet>
I want to know how can I edit my XSL to transform my playlist to reorder everything according to the user-given order that appears at the end of the original XML file rather than by "date added"?
EDIT - adding more of the original XML. Here is the snippet that pertains to the XSL thing I did:
<key>5189</key>
<dict>
<key>Track ID</key><integer>5189</integer>
<key>Name</key><string>varsity jacket</string>
<key>Artist</key><string>bayou</string>
<key>Kind</key><string>MPEG audio file</string>
<key>Size</key><integer>3532008</integer>
<key>Total Time</key><integer>220107</integer>
<key>Date Modified</key><date>2015-07-26T14:04:34Z</date>
<key>Date Added</key><date>2015-07-26T14:04:17Z</date>
<key>Bit Rate</key><integer>128</integer>
<key>Sample Rate</key><integer>44100</integer>
<key>Play Count</key><integer>1</integer>
<key>Play Date</key><integer>3520959994</integer>
<key>Play Date UTC</key><date>2015-07-29T00:26:34Z</date>
<key>Skip Count</key><integer>2</integer>
<key>Skip Date</key><date>2015-08-29T05:12:33Z</date>
<key>Persistent ID</key><string>BE57D36AF01737E3</string>
<key>Track Type</key><string>File</string>
<key>Location</key><string>file:///Users/jason/Music/iTunes/iTunes%20Music/bayou/Unknown%20Album/varsity%20jacket.mp3</string>
<key>File Folder Count</key><integer>4</integer>
<key>Library Folder Count</key><integer>1</integer>
</dict>
Essential, but friendly suggestion to your code: do not use xsl:call-template
with a context if you can suffice with xsl:apply-templates
, as it will limit your flow and complicates your code.
See my edits. I took your structure, but simplified by removing xsl:call-template
, which served no additional purpose.
To sort, just use xsl:sort
as child of xsl:apply-templates
. Here I sort by name of the artist. You can add more sort keys if you want by adding more xsl:sort
elements.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- from SO: http://stackoverflow.com/questions/32432619/how-to-sort-xml-in-given-order-messing-with-exported-itunes-playlists -->
<xsl:output indent="yes" />
<xsl:template match="/">
<table>
<tr>
<th>Name</th>
<th>Artist</th>
<th>Year</th>
</tr>
<xsl:apply-templates select="*/*/dict[1]/dict" >
<!-- sort by value of the name of the song -->
<xsl:sort select="key[. = 'Name']/following-sibling::*[1]" />
</xsl:apply-templates>
</table>
</xsl:template>
<xsl:template match="dict">
<tr>
<xsl:apply-templates select="*" />
</tr>
</xsl:template>
<xsl:template match="dict/key[. = 'Track ID' or . = 'Name' or . = 'Artist']">
<td>
<xsl:value-of select="following-sibling::*[1]" />
</td>
</xsl:template>
<!-- ignore what we do not need -->
<xsl:template match="dict/*" priority="-1" />
</xsl:stylesheet>
Since you didn't give valid XML as source document (it had two root elements), so I invented my own that fit your original code (i.e., that actually output something) and added a second record to make sure the sorting worked. A friendly suggestion, if you ask XSLT questions in the future, it will make it easier for us if you include a (minimal) source document that we can use to test fixes to your code.
The new source doc:
<root>
<record>
<dict>
<key>5189</key>
<dict>
<key>Track ID</key><integer>5189</integer>
<key>Name</key><string>varsity jacket</string>
<key>Artist</key><string>bayou</string>
<key>Kind</key><string>MPEG audio file</string>
<key>Size</key><integer>3532008</integer>
<key>Total Time</key><integer>220107</integer>
<key>Date Modified</key><date>2015-07-26T14:04:34Z</date>
<key>Date Added</key><date>2015-07-26T14:04:17Z</date>
<key>Bit Rate</key><integer>128</integer>
<key>Sample Rate</key><integer>44100</integer>
<key>Play Count</key><integer>1</integer>
<key>Play Date</key><integer>3520959994</integer>
<key>Play Date UTC</key><date>2015-07-29T00:26:34Z</date>
<key>Skip Count</key><integer>2</integer>
<key>Skip Date</key><date>2015-08-29T05:12:33Z</date>
<key>Persistent ID</key><string>BE57D36AF01737E3</string>
<key>Track Type</key><string>File</string>
<key>Location</key><string>file:///Users/jason/Music/iTunes/iTunes%20Music/bayou/Unknown%20Album/varsity%20jacket.mp3</string>
<key>File Folder Count</key><integer>4</integer>
<key>Library Folder Count</key><integer>1</integer>
</dict>
<key>5190</key>
<dict>
<key>Track ID</key><integer>5190</integer>
<key>Name</key><string>some song</string>
<key>Artist</key><string>anton</string>
<key>Kind</key><string>MPEG audio file</string>
<key>Size</key><integer>3532008</integer>
<key>Total Time</key><integer>220107</integer>
<key>Date Modified</key><date>2015-07-26T14:04:34Z</date>
<key>Date Added</key><date>2015-07-26T14:04:17Z</date>
<key>Bit Rate</key><integer>128</integer>
<key>Sample Rate</key><integer>44100</integer>
<key>Play Count</key><integer>1</integer>
<key>Play Date</key><integer>3520959994</integer>
<key>Play Date UTC</key><date>2015-07-29T00:26:34Z</date>
<key>Skip Count</key><integer>2</integer>
<key>Skip Date</key><date>2015-08-29T05:12:33Z</date>
<key>Persistent ID</key><string>BE57D36AF01737E3</string>
<key>Track Type</key><string>File</string>
<key>Location</key><string>file:///Users/jason/Music/iTunes/iTunes%20Music/bayou/Unknown%20Album/varsity%20jacket.mp3</string>
<key>File Folder Count</key><integer>4</integer>
<key>Library Folder Count</key><integer>1</integer>
</dict>
</dict>
</record>
</root>
And the new sorted-by-name output:
<table>
<tr>
<th>Name</th>
<th>Artist</th>
<th>Year</th>
</tr>
<tr>
<td>5190</td>
<td>some song</td>
<td>anton</td>
</tr>
<tr>
<td>5189</td>
<td>varsity jacket</td>
<td>bayou</td>
</tr>
</table>
Also note: your first snippet has array/dict
, your second snippet only had dict
, but your XSLT uses dict/dict
, so I changed the source structure such that it fit your XSLT code.
You said:
sort [...] according to the user-given order that appears at the end of the original XML file rather than by "date added"
You'll need to expand on the example I gave above. Since I do not know the structure of the XML with regards to this part, and it was not given in the question, I couldn't apply it. However, I assume it will be one element at the bottom, so it should be easy to use it.
For instance, suppose the order is given by /*/userorder/@by
and @by
contains the value matching a key
element, you can do this:
<xsl:sort select="key[. = /*/userorder/@by]/following-sibling::*[1]" />