Array in Datei Speichern

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von exc-jdbi.

    Array in Datei Speichern

    Guten Morgen,

    für eine Anwendung speichere ich Daten aus einer DataGridView in einer Arraylist. Dies schreibe ich dann in eine Datei.
    Das sieht derzeit wie folgt aus.

    Visual Basic-Quellcode

    1. Public Sub Save(ByVal Filename As String, ByVal array As Object)
    2. Dim fs As FileStream = Nothing
    3. Try
    4. fs = New FileStream(Filename, FileMode.Create, FileAccess.Write)
    5. Dim formatter As New BinaryFormatter()
    6. formatter.Serialize(fs, array)
    7. Catch ex As Exception
    8. Finally
    9. If Not IsNothing(fs) Then fs.Close()
    10. End Try
    11. End Sub


    Dies funktioniert gut und kann jederzeit wieder eingelesen werden und in das DataGridView zurückgeschrieben werden.
    Soweit so gut.
    Nun zu meiner Frage.

    Ich erstelle eine Structure für einen Artikel diese schreibe ich jetzt ebenfalls in eine ArrayList auch das funktioniert.
    Jedoch kann ich diese ArrayList jetzt nicht mehr mit obiger Funktion schreiben.

    Fehlermeldung ist das das Array nicht mehr serialisiert werden kann.

    Visual Basic-Quellcode

    1. Public Structure Artikel
    2. Dim Bezeichnung as String
    3. Dim Artikelnummer as Integer
    4. Dim Bestand as Integer
    5. Dim Preis as Double
    6. End Structure
    7. Public Sub Füllen
    8. Dim A as new Artikel
    9. A.Bezeichnung = DataGridView1.Item(0, DataGridView1.CurrentRow.Index).Value
    10. A.Artikelnummer = DataGridView1.Item(1, DataGridView1.CurrentRow.Index).Value
    11. .
    12. .
    13. .
    14. ArtikelDB.add(A)
    15. Save(Filename,ArtikelDB)
    16. end sub


    Habt Ihr Ideen dazu?
    Idee Nr.1 : Anstatt eine Struct und ne ArrayList (Die Dinger werden noch verwendet?) designst du dir ein Dataset dass du dann ans DGV bindest. Das DataSet kann über deinDataset.LoadFromXml() und deinDataset.SaveToXml() einfachst gelesen und gespeichert werden.

    Idee Nr.2 : Anstatt des langsamen und unflexiblen BinaryFormatters, bindest du dir JSON.NET ins Projekt via NuGet ein und Serialisierst eine List(Of Artikel) mit dem einzeiler JsonConvert.Serialize() in eine .json Datei. Mit JsonConvert.Deserialize() bekommst du deine List(Of Artikel) wieder.
    Fand den BinaryFormatter jetzt auch nicht so verkehrt also nutze den schon ewig aber vielleicht muss ich das Konzept nochmal überdenken.

    Oder ich muss das Array vor dem Save zerpflücken. was ich ja vermeiden wollte.
    Einfacher wäre es sicher mit SQL oder nen DataSet. Aber leider muss ich mit einer alten Software des Kunden kommunzieren. ;(
    Auf dem Selben Weg.

    Ist ne Textdatei (*.csv) mit Semikolon getrennten Werten. Die lese ich in auch in eine ArrayList ein um sie intern weiter zu verarbeiten.

    Die DataGridView hab ich dann mit der ArrayList befüllt. Also jedes Item in der Arraylist ist eine Datagridview.Rows.row

    sorry, dassich das so unverschämt sage: An deim Code ist so gut wie nix richtig.
    Schon die ArrayList sollte man garnet mehr verwenden, die ist ein Relikt von vor 15 jahren.

    Wenn du eine Liste von Artikeln haben willst, dann nimm eine List(Of Artikel). Da weiss der Compiler, welchen Datentyp die Elemente haben, und die Chance ist besser, dass der kram serialisierbar ist.



    (Themawechsel)
    Jo, wenn das hinhaut, seisogut, und mach: Visual Studio - Empfohlene Einstellungen

    Da ist auch eine Anleitung zum Abklemmen des Microsoft.VisualBasic-Namespaces.
    Ebenso ist nämlich mit System.Collections zu verfahren, dann purzelt dir der ArrayList-Schrott garnet mehr unter die Tipp-Finger.

    Weiters hätte ich noch was zum TryCatch zu sagen, und wenn wolle könnte ich dir auch den Ansatz mit typisiertem Dataset nahezubringen versuchen.
    Dann kann man mit Databinding dabei gehen, und da kann einem dann schwindlich werden, wie einfach auf einmal alles werden kann.

    Achso - hier habich sogar ein Tut, wie man csv in ein typDataset einlesen kann: Csv importieren
    Empfind ich gar nicht als unverschämt. Danke erstmal an alle.

    Ja warum nutzt ich ArrayList. Wahrscheinlich wenn man darüber nachdenkt ist die einzige Begründung:
    "Weil hab ich ja schon immer so gemacht!"

    Da denkt man aber im Alltag gar net so drüber nach. Ich wird es jetzt mal ganz anders angehen wie bisher.

    Man soll ja nie aufhören zu lernen. ;)
    Noch ein Hinweis zur "Überlegenheit" des BinaryFormatters gegenüber JSON und XMLFormatter.

    Schaut man sich dieses Stück Code an.

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.IO
    3. Imports System.Runtime.Serialization
    4. Imports System.Runtime.Serialization.Formatters.Binary
    5. Imports System.Xml.Serialization
    6. Imports Newtonsoft.Json
    7. Module Module1
    8. Sub Main(args As String())
    9. Dim lands As New List(Of Land)
    10. lands.Add(New Land("Germany"))
    11. lands.Add(New Land("Austria"))
    12. lands(0).AddAdjacentLand(lands(1))
    13. lands(1).AddAdjacentLand(lands(0))
    14. Dim formatter As IFormatter = New BinaryFormatter()
    15. Using fS As New FileStream("lands.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
    16. formatter.Serialize(fS, lands)
    17. End Using
    18. Dim xml As New XmlSerializer(GetType(Land))
    19. Using fS As New FileStream("lands.xml", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
    20. xml.Serialize(fS, lands)
    21. End Using
    22. Dim json As String = JsonConvert.SerializeObject(lands)
    23. File.WriteAllText("lands.json", json)
    24. End Sub
    25. End Module
    26. <Serializable()>
    27. Public Class Land
    28. Public Property Name As String
    29. Public Property AdjacentLands As List(Of Land) = New List(Of Land)
    30. Public Sub New()
    31. End Sub
    32. Public Sub New(name As String)
    33. Me.Name = name
    34. End Sub
    35. Public Sub AddAdjacentLand(land As Land)
    36. Me.AdjacentLands.Add(land)
    37. End Sub
    38. End Class


    Ein Land wird als Klasse abgebildet. Jedes Land kann Nachbarländer haben, welche ebenfalls vom Typ Land sind.

    Versucht man, dieses Konstrukt mit dem XML-Formatter zu serialisieren, passiert das.



    Das "knallt", da hier eine "Art Endlosschleife" entsteht. Der Typ Land beinhaltet selbst eine Rerferenz auf ein Land. Hier in Form von einer Liste von Land.

    Selbiges gilt, wenn man versucht, das mit Newtonsoft.JSON zu serialisieren.



    Lediglich der BinaryFormatter vermag, dies zu verarbeiten.

    Nur so als Hinweis, falls man mal in die Verlegenheit kommt, und wie ich, tagelang nach einer Lösung sucht.....
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o
    JSON.NET ist sehrwohl dazu in der Lage, man muss es ihm nur sagen:

    C#-Quellcode

    1. List<Land> lands = new List<Land>
    2. {
    3. new Land("Germany"),
    4. new Land("Austria")
    5. };
    6. lands[0].AddAdjacentLand(lands[1]);
    7. lands[1].AddAdjacentLand(lands[0]);
    8. JsonSerializerSettings settings = new JsonSerializerSettings
    9. {
    10. ReferenceLoopHandling = ReferenceLoopHandling.Ignore, //Ignore und Serialize erzeugen in diesem Beispiel denselben JSON String.
    11. PreserveReferencesHandling = PreserveReferencesHandling.Objects
    12. };
    13. string json = JsonConvert.SerializeObject(lands, settings);
    14. List<Land> landsNew = JsonConvert.DeserializeObject<List<Land>>(json, settings); //Settings muss hier nicht gesetzt werden


    Edit:
    Und das JSON dazu ist auch nicht mal so unlesbar:

    JSON-Quellcode

    1. [
    2. {
    3. "$id":"1",
    4. "Name":"Germany",
    5. "AdjacentLands":
    6. [
    7. {
    8. "$id":"2",
    9. "Name":"Austria",
    10. "AdjacentLands":[{"$ref":"1"}]
    11. }
    12. ]
    13. },
    14. {
    15. "$ref":"2"
    16. }
    17. ]

    bzw. Flach:

    JSON-Quellcode

    1. [{"$id":"1","Name":"Germany","AdjacentLands":[{"$id":"2","Name":"Austria","AdjacentLands":[{"$ref":"1"}]}]},{"$ref":"2"}]


    Edit2:
    Für die Deserialisierung müssen die Settings nicht mal gesetzt werden.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „EaranMaleasi“ ()

    Für XML ist es hingegen noch schlimmer.
    Schon die leere Liste kann nicht serialisiert werden und knallt mit obigem Fehler:

    VB.NET-Quellcode

    1. Dim lands As New List(Of Land)
    2. 'lands.Add(New Land("Germany"))
    3. 'lands.Add(New Land("Austria"))
    4. 'lands(0).AddAdjacentLand(lands(1))
    5. 'lands(1).AddAdjacentLand(lands(0))

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @SpaceyX

    Interessanter Code. Ich tippe mal darauf, dass das nicht funktioniert, weil du die gegeneinander Referenzierst.

    So sollte es gehen.

    VB.NET-Quellcode

    1. Dim tmp = New Land("Austria")
    2. lands(0).AddAdjacentLand(tmp)
    3. tmp = New Land("Germany")
    4. lands(1).AddAdjacentLand(tmp)


    EDIT:
    Hab die Serialisierungswerkzeuge von EDR genommen, dann ist auch das Problem
    von RFG gelöst.

    VB.NET-Quellcode

    1. Private Sub SaveXml(Of T)(ByVal obj As T, ByVal fileName As String)
    2. If File.Exists(fileName) Then File.Delete(fileName)
    3. Using fs As New FileStream(fileName, FileMode.Create)
    4. Dim xmlSer As New XmlSerializer(GetType(T))
    5. xmlSer.Serialize(fs, obj)
    6. fs.Flush()
    7. End Using
    8. End Sub
    9. Private Sub LoadXml(Of T)(ByRef obj As T, ByVal fileName As String)
    10. If File.Exists(fileName) Then
    11. Using fs As New FileStream(fileName, FileMode.Open)
    12. Dim xmlSer As New XmlSerializer(GetType(T))
    13. obj = DirectCast(xmlSer.Deserialize(fs), T)
    14. End Using
    15. End If
    16. End Sub



    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „exc-jdbi“ ()

    naja, der Witz an SpaceyX interessantem Code ist aber doch grade, dass die Land-Dinger sich gegenseitig referenzieren.

    Dein Code dürfte failen, weil er zeigt nicht, wie du ühaupt etwas in Lands einfüllst - aber greifst fröhlich drauf zu, also da täte ich dann eine IndexOutOfRangeException erwarten.
    Guten Morgen EDR

    So läuft der Code perfekt.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Module Module1
    2. Public Sub Main()
    3. Dim lands As New List(Of Land) From {New Land("Germany"),
    4. New Land("Austria")}
    5. Dim tmp = New Land("Austria")
    6. lands(0).AddAdjacentLand(tmp)
    7. tmp = New Land("Germany")
    8. lands(1).AddAdjacentLand(tmp)
    9. 'DIESE VARIANTE FAILT
    10. 'Das geht leider nicht mit XML-Serialize
    11. 'lands(0).AddAdjacentLand(lands(1))
    12. 'lands(1).AddAdjacentLand(lands(0))
    13. 'Serialisieren
    14. SaveXml(lands, "lands.xml")
    15. 'Deserialisieren
    16. Dim landres As List(Of Land) = Nothing
    17. LoadXml(landres, "lands.xml")
    18. landres.Add(New Land("Germany"))
    19. Stop
    20. End Sub
    21. Private Sub SaveXml(Of T)(ByVal obj As T, ByVal filename As String)
    22. If File.Exists(filename) Then File.Delete(filename)
    23. Using fs As New FileStream(filename, FileMode.Create)
    24. Dim XmlSer As New XmlSerializer(GetType(T))
    25. XmlSer.Serialize(fs, obj)
    26. fs.Flush()
    27. End Using
    28. End Sub
    29. Private Sub LoadXml(Of T)(ByRef obj As T, ByVal filename As String)
    30. If File.Exists(filename) Then
    31. Using fs As New FileStream(filename, FileMode.Open)
    32. Dim XmlSer As New XmlSerializer(GetType(T))
    33. obj = DirectCast(XmlSer.Deserialize(fs), T)
    34. End Using
    35. End If
    36. End Sub
    37. End Module
    38. <Serializable()>
    39. Public Class Land
    40. Public Property Name As String
    41. Public Property AdjacentLands As List(Of Land)
    42. Public Sub AddAdjacentLand(land As Land)
    43. Me.AdjacentLands.Add(land)
    44. End Sub
    45. Public Sub New()
    46. Me.AdjacentLands = New List(Of Land)
    47. End Sub
    48. Public Sub New(name As String)
    49. Me.New
    50. Me.Name = name
    51. End Sub
    52. End Class


    Ich hab mich jetzt gerade ein bisschen eingelesen in das IXmlSerializable und auch noch anders, um es zu lösen. Funkst jedoch bis jetzt nichts.

    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ ()

    entspricht immer noch nicht spaceyX' Vorgabe:
    Dort ist Land1 in der Liste von Land2, und Land2 ist in der Liste von Land1.
    Und beide Länder sind in lands.
    Bei dir besteht Daten-Redundanz, nämlich es gibt 2 Länder mit demselben Namen: "Austria", und 2 Länder mit Namen: "Germany".

    Als Datenmodell für Staaten wäre das völlig ungeeignet, weil in wirklichkeit gibt es nur ein Germany und nur ein Austria.
    Aber klar - serialisieren tut das.
    Hast recht.

    Ich hab gestern noch nach anderen Möglichkeiten gesucht, mit dem Ziel die so reinzubringen wie bei SpaceyX. Aber auf XML-Basis ist das wohl nicht möglich.

    Schade, hätte es gut brauchen können. Im Notfall gäbe es ja noch Json oder BinarySerialize.