【问题标题】:Use XmlReader to get a name and value from an XML File in VB.NET使用 XmlReader 从 VB.NET 中的 XML 文件中获取名称和值
【发布时间】:2018-12-06 23:02:56
【问题描述】:

我正在使用以下 XML 文件和 VB 代码。我希望能够获取项目标签中的项目名称以及标签之间的任何值。获取“名称”值工作正常,但我似乎无法弄清楚如何阅读任何子项目。我想用 2 列数据填充 ListView 或可能的 DataGrid:

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

XML 文件:

<?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>

这段代码的奇怪之处在于“IF”测试通过了,这意味着 xr.Name 必须是“project”。但是,我的日志测试行 (xr.Name.ToString) 显示 .Name 是“描述”。我不明白怎么做。

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()

【问题讨论】:

    标签: xml vb.net xmlreader


    【解决方案1】:

    你可以让VS做一个对应XML文件的类,然后你可以用那个类来获取数据,有时很简单。

    我在表单上放了一个 DataGridView 并使用了这段代码:

    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
    

    要得到这个:

    请根据需要调整类和属性的名称。

    当然,您需要类与 XML 文件一起使用。为此,请复制 XML 数据,然后在 Visual Studio 中选择“编辑”->“选择性粘贴”->“将 XML 粘贴为类”。我选择将它粘贴到一个名为“Projects”的类中并得到这个:

    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
    

    附:我将编码放在 XML 文件的声明中:&lt;?xml version="1.0" encoding="utf-8" ?&gt;

    【讨论】:

    • 哇,跳出框框思考。不仅仅是对我的问题的简单回答,而是一种全新的解决方法。如果我想将 XML 文件加载到数据库中并将该数据库链接到 DataGridView 以便我可以保存文件并稍后加载它以进行编辑,该怎么办?我将使用您的 DataGridView 想法,添加一个复选框列,然后删除驱动器上所有已检查的项目,并删除那些已删除项目的行。
    • 将 XML 文件加载到数据库中并将该数据库链接到 DataGridView 以便以后可以保存和加载以进行编辑,这与最初的问题有点太远了,无法继续这个问答;)
    • 很公平!我假设当您加载 xml 并使用“将 XML 粘贴为类”时,您将获得整个文件。我的实际文件需要 10 分钟来处理。我给了你一个对整个 XLM 文件重复的子集。只有 1 条记录或 2 或 3 条记录的 XML 文件可以完成这项工作,还是我必须使用整个文件?
    • 文件的代表性样本就足够了 - 如果有重复的实体,您需要包含多个实体,以便它可以判断它们是重复的。因此,只需 2 或 3 个即可完成这项工作。
    【解决方案2】:

    我为您的 XML 编写了这段代码来获取文件名(项目/名称)和标题(描述)。我希望你能理解它:)

    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
    

    您可以在这里测试代码:https://dotnetfiddle.net/3CDd6Q

    在您的原始代码中存在一些错误,例如您使用xr.Name 获取描述标签的元素,但使用.Name 您只能获得&lt;name&gt; 标签的名称。如果要获取&lt;&gt;element&lt;&gt; 标签之间的元素,则需要使用.ReadElementString

    【讨论】:

    • 很难选择实际答案!这完全解决了我的要求并教会了我关于 dotnetfiddle 的知识。另一个答案给了我一种全新的看待它的方式。我现在正在使用这种方法并学习另一种方法。完美运行。有没有办法在像这样的流阅读器中知道它必须经历的所有事情的“计数”是什么,或者直到它完成才知道?如果可能的话,我想要一个进度条。实际的 XML 文件有数千个项目,需要几分钟时间。
    猜你喜欢
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多