提问人:Philip Lee 提问时间:11/15/2023 最后编辑:Ken WhitePhilip Lee 更新时间:11/15/2023 访问量:48
遍历包含嵌套标记的 XML 文件
Looping through an XML file containing nested tags
问:
我正在编写一些代码,这些代码将允许我通过向 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"
}
]
我在网上找不到与此问题完全匹配的任何内容,但这一定是一个相当普遍的问题。如何计算部分标签的数量并(理想情况下)访问名称和零件属性?
答:
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
评论