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

问题描述:

示例XML如何同时检索子节点和父节点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文件填充了一个TreeView控件,并且我想在节点和子节点之间检索某些值(如字符串),因为我在控件中选择它们。

我的预期结果

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

如果我选择一个抽屉节点或任何子节点,我想保留前面提到的值,并检索DrawerID和内容节点的值。

每个节点的值应显示在单独的标签中,但如果它更容易在多行文本框(或标签)中显示我所请求的数据,那也可以。

注意

我使用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中。

听起来像一个奇怪的方式来做到这一点。我想如果你将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 

然后,当你对一个抽屉系统创建一个'树节点”,你可以设置是这样的标签:

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 

我不明白到底为什么你需要显示所有的XML在同一个元素教主y在TreeView中。看起来像一个简单的抽屉系统清单就足够了。然后,当列表中的某个项目被选中时,您可以在其他控件中显示该抽屉系统的所有详细信息。另外,我不建议循环访问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 
+0

谢谢您的回答。但是,这似乎并不适合我。 (Visual Studio 2012 RC显示SelectNode方法的错误。)如何从TreeNode.Tag属性读取数据? – 2012-07-12 19:29:39

+0

@ user1517987我更新了我的答案,以修复我的错误并更清楚地解释标签的含义。 – 2012-07-12 19:36:04

+0

为答谢新的信息,但我不知道在哪里或如何把它放在我的代码。我只是将我的代码添加到我的问题中以供参考。 – 2012-07-12 22:57:47