Mon Tolentino
Mon Tolentino

Reputation: 23

Reading XML in VB

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

Answers (3)

har07
har07

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

Matthew Haugen
Matthew Haugen

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

Alex
Alex

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

Related Questions