Reading XML data using LINQ, multiple elements wit

2019-08-22 03:31发布

I have an xml with multiple nodes of the same name

<?xml version="1.0" encoding="UTF-8"?>
<Versions>
    <Version>
        <Trunk>GapGun Software Version 7.1</Trunk>
            <Branch>.142</Branch>
            <Branch>.145</Branch>
            <Branch>.148</Branch>
            <Branch>.153</Branch>
            <Branch>.176</Branch>
    </Version>
    <Version>
        <Trunk>GapGun Software Version 7.2</Trunk>
            <Branch>.142</Branch>
            <Branch>.145</Branch>
            <Branch>.148</Branch>
            <Branch>.153</Branch>
            <Branch>.176</Branch>
    </Version>
</Versions> 

I need to populate a combo box when filtered using the Trunk as a query so far i have this code

Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim xelement As XElement = XElement.Load("F:\Test.xml")
    Dim Versions As IEnumerable(Of XElement) = xelement.Elements()
    For Each Version In Versions
        Console.WriteLine(Version.Element("Trunk").Value)
        ComboBox1.Items.Add(Version.Element("Trunk").Value)
    Next Version
End Sub

Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
    Dim xelement As XElement = XElement.Load("F:\Test.xml")
    Dim name =
        From nm In xelement.Elements("Version")
        Where CStr(nm.Element("Trunk")) = ComboBox1.Text
        Select nm
    For Each xEle As XElement In name
        Console.WriteLine(xEle)
        ComboBox2.Items.Add(xEle.Element("Branch").Value)
    Next xEle
End Sub
End Class

This works but only returns the first branch please help, i am a complete novice!

2条回答
Root(大扎)
2楼-- · 2019-08-22 04:15

Try following :

   Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim xdoc As XDocument = XDocument.Load(FILENAME)
        Dim versions As IEnumerable(Of XElement) = xdoc.Descendants("Version")
        For Each version As XElement In versions
            For Each trunk As XElement In version.Elements("Trunk")
                Console.WriteLine(CType(trunk, String))
                ComboBox1.Items.Add(CType(trunk, String))
            Next trunk

            For Each xEle As XElement In version.Elements("Branch")
                Console.WriteLine(CType(xEle, String))
                ComboBox2.Items.Add(CType(xEle, String))
            Next xEle
        Next version

    End Sub
查看更多
男人必须洒脱
3楼-- · 2019-08-22 04:34

Your problem is that you using method XElement.Element which will return one(first) element of given name.
From docs: Gets the first (in document order) child element with the specified XName.

You need change code to loop all "Branch" elements and you can add all of them by using ComboBox.Items.AddRange method

Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) _
        Handles ComboBox1.SelectedIndexChanged

    Dim xelement As XElement = XElement.Load("F:\Test.xml")
    Dim name =
        From nm In xelement.Elements("Version")
        Where CStr(nm.Element("Trunk")) = ComboBox1.Text
        Select nm

    For Each xEle As XElement In name
        Dim branches = xEle.Elements("Branch").Select(Function(el) el.Value).ToArray()

        Console.WriteLine(xEle)
        ComboBox2.Items.AddRange(branches)
    Next
End Sub

To make code little bid simpler and load files only ones - you can introduce Version class which contains all required data.
Then you don't need to search for correct trunk and will use already existing branches.

Public Class Version
    Public Property Trunk As String
    Public Property Branches As List(Of String)
End Class

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim xelement As XElement = XElement.Load("F:\Test.xml")

    ' Create collection of versions
    Dim versions = _ 
        xelement.Elements().
                 Select(Function(version)
                           Return New Version With
                           {
                               .Trunk = version.Element("Trunk").Value
                               .Branches = version.Elements("Branch").
                                                   Select(Function(el) el.Value)
                           }
                        End Function)

    ' Bind collection of versions to ComboBox
    ' Name of property which will be used as displayed text
    ComboBox1.DisplayMember = "Trunk"
    ComboBox1.DataSource = versions
End Sub

Then when user selects trunk populating branches combobox will be much simpler

Private Sub ComboBox1_SelectionChangeCommitted(sender As Object, e As EventArgs) _ 
        Handles ComboBox1.SelectionChangeCommitted

    Dim comboBox = DirectCast(sender, ComboBox)
    Dim selectedVersion = DirectCast(comboBox.SelectedItem, Version)

    ComboBox2.DataSource = selectedVersion.Branches
End Sub
查看更多
登录 后发表回答