none
Extracting information from an xml nodelist

    Question

  • I have an xml file that looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <!--filesdefinition xml-->
    <file_types>
        <csv_files>
            <file type="test csv file 1">
                <file_info>
                    <data_type>type 1</data_type>
                    <ID_header_rows>1,2</ID_header_rows>
                    <data_start_row>3</data_start_row>
                </file_info>
                <other_things>
                    <thing1>1</thing1>
                    <thing2>2</thing2>
                </other_things>
            </file>
            <file type="test csv file 2">
                <file_info>
                    <data_type>type 1</data_type>
                    <ID_header_rows>1,5</ID_header_rows>
                    <data_start_row>6</data_start_row>
                </file_info>
                <other_things>
                    <thing1>4</thing1>
                    <thing2>5</thing2>
                </other_things>
            </file>
        </csv_files>
    </file_types>

    I have some code which reads each of the csv_file information into a nodelist:

            Dim xml_file1 As XmlDocument
            Dim xml_nodelist As XmlNodeList
            Dim xml_node As XmlNode
    
            xml_file1 = New XmlDocument()
            xml_file1.Load("C:\TEST.xml")
    
            xml_nodelist = xml_file1.SelectNodes("file_types/csv_files")

    **Update**

    With this:

    Dim bob as string
    
            For Each xml_node In xml_nodelist
                bob = xml_node("file_info").InnerText
            Next
    I can get the complete contents of the file_info element but can't find a way to specifically select the ID_header_rows.  Is there a way to specifically recover this informationwithin this nodelist??


    • Edited by Mister S Friday, April 28, 2017 1:12 PM found more info
    Friday, April 28, 2017 10:51 AM

Answers

  • Here is a quickie

    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim fileName As String = IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "XMLFile1.xml")
            Dim Doc As XDocument = XDocument.Load(fileName)
    
            DataGridView1.DataSource =
                (From root In Doc...<csv_files>
                 From level1 In root.<file>
                 From level2 In level1.<file_info>
                 Select New Demo With
                     {
                         .ItemType = level1.@type,
                         .ID_header_rows = level1...<ID_header_rows>.Value
                     }).ToList
        End Sub
    End Class
    Class Demo
        Property ItemType As String
        Property ID_header_rows As String
    End Class
    


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    • Marked as answer by Mister S Tuesday, May 2, 2017 5:44 PM
    Saturday, April 29, 2017 12:00 AM
    Moderator

All replies

  • MS,

    I took a different approach to this: I created a 'main' class that also has instances of two other classes. Essentially, matching back the data that's in the XML file. It might be worth trying:

    Option Strict On Option Explicit On Option Infer Off Imports System.IO Imports System.IO.Path Public Class Form1 Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load Dim desktop As String = _ Environment.GetFolderPath(Environment.SpecialFolder.Desktop) Dim xPath As String = _ Combine(desktop, "Example.xml") Dim myList As New List(Of CSV_Files) CSV_Files.ParseXML(xPath, myList) Stop End Sub End Class Public Class CSV_Files Private _dataType As String Private _fileInfo As CSV_FileInfo Private _otherThings As OtherThings Private Sub New(ByVal dataType As String, _ ByVal info As CSV_FileInfo, _ ByVal otherThings As OtherThings) _dataType = dataType _fileInfo = info _otherThings = otherThings End Sub Public Shared Sub ParseXML(ByVal xmlFilePath As String, _ ByVal cList As List(Of CSV_Files)) Try If String.IsNullOrWhiteSpace(xmlFilePath) Then Throw New ArgumentException("The file path cannot be null or empty.") ElseIf cList Is Nothing Then Throw New ArgumentNullException("List(Of CSV_Files)", "Cannot be null." & vbCrLf) Else Dim fi As New FileInfo(xmlFilePath) If Not fi.Exists Then Throw New FileNotFoundException("The file path could not be located.") Else cList.Clear() Dim xDoc As XElement = XElement.Load(fi.FullName) For Each cInfo As XElement In xDoc...<file> Dim fileType As String = cInfo.@type Dim cFileInfo As CSV_FileInfo = Nothing Dim oThings As OtherThings = Nothing Dim cData As XElement = cInfo...<file_info>.FirstOrDefault If cData IsNot Nothing Then Dim rows() As Integer Dim startRow As Integer Dim dType As String = cData...<data_type>.Value Dim rowString() As String = (cData...<ID_header_rows>.Value).Split(","c) Dim dStart As String = cData...<data_start_row>.Value If rowString IsNot Nothing AndAlso _ rowString.Length > 0 AndAlso _ Not String.IsNullOrWhiteSpace(dStart) Then Dim tempList As New List(Of Integer) For Each element As String In rowString If Integer.TryParse(element, New Integer) Then tempList.Add(CInt(element)) End If Next If tempList.Count > 0 Then rows = tempList.ToArray End If If Integer.TryParse(dStart, New Integer) Then startRow = CInt(dStart) End If cFileInfo = New CSV_FileInfo(dType, rows, startRow) End If End If Dim cOther As XElement = cInfo...<other_things>.FirstOrDefault If cOther IsNot Nothing Then oThings = New OtherThings(cOther...<thing1>.Value, _ cOther...<thing2>.Value) End If If Not String.IsNullOrWhiteSpace(fileType) AndAlso cFileInfo IsNot Nothing AndAlso oThings IsNot Nothing Then cList.Add(New CSV_Files(fileType, cFileInfo, oThings)) End If Next End If End If Catch ex As Exception Throw End Try End Sub Public ReadOnly Property DataType As String Get Return _dataType End Get End Property Public ReadOnly Property FileInfo As CSV_FileInfo Get Return _fileInfo End Get End Property Public ReadOnly Property OtherThings As OtherThings Get Return _otherThings End Get End Property End Class Public Class CSV_FileInfo Private _dataType As String Private _iD_HeaderRows() As Integer Private _dataStartRow As Integer Friend Sub New(ByVal dataType As String, _ ByVal headerRows() As Integer, _ ByVal startRow As Integer) If Not String.IsNullOrWhiteSpace(dataType) Then _dataType = dataType.Trim _iD_HeaderRows = headerRows _dataStartRow = startRow End If End Sub Public ReadOnly Property DataStartRow As Integer Get Return _dataStartRow End Get End Property Public ReadOnly Property DataStartRow_String As String Get Return _dataStartRow.ToString("n0") End Get End Property Public ReadOnly Property ID_HeaderRows As Integer() Get Return _iD_HeaderRows End Get End Property Public ReadOnly Property ID_HeaderRows_String As String Get If _iD_HeaderRows IsNot Nothing AndAlso _iD_HeaderRows.Length > 0 Then Return String.Join(",", _iD_HeaderRows) Else Return "--" End If End Get End Property Public ReadOnly Property DataType As String Get Return _dataType End Get End Property End Class Public Class OtherThings Private _thing1 As String = "--" Private _thing2 As String = "--" Friend Sub New(ByVal thing1 As String) If Not String.IsNullOrWhiteSpace(thing1) Then _thing1 = thing1.Trim End If End Sub Friend Sub New(ByVal thing1 As String, _ ByVal thing2 As String) If Not String.IsNullOrWhiteSpace(thing1) Then _thing1 = thing1.Trim End If If Not String.IsNullOrWhiteSpace(thing2) Then _thing2 = thing2.Trim End If End Sub Public ReadOnly Property Thing1 As String Get Return _thing1 End Get End Property Public ReadOnly Property Thing2 As String Get Return _thing2 End Get End Property End Class


    When I run it, it will "stop" at the Stop that's in the .Load event handler:

    If you then expand each level, you'll see that all of the information is there:

    Having it in class instances like that should allow you to do whatever you need with it.

    Give that a go and let me know if that helps please?


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Friday, April 28, 2017 1:34 PM
  • Thanks for the really detailed reply.  I was hoping to continue to use XmlDocument as I find it suited to what I need to do elsewhere.  Is there an implementation using this as well?
    Friday, April 28, 2017 4:28 PM
  • I was hoping to continue to use XmlDocument as I find it suited to what I need to do elsewhere.  Is there an implementation using this as well?

    I know of it, but I've never used it. It looks like you're probably on the right track though, so experiment with it, test, repeat. ;-)

    The worst that will happen is that you learn something that you'll eventually use somewhere.

    :)


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Friday, April 28, 2017 4:32 PM
  • I'll keep huntin but I have been now for 2 days! Thinking about I'll also need to be able pick a file type and then examine each of its individual elements. This is along similar lines to the above. I know how to create xmlnodelists but not sure how to delve down into the elements. The order is likely to move around so referencing an element name (not an index) would be they best way forward. There may be an even easier way to do it so any suggestions would be most welcome.
    Friday, April 28, 2017 8:24 PM
  • There may be an even easier way to do it so any suggestions would be most welcome.

    MS,

    Let's start from the beginning: In non-code lingo, tell me what your program does please?


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Friday, April 28, 2017 8:32 PM
  • Personally I prefer XElement.  It seems to me that it is easier to work with, especially with XML literals and LINQ.  Take a look at this

            'Dim yourpath As String = "C:\TEST.xml"
            Dim xml_file1 As XElement
            ' to load from a file
            'xml_file1 = XElement.Load(yourpath)
            ' for testing
            xml_file1 = <file_types>
                            <csv_files>
                                <file type="test csv file 1">
                                    <file_info>
                                        <data_type>type 1</data_type>
                                        <ID_header_rows>1,2</ID_header_rows>
                                        <data_start_row>3</data_start_row>
                                    </file_info>
                                    <other_things>
                                        <thing1>1</thing1>
                                        <thing2>2</thing2>
                                    </other_things>
                                </file>
                                <file type="test csv file 2">
                                    <file_info>
                                        <data_type>type 1</data_type>
                                        <ID_header_rows>1,5</ID_header_rows>
                                        <data_start_row>6</data_start_row>
                                    </file_info>
                                    <other_things>
                                        <thing1>4</thing1>
                                        <thing2>5</thing2>
                                    </other_things>
                                </file>
                            </csv_files>
                        </file_types>
    
            'get all ID_header_rows
            Dim AllHdrRWS As IEnumerable(Of XElement)
            AllHdrRWS = xml_file1...<ID_header_rows>
            'look at the values
            For Each el As XElement In AllHdrRWS
                Debug.WriteLine(el.Value)
            Next
    
            'get file_infos for all ID_header_rows
            Dim HdrRWSFileInfo As IEnumerable(Of XElement)
            HdrRWSFileInfo = From el In AllHdrRWS Select el.Parent
            'look at the values
            For Each el As XElement In HdrRWSFileInfo
                For Each sel As XElement In el.Elements
                    Debug.WriteLine(sel.Name.LocalName & "  " & sel.Value)
                Next
            Next
    
    
            ' to save file
            ' xml_file1.Save(yourpath)
    


    "Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it." - MSDN User JohnWein    Multics - An OS ahead of its time.

    Friday, April 28, 2017 10:39 PM
  • Here is a quickie

    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim fileName As String = IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "XMLFile1.xml")
            Dim Doc As XDocument = XDocument.Load(fileName)
    
            DataGridView1.DataSource =
                (From root In Doc...<csv_files>
                 From level1 In root.<file>
                 From level2 In level1.<file_info>
                 Select New Demo With
                     {
                         .ItemType = level1.@type,
                         .ID_header_rows = level1...<ID_header_rows>.Value
                     }).ToList
        End Sub
    End Class
    Class Demo
        Property ItemType As String
        Property ID_header_rows As String
    End Class
    


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    • Marked as answer by Mister S Tuesday, May 2, 2017 5:44 PM
    Saturday, April 29, 2017 12:00 AM
    Moderator
  • Here is a quickie

    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim fileName As String = IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "XMLFile1.xml")
            Dim Doc As XDocument = XDocument.Load(fileName)
    
            DataGridView1.DataSource =
                (From root In Doc...<csv_files>
                 From level1 In root.<file>
                 From level2 In level1.<file_info>
                 Select New Demo With
                     {
                         .ItemType = level1.@type,
                         .ID_header_rows = level1...<ID_header_rows>.Value
                     }).ToList
        End Sub
    End Class
    Class Demo
        Property ItemType As String
        Property ID_header_rows As String
    End Class


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    I think Im nearly there with this one, just the information I need.  The only thing is that I need to extract each element as a string instead of a datasource but my VB isnt good enough to work out how its done!  I someone could point me in the right direction it would be much appreciated!
    Saturday, April 29, 2017 3:57 PM