locked
Visual Studio 2013 (Visual Basic) Serializing an Array of Objects

    Question

  • So, I'm at the point where I have the XML loading, saving to a local file and is readable by the application. The issue is when I run the routine to save instead of rewriting the XML file with all Vehicles I'm only getting one.  I'm thinking it is writing over all other entries and only returning the one that is the last.

    Example I used is located here:

    http://msdn.microsoft.com/en-us/library/aa719523.aspx

    I have the Array setup pretty much like the examples that I have seen, but I can't figure out how to add multiple entries in the Array of "Vehicles"

    Original File on Load:

    <?xml version="1.0" encoding="utf-8" ?>
    <?test value?>
      <!--Vechicle Data for the Application - Entered by User-->
    <Vehicles>
      <Vehicle>
        <VehicleVIN>SAMPLEVIN1</VehicleVIN>
        <VehicleNCAPModelID>8160</VehicleNCAPModelID>
        <ModelYear>2014</ModelYear>
        <Make>Toyota</Make>
        <Model>Prius</Model>
      </Vehicle>
      <Vehicle>
        <VehicleVIN>SAMPLEVIN2</VehicleVIN>
        <VehicleNCAPModelID>3966</VehicleNCAPModelID>
        <ModelYear>1999</ModelYear>
        <Make>Honda</Make>
        <Model>Civic</Model>
      </Vehicle>
       <Vehicle>
        <VehicleVIN>SAMPLEVIN3</VehicleVIN>
         <VehicleNCAPModelID>1578</VehicleNCAPModelID>
         <ModelYear>2005</ModelYear>
        <Make>Ford</Make>
        <Model>Ranger</Model>
      </Vehicle>
       <Vehicle>
        <VehicleVIN>SAMPLEVIN4</VehicleVIN>
         <VehicleNCAPModelID>2174</VehicleNCAPModelID>
         <ModelYear>2007</ModelYear>
        <Make>Toyota</Make>
        <Model>Corolla</Model>
      </Vehicle>
      <Vehicle>
        <VehicleVIN>SAMPLEVIN5</VehicleVIN>
        <VehicleNCAPModelID>7731</VehicleNCAPModelID>
        <ModelYear>2013</ModelYear>
        <Make>Acura</Make>
        <Model>RDX</Model>
      </Vehicle>
    </Vehicles>

    Output File after Running "Edit" routine

    <?xml version="1.0" encoding="utf-8"?>
    <Vehicles xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Vehicle VehicleUID="TESTVEH1">
        <VehicleVIN>SAMPLEVIN5</VehicleVIN>
        <VehicleMake>ACURA</VehicleMake>
        <VehicleModel>RDX</VehicleModel>
        <VehicleNCAPID>7520</VehicleNCAPID>
      </Vehicle>
    </Vehicles>

    Routine snippets to make it make sense

    Public Class VehicleCollection
        Public Vehicles() As Vehicle
    End Class
    
    Public Class Vehicle
        Public VehicleUID As String
        Public VehicleVIN As String
        Public ModelYear As String
        Public Make As String
        Public Model As String
        Public VehicleNCAPModelID As String
    End Class
        Public Async Sub WriteXML(vehVIN As String)
            Dim myvehiclescan As Object
    
            ' Obtain Local Copy of Data for Importing into the Application
            Dim _readfolder As StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder
            Dim _readfile As StorageFile = Await _readfolder.GetFileAsync("VehicleData.xml")
            Dim _readstream As Stream = Await _readfile.OpenStreamForReadAsync()
            Dim _readserializer As New System.Xml.Serialization.XmlSerializer(GetType(VehicleCollection))
            Dim reader As New System.IO.StreamReader(_readstream)
    
            Dim MyVehiclesXml As XElement = XElement.Load(reader)
            'Dim MyVehiclesXml As XElement = XElement.Load("VehicleData/VehicleData.xml")
            Dim myvehiclesquery = From myVehicleInfo In MyVehiclesXml...<Vehicle> _
                           Select VehicleVIN = myVehicleInfo.<VehicleVIN>.Value, VehicleYear = myVehicleInfo.<ModelYear>.Value, _
                            VehicleNCAPModelID = myVehicleInfo.<VehicleNCAPModelID>.Value, _
                            VehicleMake = myVehicleInfo.<Make>.Value, VehicleModel = myVehicleInfo.<Model>.Value
    
            _readstream.Dispose()
    
            ' Establish XML Structure for New File
            Dim vehiclefile As New VehicleCollection()
    
            For Each myvehiclescan In myvehiclesquery
                Dim i1 As New Vehicle()
                ' load vehicle data for stored values
                If myvehiclescan.VehicleVIN = vehVIN Then
                    i1.VehicleUID = "TESTVEH1"
                    i1.VehicleVIN = vehVIN
                    i1.ModelYear = txtMakeYear.Text
                    i1.Make = txtMakeName.Text
                    i1.Model = txtModelName.Text
                    i1.VehicleNCAPModelID = cmbNCAPID.SelectedValue
                Else
                    ' Establish Values for Vehicle
                    i1.VehicleUID = "TESTVEH2"
                    i1.VehicleVIN = myvehiclescan.VehicleVIN
                    i1.ModelYear = myvehiclescan.VehicleYear
                    i1.Make = myvehiclescan.VehicleMake
                    i1.Model = myvehiclescan.VehicleModel
                    i1.VehicleNCAPModelID = myvehiclescan.VehicleNCAPModelID
                End If
                Dim vehicles(0) As Vehicle
                vehicles(0) = i1
                ' Populate Vehicle Information
                vehiclefile.Vehicles = vehicles
            Next
    
            Dim folder As StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder
            Dim file As StorageFile = Await folder.CreateFileAsync("VehicleData.xml", CreationCollisionOption.ReplaceExisting)
            Dim stream As Stream = Await file.OpenStreamForWriteAsync()
            Dim serializer As New System.Xml.Serialization.XmlSerializer(GetType(VehicleCollection))
            Dim writer As New System.IO.StreamWriter(stream)
    
            serializer.Serialize(writer, vehiclefile)
            stream.Dispose()
    
            ' Reload Vehicle List Data
            ' Error if no Internet Connection
            Dim connectionProfile = Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile()
            If connectionProfile IsNot Nothing Then
                Dim myvehiclesDataSource = DirectCast(App.Current.Resources("myvehiclesDataSource"), MyVehiclesDataSource)
                Await myvehiclesDataSource.GetMyVehiclesAsync()
            Else
                Dim messageDialog = New Windows.UI.Popups.MessageDialog("An internet connection is needed to download feeds. Please check your connection and restart the app.")
                Dim result = messageDialog.ShowAsync()
            End If
    
            ' Return to Vehicle Items Listing
            'Me.Frame.Navigate(backButton)
    
        End Sub



    M. Frenchik Developer At Large




    • Edited by GoliathRulz Saturday, June 21, 2014 4:13 AM
    Saturday, June 21, 2014 4:05 AM

Answers

  • Got it solved.

    The line of

                Dim i1 As New Vehicle()

    Needs to be within the loop.  This will cause a new "group" to be created and not write over the existing one(s).

    Full code below for those who want to know:

        Public Async Sub WriteXML(vehVIN As String)
            Dim myvehiclescan As Object
    
            ' Establish XML Structure for New File
            Dim vehiclefile As New VehicleCollection()
            Dim count As Integer = 0
    
    
            ' Obtain Local Copy of Data for Importing into the Application
            Dim _readfolder As StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder
            Dim _readfile As StorageFile = Await _readfolder.GetFileAsync("VehicleData.xml")
            Dim _readstream As Stream = Await _readfile.OpenStreamForReadAsync()
            Dim _readserializer As New System.Xml.Serialization.XmlSerializer(GetType(VehicleCollection))
            Dim reader As New System.IO.StreamReader(_readstream)
    
            Dim MyVehiclesXml As XElement = XElement.Load(reader)
            'Dim MyVehiclesXml As XElement = XElement.Load("VehicleData/VehicleData.xml")
            Dim myvehiclesquery = From myVehicleInfo In MyVehiclesXml...<Vehicle> _
                           Select VehicleVIN = myVehicleInfo.<VehicleVIN>.Value, VehicleYear = myVehicleInfo.<ModelYear>.Value, _
                            VehicleNCAPModelID = myVehicleInfo.<VehicleNCAPModelID>.Value, _
                            VehicleMake = myVehicleInfo.<Make>.Value, VehicleModel = myVehicleInfo.<Model>.Value
    
            _readstream.Dispose()
    
            Dim vehiclecnt() As Vehicle
    
            For Each myvehiclescan In myvehiclesquery
                ' load vehicle data for stored values
                ReDim Preserve vehiclecnt(count)
                Dim i1 As New Vehicle()
                If myvehiclescan.VehicleVIN = vehVIN Then
                    i1.VehicleUID = "TESTVEH1"
                    i1.VehicleVIN = vehVIN
                    i1.ModelYear = txtMakeYear.Text
                    i1.Make = txtMakeName.Text
                    i1.Model = txtModelName.Text
                    i1.VehicleNCAPModelID = cmbNCAPID.SelectedValue
                Else
                    ' Establish Values for Vehicle
                    i1.VehicleUID = "TESTVEH2"
                    i1.VehicleVIN = myvehiclescan.VehicleVIN
                    i1.ModelYear = myvehiclescan.VehicleYear
                    i1.Make = myvehiclescan.VehicleMake
                    i1.Model = myvehiclescan.VehicleModel
                    i1.VehicleNCAPModelID = myvehiclescan.VehicleNCAPModelID
                End If
                vehiclecnt(count) = i1
                count += 1
                ' Populate Vehicle Information
                vehiclefile.Vehicles = vehiclecnt
            Next
    
            Dim folder As StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder
            Dim file As StorageFile = Await folder.CreateFileAsync("VehicleData.xml", CreationCollisionOption.ReplaceExisting)
            Dim stream As Stream = Await file.OpenStreamForWriteAsync()
            Dim serializer As New System.Xml.Serialization.XmlSerializer(GetType(VehicleCollection))
            Dim writer As New System.IO.StreamWriter(stream)
    
            serializer.Serialize(writer, vehiclefile)
            stream.Dispose()
    
            ' Reload Vehicle List Data
            ' Error if no Internet Connection
            Dim connectionProfile = Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile()
            If connectionProfile IsNot Nothing Then
                Dim myvehiclesDataSource = DirectCast(App.Current.Resources("myvehiclesDataSource"), MyVehiclesDataSource)
                Await myvehiclesDataSource.GetMyVehiclesAsync()
            Else
                Dim messageDialog = New Windows.UI.Popups.MessageDialog("An internet connection is needed to download feeds. Please check your connection and restart the app.")
                Dim result = messageDialog.ShowAsync()
            End If
    
        End Sub

    Need to have Class Information setup as such ....

    Public Class VehicleCollection
        Public Vehicles() As Vehicle
    End Class
    
    Public Class Vehicle
        Public VehicleUID As String
        Public VehicleVIN As String
        Public ModelYear As String
        Public Make As String
        Public Model As String
        Public VehicleNCAPModelID As String
    End Class


    M. Frenchik Developer At Large

    • Marked as answer by GoliathRulz Saturday, June 21, 2014 5:41 PM
    Saturday, June 21, 2014 5:41 PM

All replies

  • It looks like you are overwriting the existing file when writing the xml to storage.  Are you sure you are including xml for all the items in the list not just the last one?
    Saturday, June 21, 2014 1:19 PM
  • OK, I got this far but now I get <count> vehicles all the same from the most recent myvehiclescan:

            ' Establish XML Structure for New File
            Dim vehiclefile As New VehicleCollection()
            Dim count As Integer = 0
            Dim i1 As New Vehicle()
    
            ' Obtain Local Copy of Data for Importing into the Application
            Dim _readfolder As StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder
            Dim _readfile As StorageFile = Await _readfolder.GetFileAsync("VehicleData.xml")
            Dim _readstream As Stream = Await _readfile.OpenStreamForReadAsync()
            Dim _readserializer As New System.Xml.Serialization.XmlSerializer(GetType(VehicleCollection))
            Dim reader As New System.IO.StreamReader(_readstream)
    
            Dim MyVehiclesXml As XElement = XElement.Load(reader)
            'Dim MyVehiclesXml As XElement = XElement.Load("VehicleData/VehicleData.xml")
            Dim myvehiclesquery = From myVehicleInfo In MyVehiclesXml...<Vehicle> _
                           Select VehicleVIN = myVehicleInfo.<VehicleVIN>.Value, VehicleYear = myVehicleInfo.<ModelYear>.Value, _
                            VehicleNCAPModelID = myVehicleInfo.<VehicleNCAPModelID>.Value, _
                            VehicleMake = myVehicleInfo.<Make>.Value, VehicleModel = myVehicleInfo.<Model>.Value
    
            _readstream.Dispose()
    
            Dim vehiclecnt() As Vehicle
    
            For Each myvehiclescan In myvehiclesquery
                ' load vehicle data for stored values
                ReDim Preserve vehiclecnt(count)
                If myvehiclescan.VehicleVIN = vehVIN Then
                    i1.VehicleUID = "TESTVEH1"
                    i1.VehicleVIN = vehVIN
                    i1.ModelYear = txtMakeYear.Text
                    i1.Make = txtMakeName.Text
                    i1.Model = txtModelName.Text
                    i1.VehicleNCAPModelID = cmbNCAPID.SelectedValue
                Else
                    '' Establish Values for Vehicle
                    i1.VehicleUID = "TESTVEH2"
                    i1.VehicleVIN = myvehiclescan.VehicleVIN
                    i1.ModelYear = myvehiclescan.VehicleYear
                    i1.Make = myvehiclescan.VehicleMake
                    i1.Model = myvehiclescan.VehicleModel
                    i1.VehicleNCAPModelID = myvehiclescan.VehicleNCAPModelID
                End If
                vehiclecnt(count) = i1
                ' Populate Vehicle Information
                count += 1
                vehiclefile.Vehicles = vehiclecnt
            Next


    M. Frenchik Developer At Large

    Saturday, June 21, 2014 2:07 PM
  • I tracked it all the way through and the Array stays the same until it his the vehicle(count) = i1 and then it overwrites all indexes with the same value ({0}, {1}, etc. end up the same vehicle).   I followed the i1 values through the process and they change.  The issue is that the vehiclecnt does not retain the values previous to the next index I want to write.

    I have checked other examples of code and the vehiclecnt(count) = i1 should put the values on the next set, but it does not and instead writes <count> number of the same into the array.


    M. Frenchik Developer At Large

    Saturday, June 21, 2014 3:53 PM
  • Now closer to the problem .... Actually on each line of the "i1" it is writing over of the previous index values at each index .... Any ideas?

    Then it add the complete one to the next.


    M. Frenchik Developer At Large


    • Edited by GoliathRulz Saturday, June 21, 2014 4:08 PM
    Saturday, June 21, 2014 4:07 PM
  • Try moving the line of code where you create the new Vehicle inside of the for each loop.  This way you are creating a new Vehicle each time instead of reusing one.  You had this orginally

     For Each myvehiclescan In myvehiclesquery
    
    Dim i1 As New Vehicle()

    Saturday, June 21, 2014 5:33 PM
  • Got it solved.

    The line of

                Dim i1 As New Vehicle()

    Needs to be within the loop.  This will cause a new "group" to be created and not write over the existing one(s).

    Full code below for those who want to know:

        Public Async Sub WriteXML(vehVIN As String)
            Dim myvehiclescan As Object
    
            ' Establish XML Structure for New File
            Dim vehiclefile As New VehicleCollection()
            Dim count As Integer = 0
    
    
            ' Obtain Local Copy of Data for Importing into the Application
            Dim _readfolder As StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder
            Dim _readfile As StorageFile = Await _readfolder.GetFileAsync("VehicleData.xml")
            Dim _readstream As Stream = Await _readfile.OpenStreamForReadAsync()
            Dim _readserializer As New System.Xml.Serialization.XmlSerializer(GetType(VehicleCollection))
            Dim reader As New System.IO.StreamReader(_readstream)
    
            Dim MyVehiclesXml As XElement = XElement.Load(reader)
            'Dim MyVehiclesXml As XElement = XElement.Load("VehicleData/VehicleData.xml")
            Dim myvehiclesquery = From myVehicleInfo In MyVehiclesXml...<Vehicle> _
                           Select VehicleVIN = myVehicleInfo.<VehicleVIN>.Value, VehicleYear = myVehicleInfo.<ModelYear>.Value, _
                            VehicleNCAPModelID = myVehicleInfo.<VehicleNCAPModelID>.Value, _
                            VehicleMake = myVehicleInfo.<Make>.Value, VehicleModel = myVehicleInfo.<Model>.Value
    
            _readstream.Dispose()
    
            Dim vehiclecnt() As Vehicle
    
            For Each myvehiclescan In myvehiclesquery
                ' load vehicle data for stored values
                ReDim Preserve vehiclecnt(count)
                Dim i1 As New Vehicle()
                If myvehiclescan.VehicleVIN = vehVIN Then
                    i1.VehicleUID = "TESTVEH1"
                    i1.VehicleVIN = vehVIN
                    i1.ModelYear = txtMakeYear.Text
                    i1.Make = txtMakeName.Text
                    i1.Model = txtModelName.Text
                    i1.VehicleNCAPModelID = cmbNCAPID.SelectedValue
                Else
                    ' Establish Values for Vehicle
                    i1.VehicleUID = "TESTVEH2"
                    i1.VehicleVIN = myvehiclescan.VehicleVIN
                    i1.ModelYear = myvehiclescan.VehicleYear
                    i1.Make = myvehiclescan.VehicleMake
                    i1.Model = myvehiclescan.VehicleModel
                    i1.VehicleNCAPModelID = myvehiclescan.VehicleNCAPModelID
                End If
                vehiclecnt(count) = i1
                count += 1
                ' Populate Vehicle Information
                vehiclefile.Vehicles = vehiclecnt
            Next
    
            Dim folder As StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder
            Dim file As StorageFile = Await folder.CreateFileAsync("VehicleData.xml", CreationCollisionOption.ReplaceExisting)
            Dim stream As Stream = Await file.OpenStreamForWriteAsync()
            Dim serializer As New System.Xml.Serialization.XmlSerializer(GetType(VehicleCollection))
            Dim writer As New System.IO.StreamWriter(stream)
    
            serializer.Serialize(writer, vehiclefile)
            stream.Dispose()
    
            ' Reload Vehicle List Data
            ' Error if no Internet Connection
            Dim connectionProfile = Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile()
            If connectionProfile IsNot Nothing Then
                Dim myvehiclesDataSource = DirectCast(App.Current.Resources("myvehiclesDataSource"), MyVehiclesDataSource)
                Await myvehiclesDataSource.GetMyVehiclesAsync()
            Else
                Dim messageDialog = New Windows.UI.Popups.MessageDialog("An internet connection is needed to download feeds. Please check your connection and restart the app.")
                Dim result = messageDialog.ShowAsync()
            End If
    
        End Sub

    Need to have Class Information setup as such ....

    Public Class VehicleCollection
        Public Vehicles() As Vehicle
    End Class
    
    Public Class Vehicle
        Public VehicleUID As String
        Public VehicleVIN As String
        Public ModelYear As String
        Public Make As String
        Public Model As String
        Public VehicleNCAPModelID As String
    End Class


    M. Frenchik Developer At Large

    • Marked as answer by GoliathRulz Saturday, June 21, 2014 5:41 PM
    Saturday, June 21, 2014 5:41 PM