skip to Main Content

I’m writing some code that’ll allow me to control a bands lights by sending a MIDI note to a VB.net program. The setlist and structure of each song is defined in an XML file as well as the lighting sequence to use in each section of the song (verse, chorus, outro etc.). The XML file looks like this:

<?xml version="1.0" standalone="yes"?>
<setlist>
    <song>
        <title>Blink 182 - All the Small Things</title>
        <section id = "0" name = "intro"  part = "SCENE0"></section>
        <section id = "1" name = "verse"  part = "SCENE1"></section>
        <section id = "2" name = "chorus" part = "SCENE2"></section>
        <section id = "3" name = "verse"  part = "SCENE3"></section>
        <section id = "4" name = "solo"   part = "SCENE4"></section>
        <section id = "5" name = "chorus" part = "SCENE5"></section>
        <section id = "6" name = "outro"  part = "SCENE6"></section>
        <section id = "7" name = "hold"   part = "SCENE7"></section>
    </song>
    <song>
        <title>Green Day - Holiday</title>
        <section id = "0" name = "intro"  part = "SCENE0"></section>
        <section id = "1" name = "verse"  part = "SCENE3"></section>
        <section id = "2" name = "chorus" part = "SCENE2"></section>
        <section id = "3" name = "verse"  part = "SCENE4"></section>
        <section id = "4" name = "solo"   part = "SCENE5"></section>
        <section id = "5" name = "outro"  part = "SCENE6"></section>
        <section id = "6" name = "hold"   part = "SCENE7"></section>
    </song>
    <song>
        <title>Paramore - Still into you</title>
        <section id = "0" name = "intro"  part = "SCENE0"></section>
        <section id = "1" name = "verse"  part = "SCENE3"></section>
        <section id = "2" name = "chorus" part = "SCENE2"></section>
        <section id = "3" name = "verse"  part = "SCENE4"></section>
        <section id = "4" name = "hold"   part = "SCENE7"></section>
    </song>
</setlist>

I’m coming at this from a PHP background where turning this XML into an associative array would be easy by that doesn’t appear to be easily doable in VB.Net so I’ve come up with the following code which converts the XML file into a JSON array and then loops through it.

What I’m currently trying to get is the number of songs and the number of sections in each, e.g., song 1 8 sections, song 2 7 sections and song 3 5 sections. These will be stored in an array for later use.

This is my VB function:

Private Sub readSetList(setListFile As String)
    ' Read setlist data
    Dim setlist As New XmlDocument()
    setlist.Load(setListFile)

    ' Convert to JSON
    Dim JsonString As String = JsonConvert.SerializeXmlNode(setlist)

    ' Convert to an array
    Dim JsonArray As JObject = JObject.Parse(JsonString)

    Dim songCount As Integer = 0
    Dim sectionCount As Integer = 0

    For Each item As JProperty In JsonArray.Item("setlist")
        Dim itemObjects As JToken = item.Value
        For Each i As JObject In itemObjects
            For Each p In i
                ' If the key is 'title' then this is a new song so increment the song counter
                If p.Key.ToString = "title" Then
                    songCount += 1
                    Debug.Print(songCount & " => Title: " & p.Value.ToString)
                End If

                If p.Key.ToString = "section" Then
                    sectionCount += 1
                    Debug.Print(sectionCount & " => Section: " & p.Value.ToString)
                End If
            Next
        Next
    Next
End Sub

Whilst this successfully counts the number of songs, I can’t find a way of looping through the section tags and retrieving their properties. My function just sees one section tag and outputs this:

3 => Title: Paramore - Still into you
3 => Section: [
  {
    "@id": "0",
    "@name": "intro",
    "@part": "SCENE0"
  },
  {
    "@id": "1",
    "@name": "verse",
    "@part": "SCENE3"
  },
  {
    "@id": "2",
    "@name": "chorus",
    "@part": "SCENE2"
  },
  {
    "@id": "3",
    "@name": "verse",
    "@part": "SCENE4"
  },
  {
    "@id": "4",
    "@name": "hold",
    "@part": "SCENE7"
  }
]

I can’t find anything that exactly matches this issue online, but it must be a fairly common problem. How do I count the number of section tags and (ideally) access the name and part attributes ?

2

Answers


  1. This can be done using XPATH expressions in System.Xml:

    Imports System.Xml
    
    Module Program
      Sub Main(args As String())
        Dim setList = New XmlDocument
        setList.Load("songlist.xml")
        Dim songs = setList.DocumentElement.SelectNodes("/setlist/song")
        Console.WriteLine($"{songs.Count} song(s)")
        For Each song As XmlNode In songs
          Dim title As String = song.SelectSingleNode("title").FirstChild.Value 'retrieve contents of title node
          Dim sections = song.SelectNodes("section")
          Dim fifthSectionPart = sections(4).SelectSingleNode("@part").Value 'syntax to retrieve an attribute
          Console.WriteLine($"'{title}' has {sections.Count} sections, the fifth section's part is called '{fifthSectionPart}'")
        Next song
      End Sub
    End Module
    

    Output:

    3 song(s)
    'Blink 182 - All the Small Things' has 8 sections, the fifth section's part is called 'SCENE4'
    'Green Day - Holiday' has 7 sections, the fifth section's part is called 'SCENE5'
    'Paramore - Still into you' has 5 sections, the fifth section's part is called 'SCENE7'
    
    Login or Signup to reply.
  2. I like to use XElement.

    Here is the data showing how to load from URI but using a literal to test with,

        Dim setlist As XElement
        ''for production
        'Dim path As String = "path here"
        'setlist = XElement.Load(path)
    
        'for testing use literal
        setlist = <setlist>
                      <song>
                          <title>Blink 182 - All the Small Things</title>
                          <section id="0" name="intro" part="SCENE0"></section>
                          <section id="1" name="verse" part="SCENE1"></section>
                          <section id="2" name="chorus" part="SCENE2"></section>
                          <section id="3" name="verse" part="SCENE3"></section>
                          <section id="4" name="solo" part="SCENE4"></section>
                          <section id="5" name="chorus" part="SCENE5"></section>
                          <section id="6" name="outro" part="SCENE6"></section>
                          <section id="7" name="hold" part="SCENE7"></section>
                      </song>
                      <song>
                          <title>Green Day - Holiday</title>
                          <section id="0" name="intro" part="SCENE0"></section>
                          <section id="1" name="verse" part="SCENE3"></section>
                          <section id="2" name="chorus" part="SCENE2"></section>
                          <section id="3" name="verse" part="SCENE4"></section>
                          <section id="4" name="solo" part="SCENE5"></section>
                          <section id="5" name="outro" part="SCENE6"></section>
                          <section id="6" name="hold" part="SCENE7"></section>
                      </song>
                      <song>
                          <title>Paramore - Still into you</title>
                          <section id="0" name="intro" part="SCENE0"></section>
                          <section id="1" name="verse" part="SCENE3"></section>
                          <section id="2" name="chorus" part="SCENE2"></section>
                          <section id="3" name="verse" part="SCENE4"></section>
                          <section id="4" name="hold" part="SCENE7"></section>
                      </song>
                  </setlist>
    

    And then accessing various parts

        'get all the songs
        Dim songs As IEnumerable(Of XElement)
        songs = From el In setlist.<song> Select el
    
        Debug.WriteLine(songs.Count)
    
    
        For Each song As XElement In songs
            Debug.WriteLine(song.<title>.Value)
            'get sections in song
            Dim sections As IEnumerable(Of XElement)
            sections = From sec In song.Elements Where sec.Name.LocalName = "section" Select sec
    
            Debug.WriteLine(sections.Count)
            For Each sec As XElement In sections 'examine each section
                Debug.WriteLine("id:{0}  name:{1} part{2}", sec.@id, sec.@name, sec.@part)
            Next
        Next
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search