如何同时检索子XML节点和父XML节点的值?

时间:2012-07-12 01:03:55

标签: xml vb.net winforms treeview

示例XML

<Drawer_System_1>
 <DrawerSystemID>1</DrawerSystemID>
 <DrawerSysName>Drawer_System_1</DrawerSysName>
 <DrawerSysLocation>North Wall (2nd from left)</DrawerSysLocation>
 <Drawers>
  <DrawerID>1-01</DrawerID>`enter code here`
  <Contents>Contents of Drawer 1-01</Contents>
 </Drawers>
 <Drawers>
  <DrawerID>1-02</DrawerID>
  <Contents>Contents of Drawer 1-02</Contents>
 </Drawers>
</Drawer_System_1>

我的问题

如何同时检索子XML节点和父XML节点的值?

我用我的XML文件填充了一个TreeView控件,我想在控件中选择它们之前检索节点和子节点之间的某些值(如字符串)。

我的预期结果

如果我选择Drawer_System_1节点或任何子节点,我想检索DrawerSystemID,DrawerSysName和DrawerSysLocation节点之间的值。

如果我选择Drawers节点或任何子节点,我想保留前面提到的值,并检索DrawerID和Contents节点的值。

每个节点的价值应显示在单个标签中,但如果在多行文本框(或标签)中更容易显示我请求的数据,那也很好。< / p>

备注

我正在使用VB.NET,但如果您能想到C#中的解决方案,那也很好 - 我应该能够使用an online converter将答案转换为VB。

如果有必要,我可以重新构建我的XML文件,以便我的程序更容易阅读。

编辑:到目前为止我的代码在这里:

Imports System
Imports System.Xml
Imports System.Xml.Serialization
Imports System.IO

Public Class My_LEGO_Elements
    Private Sub My_LEGO_Elements_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim serializer As New SimpleXmlSerializer()
        Dim data As LEGOElementsData = serializer.DeSerialize(Of LEGOElementsData)(File.ReadAllText("C:\Users\Steven\Documents\Visual Studio 2012\Projects\My_LEGO_Elements\My_LEGO_Elements\Drawer_Systems_5.xml"))
        ListBox1.Items.AddRange(data.DrawerSystems.ToArray())
    End Sub

    Private Sub ListBox1_SelectedValueChanged(ByVal sender As Object, ByVal e As EventArgs) Handles ListBox1.SelectedValueChanged
        Dim drawerSystem As DrawerSystem = CType(ListBox1.SelectedItem, DrawerSystem)
        DrawerSysIDLabel.Text = drawerSystem.Id
        DrawerSysNameLabel.Text = drawerSystem.Name
        DrawerSysLocLabel.Text = drawerSystem.Location
        'retrieve specific drawer system image from resources
        Dim pictureResource = My.Resources.ResourceManager.GetObject(String.Format("{0}", drawerSystem.Id))
        'convert pictureResource to type Image and display in DrawerSysPictureBox
        DrawerSysPictureBox.Image = CType(pictureResource, Image)
    End Sub 'My_LEGO_Elements_Load

    Private Sub ListBox2_SelectedValueChanged(ByVal sender As Object, ByVal e As EventArgs) Handles ListBox2.SelectedValueChanged
        Dim drawer As Drawer = CType(ListBox2.SelectedItem, Drawer)
        DrawerNumberLabel.Text = drawer.Id
        DrawerContentsLabel.Text = drawer.Contents
    End Sub
End Class 'My_LEGO_Elements

Public Class LEGOElementsData
    Public Property DrawerSystems() As List(Of DrawerSystem)
        Get
            Return _drawerSystems
        End Get
        Set(ByVal value As List(Of DrawerSystem))
            _drawerSystems = value
        End Set
    End Property
    Private _drawerSystems As List(Of DrawerSystem)
End Class


Public Class DrawerSystem
    Public Property Id() As String
        Get
            Return _id
        End Get
        Set(ByVal value As String)
            _id = value
        End Set
    End Property
    Private _id As String

    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
        End Set
    End Property
    Private _name As String

    Public Property Location() As String
        Get
            Return _location
        End Get
        Set(ByVal value As String)
            _location = value
        End Set
    End Property
    Private _location As String

    Public Property Drawers() As List(Of Drawer)
        Get
            Return _drawers
        End Get
        Set(ByVal value As List(Of Drawer))
            _drawers = value
        End Set
    End Property
    Private _drawers As List(Of Drawer)

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


Public Class Drawer
    Public Property Id() As String
        Get
            Return _id
        End Get
        Set(ByVal value As String)
            _id = value
        End Set
    End Property
    Private _id As String

    Public Property Contents() As String
        Get
            Return _contents
        End Get
        Set(ByVal value As String)
            _contents = value
        End Set
    End Property
    Private _contents As String
End Class

Public Class SimpleXmlSerializer
    Public Function Serialize(ByVal objectToSerialize As Object) As String
        Dim serializer As XmlSerializer = New XmlSerializer(objectToSerialize.GetType())
        Using stream As MemoryStream = New MemoryStream()
            Dim namespaces As XmlSerializerNamespaces = New XmlSerializerNamespaces()
            namespaces.Add("", "")
            serializer.Serialize(stream, objectToSerialize, namespaces)
            Using reader As StreamReader = New StreamReader(stream)
                stream.Position = 0
                Return reader.ReadToEnd()
            End Using
        End Using
    End Function

    Public Function DeSerialize(Of T)(ByVal serializedObject As String) As T
        Dim serializer As XmlSerializer = New XmlSerializer(GetType(T))
        Using reader As StringReader = New StringReader(serializedObject)
            Return CType(serializer.Deserialize(reader), T)
        End Using
    End Function
End Class

我按照建议重新构建了我的XML文件。我得到了抽屉系统信息以便正确显示。有没有办法在选择抽屉系统时在第二个ListBox(或其他类型的控件)中显示有关各个抽屉的信息?此外,出于某种原因,我的图像不会显示在PictureBox中。

1 个答案:

答案 0 :(得分:0)

听起来像是一种奇怪的方式。我想如果你将XML加载到TreeView控件中,你会将所有必要的数据存储到TreeNode.Tag属性中,或者存储在TreeView之外的内存中的某些数据结构中,这样当它们选择一个时在节点中,您不必返回XML来获取子元素的数据。但是,如果这是你想要做的,这里有一种从XML中读取数据的方法:

Dim xmlText As String = "<Drawer_System_1>...</Drawer_System_1>..."
Dim doc As New XmlDocument()
doc.LoadXml(xmlText)
Dim parentNode As XmlNode = doc.SelectSingleNode("//Drawer_System_1")
Dim drawerSystemID As String = parentNode.SelectSingleNode("DrawerSystemID").InnerText
Dim drawerSysName As String = parentNode.SelectSingleNode("DrawerSysName").InnerText
Dim drawerSysLocation As String = parentNode.SelectSingleNode("DrawerSysLocation").InnerText

TreeNode.Tag属性只是一个Object属性,您可以将其设置为您想要的任何内容。您可以将其设置为等于字符串,列表,数据集,您自己的自定义对象或其他任何内容。无论对你有什么意义,这就是它的意义所在。它允许您存储树中每个节点的自定义数据,以便您以后可以访问它(例如选择节点时)。例如,如果你有这样的类:

Public Class DrawerSystem
    Public ID As String
    Public Name As String
    Public Location As String
End Class

然后,当您为抽屉系统创建“TreeNode”时,您可以像这样设置它的标签:

Private Sub AddNode(drawerSystem As DrawerSystem)
    Dim node As TreeNode = TreeView1.Nodes.Add(drawerSystem.Name)
    node.Tag = drawerSystem
End Sub

然后,例如,在单击或选择节点时,您可以像这样检索该对象:

Dim drawerSystem As DrawerSystem = CType(e.Node.Tag, DrawerSystem)
Label1.Text = drawerSystem.ID
Label2.Text = drawerSystem.Location

我无法准确理解为什么需要在TreeView中的同一层次结构中显示XML中的所有元素。看起来简单的抽屉系统列表就足够了。然后,当选择列表中的项目时,您可以在其他控件中显示有关该抽屉系统的所有详细信息。此外,我建议只是将XML反序列化为匹配的数据结构,而不是尝试遍历XML。例如,如果你像这样重新格式化XML(只是为了使它更有利于反序列化):

<LegoElementsData>
  <DrawerSystems>
    <DrawerSystem>
      <Id>1</Id>
      <Name>Drawer_System_1</Name>
      <Location>North Wall (2nd from left)</Location>
      <Drawers>
        <Drawer>
          <Id>1-01</Id>
          <Contents>Contents of Drawer 1-01</Contents>
        </Drawer>
        <Drawer>
          <Id>1-02</Id>
          <Contents>Contents of Drawer 1-02</Contents>
        </Drawer>
      </Drawers>
    </DrawerSystem>
  </DrawerSystems>
</LegoElementsData>

然后,您可以在代码中创建匹配的数据结构,例如:

Public Class LegoElementsData
    Public Property DrawerSystems() As List(Of DrawerSystem)
        Get
            Return _drawerSystems
        End Get
        Set(ByVal value As List(Of DrawerSystem))
            _drawerSystems = value
        End Set
    End Property
    Private _drawerSystems As List(Of DrawerSystem)
End Class


Public Class DrawerSystem
    Public Property Id() As String
        Get
            Return _id
        End Get
        Set(ByVal value As String)
            _id = value
        End Set
    End Property
    Private _id As String

    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
        End Set
    End Property
    Private _name As String

    Public Property Location() As String
        Get
            Return _location
        End Get
        Set(ByVal value As String)
            _location = value
        End Set
    End Property
    Private _location As String

    Public Property Drawers() As List(Of Drawer)
        Get
            Return _drawers
        End Get
        Set(ByVal value As List(Of Drawer))
            _drawers = value
        End Set
    End Property
    Private _drawers As List(Of Drawer)

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


Public Class Drawer
    Public Property Id() As String
        Get
            Return _id
        End Get
        Set(ByVal value As String)
            _id = value
        End Set
    End Property
    Private _id As String

    Public Property Contents() As String
        Get
            Return _contents
        End Get
        Set(ByVal value As String)
            _contents = value
        End Set
    End Property
    Private _contents As String

    Public Overrides Function ToString() As String
        Return _id & " - " & _ contents
    End Function
End Class

然后,为了让您的生活更轻松,我建议您制作自己的序列化程序类,以简化过程,例如:

Public Class SimpleXmlSerializer
    Public Function Serialize(ByVal objectToSerialize As Object) As String
        Dim serializer As XmlSerializer = New XmlSerializer(objectToSerialize.GetType())
        Using stream As MemoryStream = New MemoryStream()
            Dim namespaces As XmlSerializerNamespaces = New XmlSerializerNamespaces()
            namespaces.Add("", "")
            serializer.Serialize(stream, objectToSerialize, namespaces)
            Using reader As StreamReader = New StreamReader(stream)
                stream.Position = 0
                Return reader.ReadToEnd()
            End Using
        End Using
    End Function

    Public Function DeSerialize(Of T)(ByVal serializedObject As String) As T
        Dim serializer As XmlSerializer = New XmlSerializer(GetType(T))
        Using reader As StringReader = New StringReader(serializedObject)
            Return CType(serializer.Deserialize(reader), T)
        End Using
    End Function
End Class

然后,当您加载数据时,您可以简单地执行以下操作:

Public Class My_LEGO_Elements
    Private Sub My_LEGO_Elements_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim serializer As New SimpleXmlSerializer()
        Dim data As LegoElementsData = serializer.DeSerialize(Of LegoElementsData)(File.ReadAllText("C:\Users\Steven\Documents\Visual Studio 2012\Projects\My_LEGO_Elements\My_LEGO_Elements\Drawer_Systems_3.xml"))
        ListBox1.Items.AddRange(data.DrawerSystems.ToArray())
    End Sub

    Private Sub ListBox1_SelectedValueChanged(ByVal sender As Object, ByVal e As EventArgs) Handles ListBox1.SelectedValueChanged
        Dim drawerSystem As DrawerSystem = CType(ListBox1.SelectedItem, DrawerSystem)
        Label1.Text = drawerSystem.Id
        Label2.Text = drawerSystem.Location
        Label3.Text = drawerSystem.Name
        ListBox2.Items.Clear()
        ListBox2.Items.AddRange(drawerSystem.Drawers.ToArray())
    End Sub
End Class