Reputation: 23
I am tring to read the iTunes Music Library XML in my application however i have no idea on how to do this, what i'm trying to do is to search for a specific track name a specing key, for example "Artist", "Album" etc.. yes, i know how to use iTunes COM but there are some track information that cannot be access through it such as if the Track is "Explicit" there is an additional key
<key>Explicit</key><true/>
what i want to do is search for a specific track name and check if it track has an explicit key.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Major Version</key><integer>1</integer>
<key>Minor Version</key><integer>1</integer>
<key>Date</key><date>2014-08-24T05:21:40Z</date>
<key>Application Version</key><string>11.3.1</string>
<key>Features</key><integer>5</integer>
<key>Show Content Ratings</key><true/>
<key>Music Folder</key><string>file://localhost/C:/Users/Mon%20Tolentino/Music/iTunes/iTunes%20Media/</string>
<key>Library Persistent ID</key><string>15CDC06EC711077D</string>
<key>Tracks</key>
<dict>
<key>766</key>
<dict>
<key>Track ID</key><integer>766</integer>
<key>Name</key><string>Say Something</string>
<key>Artist</key><string>A Great Big World</string>
<key>Album Artist</key><string>A Great Big World</string>
<key>Composer</key><string>Ian Axel, Chad Vaccarino, Mike Campbell</string>
<key>Album</key><string>Is There Anybody Out There?</string>
<key>Grouping</key><string>Indie</string>
<key>Genre</key><string>Pop</string>
<key>Kind</key><string>AAC audio file</string>
<key>Size</key><integer>8639959</integer>
<key>Total Time</key><integer>233289</integer>
<key>Disc Number</key><integer>1</integer>
<key>Disc Count</key><integer>1</integer>
<key>Track Number</key><integer>5</integer>
<key>Track Count</key><integer>13</integer>
<key>Year</key><integer>2013</integer>
<key>BPM</key><integer>94</integer>
<key>Date Modified</key><date>2014-05-25T15:58:35Z</date>
<key>Date Added</key><date>2014-05-18T10:09:13Z</date>
<key>Bit Rate</key><integer>256</integer>
<key>Sample Rate</key><integer>44100</integer>
<key>Play Count</key><integer>33</integer>
<key>Play Date</key><integer>3491589574</integer>
<key>Play Date UTC</key><date>2014-08-22T13:59:34Z</date>
<key>Skip Count</key><integer>7</integer>
<key>Skip Date</key><date>2014-08-15T15:15:43Z</date>
<key>Release Date</key><date>2013-11-04T12:00:00Z</date>
<key>Rating</key><integer>60</integer>
<key>Album Rating</key><integer>60</integer>
<key>Album Rating Computed</key><true/>
<key>Artwork Count</key><integer>1</integer>
<key>Sort Album Artist</key><string>A Great Big World</string>
<key>Sort Artist</key><string>A Great Big World</string>
<key>Persistent ID</key><string>50A143EFFA8BEA97</string>
<key>Track Type</key><string>File</string>
<key>Location</key><string>file://localhost/C:/Users/Mon%20Tolentino/Music/iTunes/iTunes%20Media/Music/A%20Great%20Big%20World/Is%20There%20Anybody%20Out%20There_/05%20Say%20Something.m4a</string>
<key>File Folder Count</key><integer>5</integer>
<key>Library Folder Count</key><integer>1</integer>
</dict>
I found this on the web:
Dim itunesLib As XElement = XElement.Load("xmllocation")
Dim itunesPlaylists = From key In itunesLib...<key> Where key.Value = "Name"
Select key.ElementsAfterSelf("string").Value
List1.ItemsSource = itunesPlaylists
but it only list all my tracks in a listbox, can anyone please help me.
Upvotes: 2
Views: 504
Reputation: 89325
"what i want to do is search for a specific track name and check if it track has an explicit key."
You can use LINQ to XML expression to check if particular track is explicit, for example :
Dim xml As XElement = XElement.Load("xmllocation")
Dim trackName = "Say Something"
Dim query = From d In xml...<dict>
Where d.Elements("key").Any(Function(name) name.Value = "Name" _
AndAlso (CType(name.NextNode, XElement).Value = trackName)) _
And d.Elements("key").Any(Function(exp) exp.Value = "Explicit" _
AndAlso (CType(exp.NextNode, XElement).Name = "true"))
Dim isExplicit As Boolean = query.Any()
Or using XPath expression equivalent to above LINQ :
Dim xpath As String = String.Format("//dict[key[.='Name' and following-sibling::string='{0}'] and key[.='Explicit' and following-sibling::true]]", trackName)
Dim isExplicit As Boolean = xml.XPathSelectElements(xpath).Any()
UPDATE :
If you need to check for artist name besides the track, you can add another filter in the WHERE
clause like so :
Where ....
And d.Elements("key").Any(Function(artist) artist.Value = "Artist" _
AndAlso (CType(exp.NextNode, XElement).Value = "artist name"))
And ...
Upvotes: 0
Reputation: 13296
Wow, that is some of the worst XML I've ever seen. My condolences for having to put up with that.
I'd be tempted to start off with parsing them into a (perhaps deferred) collection of concrete classes, then .Where
those to find the one you're looking for.
I wrote all of this code in C# originally because I don't actually really know VB, then converted it using a tool online, so let me know if there are issues with this or if any of it is not best practice. It should convey the gist of the workflow, though, which is really the point.
So, then, I'd start with writing a class:
Public Class Track
Public Property Name() As String
Get
Return m_Name
End Get
Set
m_Name = Value
End Set
End Property
Private m_Name As String
Public Property Explicit() As Boolean
Get
Return m_Explicit
End Get
Set
m_Explicit = Value
End Set
End Property
Private m_Explicit As Boolean
End Class
Then I'd write a method to grab values for any key in a <dict>
element
Private Function GetValue(Of T)(dict As XElement, key As String) As T
Dim element = dict.Elements("key").FirstOrDefault(Function(c) String.Equals(DirectCast(c, String), key))
If element Is Nothing OrElse element.NextNode Is Nothing Then
Return Nothing
End If
element = element.NextNode
If T = GetType(Boolean) Then
Return element.Name = "true"
Else
Return DirectCast(element, T)
End If
End Function
Then parse the XML into that class:
Dim root = XElement.Load(path)
root = root.Element("dict")
Dim tracks = root.Elements("key").First(Function(c) DirectCast(c, String) = "Tracks").NextNode.Elements("dict")
Dim parsedTracks As IEnumerable(Of Track) = tracks.[Select](Function(c) New Track() With { _
Key .Name = GetValue(Of String)(c, "Name"), _
Key .Explicit = GetValue(Of Boolean)(c, "Explicit") _
})
Then you can easily query against that:
Return parsedTracks.First(Function(c) c.Name = desiredTrackName).Explicit
You'd want to add in lots of error handling, I'm sure, but this should convey the gist of a good way of doing it. You may or may not want to throw in a .ToArray()
call in there. It's really up to how you're using it. This will, as it stands, only loop through once per call (hence "deferred"), so that's great if you're doing infrequent searches. But you might want to .ToArray()
it so that it's all loaded into memory and already parsed if you're doing more calls.
Upvotes: 1
Reputation: 758
There are several ways to query XML documents in .net you could use direct DOM query
for example :
XmlDOcument doc=new XmlDOcument();
doc.LoadXml("your raw xml");
var plist=doc.DOcumentElement.Children[0]; //my plist
var rootDict=plist.Children[0]; //my root dict
//..
//and so on
read about .NET XML DOM API here
http://msdn.microsoft.com/en-us/magazine/cc302158.aspx
You could also use the Xml Query languege : Xpath
http://www.codeproject.com/Articles/9494/Manipulate-XML-data-with-XPath-and-XmlDocument-C
Upvotes: 0