Reputation: 41
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
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:
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
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