FDecker
FDecker

Reputation: 41

Use XmlReader to get a name and value from an XML File in VB.NET

I am using the following XML file and VB code. I want to be able to get the project name which is in the project tag and also the value of whatever is between the tags. Getting the "Name" value works fine, but I can't seem to figure out how to read any of the sub-items. I want to populate a ListView or possibly a DataGrid with 2 columns of data:

project   |  description
------------------------
file001   |  ABC Project
file002   |  DEF Project

The XML file:

<?xml version="1.0"?>
<menu>
    <header>
        <listname>Files list</listname>
        <lastlistupdate>02/08/2018</lastlistupdate>
    </header>
    <project name="file001" index="true" image="'">
        <description>ABC Project</description>
        <month>January</month>
    </project>
    <project name="file002" index="true" image="'">
        <description>DEF Project</description>
        <month>February</month>
    </project>
        <project name="file003" index="true" image="'">
        <description>Not really important project</description>
        <month>March</month>
    </project>
</menu>

What is odd about this code is that the "IF" test passes which means xr.Name must be "project". However, my log test line (xr.Name.ToString) shows .Name is "description". I don't understand how.

Dim xmlfile As String = ""
Dim filename As String = ""
Dim title As String = ""
ListViewFiles.Items.Clear()
xmlfile = Application.StartupPath & "\projectlist.xml"

Dim xr As XmlReader = XmlReader.Create(xmlfile)
While xr.Read()
    If xr.NodeType = XmlNodeType.Element AndAlso xr.Name = "project" Then
        filename = xr.GetAttribute(0) 'Gets "name" correctly (ex: file001)
        title = Trim(xr.ReadString()) '<<<<-- will not work
        WriteLog("xr.name: " & xr.Name.ToString) <-shows the tag "description"???
        ListViewFiles.Items.Add(New ListViewItem(New String() {filename, title}))
    End If
End While
xr.Close()

Upvotes: 0

Views: 7665

Answers (2)

Andrew Morton
Andrew Morton

Reputation: 25037

You can get VS to make a class which corresponds to the XML file, and then you can use that class to get the data, sometimes very simply.

I put a DataGridView on a Form and used this code:

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

Public Class Form1

    Class Project
        Property Filename As String
        Property Description As String
    End Class

    Private Sub LoadData()
        Dim xmlFile = "C:\temp\projectlist.xml"
        Dim projectsData As Projects.menu

        Dim serializer = New XmlSerializer(GetType(Projects.menu))
        Using fs As New FileStream(xmlFile, FileMode.Open, FileAccess.Read, FileShare.Read)
            Using rdr = XmlReader.Create(fs)
                projectsData = DirectCast(serializer.Deserialize(rdr), Projects.menu)
            End Using
        End Using

        Dim projectsList = projectsData.project.Select(Function(p) New Project With {.Filename = p.name, .Description = p.description}).ToList()

        DataGridView1.DataSource = projectsList

    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        LoadData()
        DataGridView1.AutoResizeColumn(0)
        DataGridView1.AutoResizeColumn(1)

    End Sub

End Class

To get this:

enter image description here

Please adjust the names of classes and properties as needed.

Of course, you need the class to go with the XML file. To do that, copy the XML data and then in Visual Studio choose "Edit"->"Paste Special"->"Paste XML as classes". I chose to paste it into a class named "Projects" and got this:

Public Class Projects

    <System.SerializableAttribute(),
 System.ComponentModel.DesignerCategoryAttribute("code"),
 System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True),
 System.Xml.Serialization.XmlRootAttribute([Namespace]:="", IsNullable:=False)>
    Partial Public Class menu

        Private headerField As menuHeader

        Private projectField() As menuProject

        '''<remarks/>
        Public Property header() As menuHeader
            Get
                Return Me.headerField
            End Get
            Set
                Me.headerField = Value
            End Set
        End Property

        '''<remarks/>
        <System.Xml.Serialization.XmlElementAttribute("project")>
        Public Property project() As menuProject()
            Get
                Return Me.projectField
            End Get
            Set
                Me.projectField = Value
            End Set
        End Property
    End Class

    '''<remarks/>
    <System.SerializableAttribute(),
System.ComponentModel.DesignerCategoryAttribute("code"),
System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)>
    Partial Public Class menuHeader

        Private listnameField As String

        Private lastlistupdateField As String

        '''<remarks/>
        Public Property listname() As String
            Get
                Return Me.listnameField
            End Get
            Set
                Me.listnameField = Value
            End Set
        End Property

        '''<remarks/>
        Public Property lastlistupdate() As String
            Get
                Return Me.lastlistupdateField
            End Get
            Set
                Me.lastlistupdateField = Value
            End Set
        End Property
    End Class

    '''<remarks/>
    <System.SerializableAttribute(),
System.ComponentModel.DesignerCategoryAttribute("code"),
System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)>
    Partial Public Class menuProject

        Private descriptionField As String

        Private monthField As String

        Private nameField As String

        Private indexField As Boolean

        Private imageField As String

        '''<remarks/>
        Public Property description() As String
            Get
                Return Me.descriptionField
            End Get
            Set
                Me.descriptionField = Value
            End Set
        End Property

        '''<remarks/>
        Public Property month() As String
            Get
                Return Me.monthField
            End Get
            Set
                Me.monthField = Value
            End Set
        End Property

        '''<remarks/>
        <System.Xml.Serialization.XmlAttributeAttribute()>
        Public Property name() As String
            Get
                Return Me.nameField
            End Get
            Set
                Me.nameField = Value
            End Set
        End Property

        '''<remarks/>
        <System.Xml.Serialization.XmlAttributeAttribute()>
        Public Property index() As Boolean
            Get
                Return Me.indexField
            End Get
            Set
                Me.indexField = Value
            End Set
        End Property

        '''<remarks/>
        <System.Xml.Serialization.XmlAttributeAttribute()>
        Public Property image() As String
            Get
                Return Me.imageField
            End Get
            Set
                Me.imageField = Value
            End Set
        End Property
    End Class


End Class

P.S. I put the encoding in the XML file's declaration: <?xml version="1.0" encoding="utf-8" ?>.

Upvotes: 2

Marco Sadowski
Marco Sadowski

Reputation: 446

I wrote this code for your XML to get the file name(project/name) and the title (description). I hope you can understand it :)

Dim filename As String = ""
Dim title As String = ""
Dim XMLReader As XmlReader = XMLReader.Create(xmlfile)
With XMLReader
    'As long as the reader hasnt come to the end of the document, this loop is executed'
    Do While .Read
    If .IsStartElement() Then
        Select Case .Name
        Case "project"
            filename = .GetAttribute(0)
            Console.WriteLine(filename)
        Case "description"
            title = .ReadElementString
            Console.WriteLine(title)
            Console.WriteLine("Found: " & filename & " - " & title) 
            'you can place your "final" code here.'
            Exit Select
        Case Else
            .Read()
            'continue reading if nothing is special'
        End Select
    End If
    Loop
    .Close() 'close the reader. All done!'
End With

You can test the code here: https://dotnetfiddle.net/3CDd6Q

In your original code was some mistakes like you used xr.Name to get the element of the description tag but with .Name you only get the name of the <name> tag. You need to use .ReadElementString if you want to get the element between the <>element<> tags.

Upvotes: 2

Related Questions