如何按给定顺序对 XML 进行排序? (弄乱导出的 iTunes 播放列表)
How to sort XML in given order? (messing with exported iTunes playlists)
在 iTunes 中,当您在 XML 中导出播放列表时,它会自动按照 "date added" 排序的方式导出,而不是按照用户订购的顺序排序。在 XML 文件的末尾,它列出了用户按歌曲 ID 订购的播放列表的顺序,如下所示:
<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>
我使用此 XSL 转换了 XML 文档:
<?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>
我想知道如何编辑我的 XSL 以转换我的播放列表以根据出现在原始 XML 文件末尾的用户给定顺序重新排序所有内容,而不是通过 "date added"?
编辑 - 添加更多原始 XML。这是与我所做的 XSL 相关的片段:
<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>
对您的代码的基本但友好的建议:如果您可以使用 xsl:apply-templates
,请不要将 xsl:call-template
与上下文一起使用,因为它会限制您的流程并使您的代码复杂化。
查看我的编辑。我采用了您的结构,但通过删除 xsl:call-template
进行了简化,这没有任何其他目的。
要排序,只需使用 xsl:sort
作为 xsl:apply-templates
的子项。这里我按艺术家的名字排序。如果需要,您可以通过添加更多 xsl:sort
元素来添加更多排序键。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- from SO: -->
<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>
因为你没有提供有效的 XML 作为源文档(它有两个根元素),所以我发明了适合你的原始代码的我自己的(即实际输出一些东西)并添加了第二个记录以确保排序有效。一个友好的建议,如果您将来询问 XSLT 问题,如果您包含一个(最小的)源文档,我们可以使用它来测试对您的代码的修复,这将使我们更容易。
新的源文档:
<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>
以及新的按名称排序的输出:
<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>
另请注意:您的第一个代码段有 array/dict
,您的第二个代码段只有 dict
,但您的 XSLT 使用 dict/dict
,所以我更改了源结构以使其适合您的XSLT 代码。
你说:
sort [...] according to the user-given order that appears at the end of the original XML file rather than by "date added"
您需要扩展我上面给出的示例。由于我不知道这部分XML的结构,而且问题中也没有给出,所以无法应用。不过,我假设它会是底部的一个元素,所以应该很容易使用它。
例如,假设订单由 /*/userorder/@by
给出,并且 @by
包含匹配 key
元素的值,您可以这样做:
<xsl:sort select="key[. = /*/userorder/@by]/following-sibling::*[1]" />
在 iTunes 中,当您在 XML 中导出播放列表时,它会自动按照 "date added" 排序的方式导出,而不是按照用户订购的顺序排序。在 XML 文件的末尾,它列出了用户按歌曲 ID 订购的播放列表的顺序,如下所示:
<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>
我使用此 XSL 转换了 XML 文档:
<?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>
我想知道如何编辑我的 XSL 以转换我的播放列表以根据出现在原始 XML 文件末尾的用户给定顺序重新排序所有内容,而不是通过 "date added"?
编辑 - 添加更多原始 XML。这是与我所做的 XSL 相关的片段:
<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>
对您的代码的基本但友好的建议:如果您可以使用 xsl:apply-templates
,请不要将 xsl:call-template
与上下文一起使用,因为它会限制您的流程并使您的代码复杂化。
查看我的编辑。我采用了您的结构,但通过删除 xsl:call-template
进行了简化,这没有任何其他目的。
要排序,只需使用 xsl:sort
作为 xsl:apply-templates
的子项。这里我按艺术家的名字排序。如果需要,您可以通过添加更多 xsl:sort
元素来添加更多排序键。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- from SO: -->
<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>
因为你没有提供有效的 XML 作为源文档(它有两个根元素),所以我发明了适合你的原始代码的我自己的(即实际输出一些东西)并添加了第二个记录以确保排序有效。一个友好的建议,如果您将来询问 XSLT 问题,如果您包含一个(最小的)源文档,我们可以使用它来测试对您的代码的修复,这将使我们更容易。
新的源文档:
<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>
以及新的按名称排序的输出:
<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>
另请注意:您的第一个代码段有 array/dict
,您的第二个代码段只有 dict
,但您的 XSLT 使用 dict/dict
,所以我更改了源结构以使其适合您的XSLT 代码。
你说:
sort [...] according to the user-given order that appears at the end of the original XML file rather than by "date added"
您需要扩展我上面给出的示例。由于我不知道这部分XML的结构,而且问题中也没有给出,所以无法应用。不过,我假设它会是底部的一个元素,所以应该很容易使用它。
例如,假设订单由 /*/userorder/@by
给出,并且 @by
包含匹配 key
元素的值,您可以这样做:
<xsl:sort select="key[. = /*/userorder/@by]/following-sibling::*[1]" />