遍历包含嵌套标记的 XML 文件

Looping through an XML file containing nested tags

提问人:Philip Lee 提问时间:11/15/2023 最后编辑:Ken WhitePhilip Lee 更新时间:11/15/2023 访问量:48

问:

我正在编写一些代码,这些代码将允许我通过向 VB.net 程序发送 MIDI 音符来控制波段灯。每首歌曲的歌单和结构都在XML文件中定义,以及歌曲每个部分(诗歌、副歌、结尾等)中使用的灯光序列。XML 文件如下所示:

<?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>

我是从PHP背景来的,在这个背景中,将这个XML转换为关联数组很容易,这在 VB.Net 中似乎并不容易,所以我想出了以下代码,它将XML文件转换为JSON数组,然后循环它。

我目前试图获得的是歌曲的数量和每个部分的数量,例如,歌曲 1 8 部分、歌曲 2 7 部分和歌曲 3 5 部分。这些将存储在数组中以备后用。

这是我的VB函数:

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

虽然这成功地计算了歌曲的数量,但我找不到一种遍历部分标签并检索其属性的方法。我的函数只看到一个部分标签并输出这个:

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"
  }
]

我在网上找不到与此问题完全匹配的任何内容,但这一定是一个相当普遍的问题。如何计算部分标签的数量并(理想情况下)访问名称和零件属性?

JSON XML vb.net

评论

0赞 Yitzhak Khabinsky 11/15/2023
请编辑您的问题,并添加所需的输出。无需使用 JSON 作为中间格式。

答:

0赞 SSS 11/15/2023 #1

这可以使用 System.Xml 中的 XPATH 表达式来完成:

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

输出:

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'

评论

0赞 Philip Lee 11/15/2023
太棒了,非常感谢!我确实尝试过在早期版本中直接导航XML文档,但无法使其工作。我现在可以看到我的错误了!
0赞 SSS 11/16/2023
很高兴我能帮上忙。祝您编程愉快!
0赞 dbasnett 11/15/2023 #2

我喜欢使用 XElement。

以下是显示如何从 URI 加载但使用文字进行测试的数据,

    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>

然后访问各个部分

    '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