简体   繁体   中英

Find XML Element by their Sibling

How can I find the sibling of an XML Element with Visual Basic? Let's say I have:

<Data>
  <Mail>
    <Subject>Welcome!</Subject>
    <From>Antonios</From>
    <Content>Welcome! How can I assist you?</Content>
  </Mail>
  <Mail>
    <Subject>Test!</Subject>
    <From>John</From>
    <Content>Hello Friend!</Content>
  </Mail>
</Data>

Now I have a list box that adds every Subject, so the list shows: Welcome! and Test! Now I want that, when I click on "Welcome!" , a Text Box shows the content of the "From" element of "Welcome!" and another Text Box shows the "Content" element of "Welcome" . In other words, I'm looking for the Sibling of a specific element .

You can do this in multiple ways. Here is how you can do it using XmlDocument and XPath:

Dim doc As New XmlDocument()
Dim From As String = doc.SelectSingleNode("/Data/Mail[Subject='Welcome!']/From").InnerText
Dim Content As String = doc.SelectSingleNode("/Data/Mail[Subject='Welcome!']/Content").InnerText

Obviously, since the subject may not be unique, it would be better to use some element that is a unique ID, or you could do it by index. For instance, this selects from the first mail message:

Dim From As String = doc.SelectSingleNode("/Data/Mail[1]/From").InnerText

However, the best way to do something like this is to load all of the required data into memory up-front. Unless the amount of data is too great, which it doesn't sound like it is, that usually makes the most sense. So, for instance, I would recommend creating a data object that represents a mail message, for instance:

Public Class Mail
    Public Property Subject() As String
        Get
            Return _subject
        End Get
        Set(ByVal value As String)
            _subject = value
        End Set
    End Property
    Private _subject As String

    Public Property From() As String
        Get
            Return _from
        End Get
        Set(ByVal value As String)
            _from = value
        End Set
    End Property
    Private _from As String

    Public Property Content() As String
        Get
            Return _content
        End Get
        Set(ByVal value As String)
            _content = value
        End Set
    End Property
    Private _content As String

    Public Overrides Function ToString() As String
        Return _subject
    End Function
End Class

Then, you can load one Mail object for each Mail element in the XML. Because the ToString method is overriden to display the subject, you can just add the objects directly to the list box, for instance:

Dim doc As New XmlDocument()
For Each node As XmlNode In doc.SelectNodes("/Data/Mail")
    Dim mail As New Mail()
    mail.Subject = node.SelectSingleNode("Subject").InnerText
    mail.From = node.SelectSingleNode("From").InnerText
    mail.Content = node.SelectSingleNode("Content").InnerText
    ListBox1.Items.Add(mail)
Next

Then, when an item in the list box is selected, you can cast the selected item to the Mail type and access its properties, for instance:

Dim mail As Mail = CType(ListBox1.SelectedItem, Mail)
Label1.Text = mail.From
Label2.Text = mail.Content

However, at that point, if the data is not too large, it's even easier to just use the XmlSerializer to simply deserialize the XML into an object, for instance, start by creating a class that defines the entire XML document, like this:

Public Class Data
    <XmlElement("Mail")> _
    Public Property Mails() As List(Of Mail)
        Get
            Return _mails
        End Get
        Set(ByVal value As List(Of Mail))
            _mails = value
        End Set
    End Property
    Private _mails As List(Of Mail)
End Class

Then load the XML into the list box like this (where xml is a string containing the XML document):

Dim serializer As New XmlSerializer(GetType(Data))
Dim reader As New StringReader(xml)
Dim data As Data = CType(serializer.Deserialize(reader), Data)
ListBox1.Items.AddRange(data.Mails.ToArray())

Or, if you want to read from an XML file directly instead of deserializing it from a string:

Dim serializer As New XmlSerializer(GetType(Data))
Using stream As New FileStream("Test.xml", FileMode.Open)
    Dim data As Data = CType(serializer.Deserialize(stream), Data)
    ListBox1.Items.AddRange(data.Mails.ToArray())
End Using

To answer your second question that you asked in your comment below, to delete a given mail message, you could do it like this:

Dim node As XmlNode = doc.SelectSingleNode("/Data/Mail[Subject='Welcome!']")
node.ParentNode.RemoveChild(node)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM