1

I am a VB6 veteran gradually coming to grips with VB.NET. I have a working app that I am trying to configure using XML files (other than basic app settings).

I want to read elements from a single XML node within my file and create an array of strings. I have searched this site and others and have found many examples that perform very similar operations, but can't quite get the grasp of it (especially if the question / answer is in C#).

I'm no XML expert, but I know how to select the single node and I could write some reasonably neat code that would iterate through the child elements and manually build the array by using redim, and that would work fine. BUT ... i'm trying to learn new techniques by specific example.

So ... it seems that with maybe 2 or 3 fairly compact statements that I should be able to write a LINQ query and use the .ToArray() extension method to populate the array without resorting to a looping construct.

Here is a representation of my XML:

<?xml version="1.0"?>
<targets>
  <target name="abc">
    <index>0</index>
    <randoms>
      <string>index</string>
      <string>local</string>
      <string>news</string>
      <string>journal</string>
      <string>information</string>
    </randoms>
  </target>
  <target name="xyz">
    <index>1</index>
    <randoms>
      <string>cat</string>
      <string>dog</string>
      <string>mouse</string>
    </randoms>
  </target>
</targets>

The elements I want to build the array from are the "string" elements. In my code I would use the target index to select the appropriate node (there would be nodes other than "randoms" but they are not relevant to this example.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
  • I have answered this question a couple of times before http://stackoverflow.com/questions/26536426/vb-net-xml-get-a-group-of-nodes-attributes-and-group-them-by-node/26536819#26536819 and http://stackoverflow.com/questions/23431411/how-to-change-an-xml-file-elements-attributes-in-vb-net/23431631#23431631 – djv Dec 05 '14 at 22:51
  • BTW I also moved from VB6 to VB.Net, however it was years ago. You should embrace .NET classes as often as possible. Serialization is there to make this sort of thing easier. There will be solutions where XML is parsed or iterated, but I believe this method is much clearer and more object oriented. – djv Dec 05 '14 at 22:53
  • I appreciate the comments @Verdolino (and will make a point of using serialization soon) but your answers don't demonstrate usage of LINQ and the .ToArray() extension method. – Phoenix.NET Dec 05 '14 at 23:00
  • 1
    Considering the first example, you don't need to use `ToArray()` if you've serialized into the `XmlModel` class. That class has a `List(Of XmlMob)` which is IEnumerable. – djv Dec 05 '14 at 23:03

1 Answers1

0

This solution creates an object, saves it to the xml file, sets it to nothing, then loads it back from the xml file. Make a new console app and overwrite all the code with what you see below.

Bonus: ToArray() is used to prove that the class' list member is IEnumerable

Imports System.Xml.Serialization
Imports System.IO

Module Module1

    Private targets As XmlTargets

    Sub Main()
        targets = New XmlTargets
        targets.Targets = New List(Of XmlTarget)

        Dim t1 As New XmlTarget
        t1.Name = "abc"
        t1.Index = 0
        t1.Randoms = New Random()
        t1.Randoms.Strings = New List(Of [String])
        Dim s1 As New [String]()
        s1.Type = 1
        s1.Text = "index"
        Dim s2 As New [String]()
        s2.Type = 2
        s2.Text = "value"
        t1.Randoms.Strings.Add(s1)
        t1.Randoms.Strings.Add(s2)


        Dim t2 As New XmlTarget
        t2.Name = "xyz"
        t2.Index = 1
        t2.Randoms = New Random()
        t2.Randoms.Strings = New List(Of [String])

        targets.Targets.Add(t1)
        targets.Targets.Add(t2)
        SaveXmlFile()
        targets = Nothing
        LoadXmlFile()
        Dim ts = targets.Targets.ToArray()
    End Sub

    Private Sub LoadXmlFile()
        Dim s As New XmlSerializer(GetType(XmlTargets))
        Using sr As New StreamReader("test.xml")
            targets = s.Deserialize(sr)
        End Using
    End Sub

    Private Sub SaveXmlFile()
        Dim s As New XmlSerializer(GetType(XmlTargets))

        Using sw As New StreamWriter("test.xml")
            s.Serialize(sw, targets)
        End Using
    End Sub

End Module

<XmlRoot("targets")>
Public Class XmlTargets
    <XmlElement("target")>
    Public Property Targets As List(Of XmlTarget)
End Class

<Serializable>
Public Class XmlTarget
    <XmlAttribute("name")>
    Public Property Name As String
    <XmlElement("index")>
    Public Property Index As Integer
    <XmlElement("randoms")>
    Public Property Randoms As Random
End Class

<Serializable>
Public Class Random
    <XmlElement("string")>
    Public Property Strings As List(Of [String])
End Class

<Serializable>
Public Class [String]
    <XmlAttribute("type")>
    Public Property [Type] As Integer
    <XmlText>
    Public Property Text As String
End Class

This is the XML file the software created. Note: I did not edit this file outside of the code above - it was created by serializing the class.

<?xml version="1.0" encoding="utf-8"?>
<targets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <target name="abc">
    <index>0</index>
    <randoms>
      <string type="1">index</string>
      <string type="2">value</string>
    </randoms>
  </target>
  <target name="xyz">
    <index>1</index>
    <randoms />
  </target>
</targets>

Post has been edited to reflect change requested in comment below.

djv
  • 15,168
  • 7
  • 48
  • 72
  • Thanks for taking the time to flesh out an example. I can now see how simple and useful serialization will be, and you have not given me the answer I thought i wanted, but the answer I probably need. – Phoenix.NET Dec 06 '14 at 10:22
  • I have implemented deserialization as suggested (I have no use for serialization in this particular case) and it worked as expected. My problem now is that if I add an attribute to the string element (i.e. 'index' I can't seem to make that work. I've googled and read through questions on Stack Overflow until I've gone cross-eyed! – Phoenix.NET Dec 08 '14 at 11:28
  • You need another class. I can add it in when I get to my desk – djv Dec 08 '14 at 14:34
  • Please see my edit. Your xml `index` would use the `` attribute. I've changed the class to have just that. – djv Dec 08 '14 at 16:05
  • Without that line, it's not clear that `targets.Targets.ToArray()` in the last line has changed from the original data before serializing / deserializing. Maybe code could have been clearer but this is just a demo. – djv Dec 08 '14 at 19:30