Десериализовать список объектов из файла для отображения

По сути, я создал файл, в котором храню сериализованные данные из объекта с именем TravelRecord. У меня нет проблем с сериализацией данных и сохранением их в файле с помощью BinaryFormatter(), и у меня нет проблем с получением первой записи. Я просто хочу получить все записи и отобразить их в ListBox.

Вот некоторые из соответствующих кодов:

     Private reader As New BinaryFormatter()
     Private input As FileStream
     input = New FileStream(fileName, FileMode.Open, FileAccess.Read)

     Dim record = CType(reader.Deserialize(input), TravelRecord)
     RequestLst.items.Add(record.toString())

Это работает для возврата первой записи, но я хочу, чтобы она возвращала их все, чтобы я мог перебирать их, как показано ниже:

 For Each r As TravelRecord In records
            RequestLst.Items.Add(r.ToString())
        Next

Я знаю, что, вероятно, упускаю что-то очень простое, но все, что я пытаюсь сделать, вызывает ошибки RunTime. Также может быть лучший способ справиться с этим.

Вот как выглядят сериализованные данные в файле.

  ÿÿÿÿ          DTravelRequest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null   TravelRequest.TravelRecord   ID FirstNameLastNamePurpose    StartDateEndDateAmount   System.Guid

   ýÿÿÿSystem.Guid   _a_b_c_d_e_f_g_h_i_j_k           “ŸPªO½cùømFÚw   Steve   Robinson   Semianr  U¾Ó €æ©PÓ     @o@ @_@    ÿÿÿÿ          DTravelRequest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null   TravelRequest.TravelRecord   ID    FirstNameLastNamePurpose    StartDateEndDateAmount   System.Guid

   ýÿÿÿSystem.Guid   _a_b_c_d_e_f_g_h_i_j_k           æ;o˜ùéAÎÜ^pñÌ   jake      Kirkbride   Stuff  l«™  Ó €CøX,Ô     À^@    ÿÿÿÿ          DTravelRequest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null   TravelRequest.TravelRecord   ID   FirstNameLastNamePurpose    StartDateEndDateAmount   System.Guid

   ýÿÿÿSystem.Guid   _a_b_c_d_e_f_g_h_i_j_k           FP“¼Î«Gž¥5tuUš   steve    stevenson   vacation @áw%Ó @ˆ!?(Ó     @_@    ÿÿÿÿ          DTravelRequest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null   TravelRequest.TravelRecord   ID  FirstNameLastNamePurpose    StartDateEndDateAmount   System.Guid

   ýÿÿÿSystem.Guid   _a_b_c_d_e_f_g_h_i_j_k           ‹MÆoh#D–âÑQþ…P   Steve    Stevenson   Program €œN4)Ô €CøX,Ô     @_@    ÿÿÿÿ          DTravelRequest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null   TravelRequest.TravelRecord   ID  FirstNameLastNamePurpose    StartDateEndDateAmount   System.Guid

   ýÿÿÿSystem.Guid   _a_b_c_d_e_f_g_h_i_j_k           k$ˆµ§FŠm§Íüâ   George    Govern   Stuff €CøX,Ô €ê¡}/Ô      `@

Кажется, моя проблема заключается в том, как мне прочитать все это и превратить их в коллекцию, чтобы я мог с ними работать.

Вот код для класса TravelRecord:

    <Serializable()>
    Public Class TravelRecord

    'Class fields
    Public ID As Guid
    Public FirstName As String
    Public LastName As String
    Public Purpose As String
    Public StartDate As Date
    Public EndDate As Date
    Public Amount As Double

    Public Sub New()
        MyClass.New(Guid.Empty, String.Empty, String.Empty, String.Empty,
                    Nothing, Nothing, 0.0)
    End Sub

    Public Sub New(id As Guid, fName As String, lName As String,
                   purpose As String, startDate As Date, endDate As Date,
                   amount As Double)
        MyClass.ID = id
        MyClass.FirstName = fName
        MyClass.LastName = lName
        MyClass.Purpose = purpose
        MyClass.StartDate = startDate
        MyClass.EndDate = endDate
        MyClass.Amount = amount
    End Sub

    Public Overrides Function ToString() As String
        Return String.Format("{0,-40}{1,-10}{2,-10}{3,-15}{4,12:d}{5,12:d}{6,12:C}", _
            ID, FirstName, LastName, Purpose, StartDate, EndDate, Amount)
    End Function
End Class

Из комментариев:

Код сериализации:

formatter.Serialize(output, travelRequest) 

Где travelRequest — это экземпляр TravelRecord.

Возможно, мне нужно объяснить это лучше, вот и все. Система предназначена для отправки Пользователем TravelRecords. Сериализация настроена на Append новую запись о путешествиях в файл, чтобы она действовала как база данных. (Это домашнее задание, я обычно использую SQL). Десериализация предназначена для чтения всех записей в файле и отображения их в сводном списке ListBox.

Причина, по которой десериализация получает только одну запись TravelRecord, заключается в том, что я не знаю, как получить все записи TravelRecord из файла.

Есть ли способ подсчитать количество элементов в сериализованном файле, чтобы я мог выполнить конечный цикл For или что-то в этом роде?


person JKirkbride    schedule 15.11.2015    source источник
comment
Должен ли вывод быть коллекцией? Если да, то Dim records = CType(reader.Deserialize(input), List(Of TravelRecord))   -  person OneFineDay    schedule 15.11.2015
comment
Когда я пытаюсь это сделать, я получаю следующее необработанное исключение: An unhandled exception of type 'System.InvalidCastException' occurred in TravelRequest.exe Additional information: Unable to cast object of type 'TravelRequest.TravelRecord' to type 'System.Collections.Generic.List1[TravelRequest.TravelRecord]'. `   -  person JKirkbride    schedule 15.11.2015
comment
является ли TravelRecord коллекцией? или он содержит коллекцию? в первом блоке вы относитесь к нему как к одному элементу, во втором — как к списку или коллекции   -  person Ňɏssa Pøngjǣrdenlarp    schedule 16.11.2015
comment
Это не Коллекция. Я получаю доступ к файлу, который содержит 3 экземпляра TravelRecord, и я хочу прочитать их и поместить в коллекцию, чтобы отобразить каждый из них.   -  person JKirkbride    schedule 16.11.2015
comment
Было бы полезнее увидеть класс TravelRecords и/или код сериализации. Похоже, что там 4 элемента, но код десериализации создает только 1 элемент. Если вы установите точку останова и задержите указатель мыши на record, будет ли это выглядеть корректно?   -  person Ňɏssa Pøngjǣrdenlarp    schedule 16.11.2015
comment
formatter.Serialize(output, travelRequest) Код сериализации. Требуется один TravelRequest, который передается из формы. Есть ли лучший способ сериализации данных, чтобы я мог получить к ним доступ как к коллекции позже?   -  person JKirkbride    schedule 16.11.2015
comment
TravelRecord — это один элемент. Что такое TravelRequest? Это должно быть какой-то коллекцией. Я призываю вас изменить общедоступные поля этого класса на свойства.   -  person Ňɏssa Pøngjǣrdenlarp    schedule 16.11.2015
comment
TravelRequest — это просто имя системы/exe, это не другой класс или объект. Упс. Ну, это имя системы, но это также экземпляр TravelRecord в данном случае как travelRequest.   -  person JKirkbride    schedule 16.11.2015
comment
formatter.Serialize(output, travelRequest) travelrequest - это EXE???   -  person Ňɏssa Pøngjǣrdenlarp    schedule 16.11.2015
comment
Dim travelRequest As TravelRecord = New TravelRecord(id, FirstNameBox.Text, LastNameBox.Text, PurposeBox.Text, Convert.ToDateTime(StartDateBox.Text), Convert.ToDateTime(EndDateBox.Text), AmountBox.Text) Это и есть travelRequest.   -  person JKirkbride    schedule 16.11.2015
comment
Странный. Весь ваш код выглядит так, как будто он сериализует и десериализует только один элемент, но файл выглядит так, как будто их больше одного. вот ответ для списка (из T)   -  person Ňɏssa Pøngjǣrdenlarp    schedule 16.11.2015
comment
Также @Plutonix на ваш вопрос выше о размещении точки останова на record, да, это действительно, и это работает просто отлично. Но он возвращает только первый экземпляр TravelRecord, увиденный в файле. Пользователь через форму ввода может ввести любое количество TravelRecords, а затем захотеть увидеть их все в списке на другом экране. Моя проблема в том, что я хочу показать их все, а не только первый в файле.   -  person JKirkbride    schedule 16.11.2015
comment
вы только записываете и читаете один элемент в этот файл (из кода здесь), поэтому я не знаю, что это за другие вещи в файле или как они туда попали. Возможно, это произошло раньше, когда вы настроили его для работы с коллекцией. Я не уверен, делает ли BF File.Truncate для нас или нет.   -  person Ňɏssa Pøngjǣrdenlarp    schedule 16.11.2015
comment
Возможно, мне нужно объяснить это лучше, вот и все. Система предназначена для отправки Пользователем TravelRecords. Сериализация настроена на Append новую запись о путешествиях в файл, чтобы она действовала как база данных. (Это домашнее задание, я обычно использую SQL). Десериализация предназначена для чтения всех записей в файле и отображения их в сводном списке ListBox. Причина, по которой десериализация получает только одну запись TravelRecord, заключается в том, что я не знаю, как получить все записи TravelRecord из файла.   -  person JKirkbride    schedule 16.11.2015
comment
Я бы не подумал использовать FileMode.Append с сериализатором. Он не будет создавать файл произвольного доступа. Код, который у вас есть, не для чтения их всех, а для чтения одного. Поскольку вы записали их по одному, вам придется читать их таким же образом, НАМНОГО проще просто использовать List(of TravelRecord) (как бы вы удалили запись 7 из 9?)   -  person Ňɏssa Pøngjǣrdenlarp    schedule 16.11.2015
comment
Мои руки связаны, так как это были спецификации задания. Есть ли способ подсчитать количество элементов в сериализованном файле, чтобы я мог выполнить конечный цикл For или что-то в этом роде?   -  person JKirkbride    schedule 16.11.2015
comment
Я обновил ваш пост с соответствующими подробностями здесь, чтобы я мог опубликовать ответ... своего рода. Назначение действительно указывает BinaryFormatter с FileMode.Append, по одной записи за раз? Какой бесполезный навык   -  person Ňɏssa Pøngjǣrdenlarp    schedule 16.11.2015
comment
Тип данных в файле сделает проблематичным подсчет количества записей. BF, вероятно, добавляет другие маркеры, которые сбивают его с толку.   -  person Ňɏssa Pøngjǣrdenlarp    schedule 16.11.2015


Ответы (1)


Вы пишете одну запись за раз:

formatter.Serialize(output, travelRequest)

поэтому вам придется читать одну запись за раз. BinaryFomratter хранит данные типа вместе с данными (TravelRecord), поэтому вы не можете просто прочитать их как коллекцию (массив или List(of TravelRecord), потому что нет способа преобразовать один тип в тип коллекции (без некоторых циклов, я уверен, что вы не Вы можете увидеть данные о типе в файле:

TravelRequest, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken...

Файл не будет иметь случайный доступ, поэтому, чтобы получить запись 3 или найти «Foo», вам придется сделать что-то подобное (например, проверить имя пользователя = «Bob» и Exit For). Вместо TravelRecord есть класс NVP, который мне оказался под рукой.

Function GetListofTravelRecords() As List(Of NVP)
    Dim tmpList As New List(Of NVP)
    Dim tmp As NVP

    Using fs As New FileStream("C:\Temp\nvplst.bin", FileMode.Open)
        Dim bf As New BinaryFormatter

        Do Until fs.Position >= fs.Length
            tmp = CType(bf.Deserialize(fs), NVP)
            tmpList.Add(tmp)
        Loop

    End Using
    Return tmpList

End Function

Использование и тест:

Dim mylist = GetListofTravelRecords()

For Each item In mylist
    Console.WriteLine(item.ToString)
Next

Выход:

Зигги (1)
Заки (2)
Зои (3)
Зигги (4)
Заки (5)

У меня там было 11 вещей, и все они вернулись благополучно и в том же порядке.


Обратите внимание, что гораздо проще сериализовать всю коллекцию и прочитать ее как коллекцию .

person Ňɏssa Pøngjǣrdenlarp    schedule 15.11.2015
comment
удаление записи будет практически невозможно - вам придется прочитать их все в коллекцию или массив, удалить ту, которую нужно удалить, а затем записать их все обратно - person Ňɏssa Pøngjǣrdenlarp; 16.11.2015
comment
Спасибо за все время и усилия Plutonix. Я понимаю, что это не идеальная ситуация или хорошее использование BF. После этой недели домашнего задания я надеюсь вернуться к полнофункциональной веб-разработке и оставить BF и сериализацию в покое! - person JKirkbride; 16.11.2015
comment
BF и сериализация в порядке ... это добавление по одному, что глупо - person Ňɏssa Pøngjǣrdenlarp; 16.11.2015