abwärtskompatibler Serialisierer gesucht

  • VB.NET

Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von Kangaroo.

    abwärtskompatibler Serialisierer gesucht

    Hallo zusammen,

    ich schreibe zurzeit ein größeres Projekt, das diverse Daten speichern und wieder öffnen können muss. Es wird in Zukunft zwangsweise dazu kommen, dass sich die Struktur der zu speichernden Daten/Klassen ändert, bzw. weiterentwickelt. Ich möchte aber in Zukunft auch die "älteren" Daten wieder einlesen können. Also eine Art abwärtskompatibilität gewährleisten.

    Jetzt weiß ich nicht, wie ich die Daten serialisieren soll, so dass die "alten Daten" in die aktuellen Objekte appliziert werden können.
    ...so wie ich das erkläre, versteht das wahrscheinlich kein Mensch ?( Also:

    Ich habe ein Objekt mit einer bestimmten Anzahl von Parametern, bzw. einem Array bestimmter Größe, das ich serialisiere. Wenn ich das Objekt um die ein oder andere Variable erweitere bzw. die Arraygröße ändere und dann versuche eine alte Speicherstand wieder einzulesen, kommt es bei den üblichen Serialisierern zu Problemen, weil ich entweder ein altes Objekt mit der falschen Arraygröße bekomme oder direkt eine Fehlermeldung, dass die falsche Anzahl von Parametern erwartet wurde.

    Kennt jemand einen passenen Serialisierer, einen Trick oder kann mir sagen, wie ich einen eigenen Serialisierer schreiben kann?



    Vielen Dank schonmal...

    mfG, Oli

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „oliverw83“ ()

    Nun, Du scheinst Dich ja intensiv mit der Materie auseinandergesetzt zu haben, insofern weiss ich nicht ob ich da viel Neues zu bieten habe.

    Grundsätzliches:
    Du erwähnst "einen eigenen Serializer" schreiben und/oder verwenden. Was meinst Du damit ? Grundsätzlich hast Du die Wahl zwischen eigentlich 2 (Soap mal aussen vor) unterschiedlichen Arten von Serialisierung:
    • XML Serialisierung: setzt auf IXMLSERIALIZABLE Interface auf, nur Public fields, langsam,editierbar,relativ versionstolerant,plattformunabhängig
    • Binary Serialisierung: setzt auf ISERIALIZABLE Interface auf, Private & Public fields, schneller,nicht editierbar,wenig versionstolerant,plattformabhängig
    Dabei haben die beiden Interfaces nichts miteinander zu tun, IXMLSerializable ist also kein Derivat von ISerializable. Ich hab geschrieben Binary Serialisation sei schneller als die von XML, ist aber immer noch relativ langsam und speicheraufwändig. Wenn es auf Grösse ankommt geht der Trend jetzt zu Protocol Buffern, insbesondere die von Google implementierte Form.

    Also solltest Du Dich zunächst mal für eine Art Serialisierung entscheiden. Ich gehe mal von Binary aus: dann hast Du wiederum die Wahl wie Du Deine Objekte persistent machst:
    • Default Serialisierung: einfach , relativ wenig versionstolerant wie Du schon bemerkt hast
    • Custom Serialisierung: so versionstolerant wie Du es implementierst, heisst aber auch daß Du bei jedem (kritischen) Objekt das ISerializable Interface implementiern musst
    Die Arbeit , individuelle Implementierungen des ISerializable Interface zu schreiben würde ich mir definitiv nicht machen, man gerät da leicht in die Versuchung die eierlegende-Wollmilchsau zu schaffen. Eine Alternative, die Dir vielleicht ausreicht sind die Erweiterungen die MS in .NET 2.0 geschaffen hat unter dem Begriff "Version tolerant Serialisation".

    Das sind im Wesentlichen eine Reihe von neuen Attributen für das ISerializable Interface, die es erlauben (etwas) flexibler auf fehlende oder überflüssige Daten zu reagieren, insofern einen Teil Deines Problems abdeckt. Ob das auch für Deine fixen Arrays eine Lösung ist kann ich nicht sagen, frage mich aber warum Du a) Arrays statt Collections benutzt und b) noch mit fixer Grösse.

    Zu dem Thema noch einmal ein interessanter Artikel, den mir vor einiger Zeit mal aufgefallen war zum Thema "versionstolerante Serialisierung": generell die Aussage dass Du zwar mit VTS Serialisierungsfehler bzw. Custom Serialization vermeiden kannst, Dir aber dafür Logikfehler einhandelst die evtl noch schwerer zu entdecken sind, hier der Artikel

    Bin gespannt was andere zu diesem Thema an Erfahrung haben, jedenfalls eine interessante Abwechslung zu den sonstigen Fragen hier im Forum.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Kangaroo“ ()

    erstmal vielen Dank für die ganze Reihe an neuen Denkanstößen und Vokabeln. Natürlich versuche ich mein bestes, bevor ich andere mit meinen Problemen belästige. Um so mehr erfreut es micht, das diese Thematik auch von anderen nicht als ausgelutscht betrachtet wird.
    Ich hab mich gerade mal ein bisschen mit dem Herrn Google auseinandergesetzt und mir deine Vorschläge angeguckt. In Sachen Custom Serialisierung hast Du natürlich recht; am liebsten würde ich auch darauf verzichten, mit dem ISerializable Interface alles per Hand machen zu müssen. Weil ich es bisher nicht kannte, habe ich mir tatsächlich schon Gedanken darüber gemacht, wie ich das hinkriege, was das VersionAdded-Property kann. Dann wäre ich also in der Lage, wenigstens neue Parameter hinzuzufügen. Allerdings wächst der Code dann natürlich mit der Zeit ins Unermessliche und deshalb tendiere ich auf den ersten Blick ja zu dem Tip, auf die VTS-Schiene aufzuspringen :thumbup: . Nach meinem ersten Testprogramm habe ich es anscheinend richtig verstanden, dass ich Parameter, die in Zukunft vielleicht entfallen könnten (zur Not also einfach alle) einfach als optional markiere, und es den Kompiler ich Zukunft nicht stört, wenn er mit einer gespeicherten Information nichts anzufangen weiß und sie deshalb einfach hinten runterfallen lässt.

    Ich hab mir gerade eine Testklasse mit einer optionalen Variable geschrieben und per BinaryFormatter serialisiert. Dann in der Klasse die Variable gelöscht, neue hinzugefügt, und die gespeicherten Daten als Objekt dieser neue Klasse deserialisert. Hat super geklappt :thumbsup: . Ich denke ich werde mein Programm ertmal in dieser Richtung weiter verfolgen, bis mir ein Grund einfällt der dagegen sprechen sollte.
    Vor allem weil ich noch lange nicht so weit bin, das Programm hier in der Firma zu launchen. Es würden also noch keine großen Datenmangen verloren gehen, wenn ich demnächst doch noch mal umschwenke.

    Bin also weiterhin für jede Art Erfahrung oder Disskussion offen...

    Auch nochmal vielen Dank für die beiden Links. War echt interessant.


    P.S.: meine Parameter waren eigentlich immer ArrayLists diverser "kleinerer" Objekte oder ganze Klassenobjekte mit Unter- Und Unter-Unter-Objekten. Aber bis gerade eben hatte ich mich auch noch nie mit Collections beschäfftigt :wacko: . Die sind echt nützlich... :D . Werd ich in Zukunft wohl öffters drauf zurückgreifen :thumbsup:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „oliverw83“ () aus folgendem Grund: P.S. hinzu

    Nun, freut mich wenn der Post Dir weiterhelfen konnte, bei neuen Membern weiss man nie wie weit man in die Grundlagen abtauchen muss oder ob das gleich den Gähn-Effekt auslöst.

    Wenn Du noch relativ neu im Bereich .NET bist, hast Du Dich evtl auch noch nicht mit dem Konzept von Generics auseinandergesetzt. Daher mal hier eine kleine aber feine generische Serializer Klasse die Dir vielleicht (oder auch nicht) bei Deinem Projekt weiterhilft:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. '############################################################################################
    2. '######## CLASS: SERIALIZER ######### -----
    3. '############################################################################################
    4. '---
    5. '--- Diese Klasse bietet folgende Serialisierungen an:
    6. '--- Binary Serialisierung/Deserialisierung, Optional mit Kompression
    7. '--- XML Serialisierung/Deserialisierung
    8. '--- Bei Deserialisierung kann eine Default-Instanz angegeben werden, sonst wird NEW genommen
    9. '--- Relative Dateipfade werden akzeptiert
    10. '--- wenn FileNamen nicht existieren werden sie angelegt
    11. ' Source:
    12. ' http://www.vbarchiv.net/tipps/details.php?id=1749
    13. ' http://www.devproconnections.com/article/net-framework2/net-generics.aspx
    14. Imports System.IO
    15. Imports System.IO.Compression
    16. Imports System.Runtime.Serialization.Formatters.Binary
    17. Public Class Serializer
    18. '############################################################
    19. '### METHODS #####
    20. '############################################################
    21. '-------------------------------
    22. '-- BinarySerialize ----
    23. '-------------------------------
    24. Public Shared Sub BinarySerialize(Of T)(ByVal fileName As String, ByVal instance As T, ByVal compression As Boolean)
    25. Dim fullPath As String
    26. Try
    27. fullPath = System.IO.Path.GetFullPath(fileName)
    28. ' directory anlegen
    29. If Not Directory.Exists(Path.GetDirectoryName(fullPath)) Then
    30. Directory.CreateDirectory(Path.GetDirectoryName(fullPath))
    31. End If
    32. ' stream und formatter definieren
    33. Dim fs As Stream = New FileStream(fullPath, FileMode.Create)
    34. Dim bf As New BinaryFormatter
    35. ' compression nur wenn gewünscht
    36. If compression Then fs = New GZipStream(fs, CompressionMode.Compress)
    37. ' die eigentliche serialisierung
    38. bf.Serialize(fs, instance)
    39. fs.Close()
    40. Catch ex As Exception
    41. MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error)
    42. Throw ex
    43. End Try
    44. End Sub
    45. ' overloads: BinarySerialize ohne Compression
    46. Public Shared Sub BinarySerialize(Of T)(ByVal fileName As String, ByVal instance As T)
    47. BinarySerialize(fileName, instance, False)
    48. End Sub
    49. '-------------------------------
    50. '-- BinaryDeserialize ----
    51. '-------------------------------
    52. Public Shared Function BinaryDeserialize(Of T)(ByVal fileName As String, ByVal defaultInstance As T, ByVal compression As Boolean) As T
    53. Dim fullPath As String
    54. Try
    55. fullPath = System.IO.Path.GetFullPath(fileName)
    56. ' directory anlegen, wenn nicht existiert wird die default Instanz geladen
    57. If Not File.Exists(fullPath) Then
    58. BinarySerialize(fileName, defaultInstance, compression)
    59. Return defaultInstance
    60. End If
    61. ' stream und formatter definieren
    62. Dim fs As Stream = New FileStream(fileName, FileMode.Create)
    63. Dim bf As New BinaryFormatter
    64. ' compression falls angegeben
    65. If compression Then fs = New GZipStream(fs, CompressionMode.Decompress)
    66. ' die Deserialisierung
    67. BinaryDeserialize = CType(bf.Deserialize(fs), T)
    68. fs.Close()
    69. Catch ex As Exception
    70. MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error)
    71. Throw ex
    72. End Try
    73. End Function
    74. ' overloads BinaryDeserialize: ohne Compression
    75. Public Shared Function BinaryDeserialize(Of T)(ByVal fileName As String, ByVal defaultInstance As T) As T
    76. Return BinaryDeserialize(Of T)(fileName, defaultInstance, False)
    77. End Function
    78. ' overloads BinaryDeserialize: als Default wird NEW genommen
    79. Public Shared Function BinaryDeserialize(Of T As New)(ByVal fileName As String, ByVal compression As Boolean) As T
    80. Return BinaryDeserialize(Of T)(fileName, New T, compression)
    81. End Function
    82. ' overloads BinaryDeserialize: ohne Compression, NEW als Default
    83. Public Shared Function BinaryDeserialize(Of T As New)(ByVal fileName As String) As T
    84. Return BinaryDeserialize(Of T)(fileName, New T)
    85. End Function
    86. '-------------------------------
    87. '-- XMLSerialize ----
    88. '-------------------------------
    89. Public Shared Sub XMLSerialize(Of T)(ByVal fileName As String, ByVal instance As T)
    90. Dim fullPath As String
    91. Try
    92. fullPath = System.IO.Path.GetFullPath(fileName)
    93. ' directory anlegen
    94. If Not Directory.Exists(Path.GetDirectoryName(fullPath)) Then
    95. Directory.CreateDirectory(Path.GetDirectoryName(fullPath))
    96. End If
    97. ' stream und serializer definieren
    98. Dim xs As New System.Xml.Serialization.XmlSerializer(GetType(T))
    99. Dim fs As Stream = New FileStream(fullPath, FileMode.Create)
    100. ' serialisieren
    101. xs.Serialize(fs, instance)
    102. fs.Close()
    103. Catch ex As Exception
    104. MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error)
    105. Throw ex
    106. End Try
    107. End Sub
    108. '-------------------------------
    109. '-- XMLDeserialize ----
    110. '-------------------------------
    111. Public Shared Function XMLDeserialize(Of T)(ByVal fileName As String, ByVal defaultInstance As T) As T
    112. Dim fullPath As String
    113. Try
    114. fullPath = System.IO.Path.GetFullPath(fileName)
    115. ' directory anlegen
    116. If Not File.Exists(fullPath) Then
    117. XMLSerialize(fileName, defaultInstance)
    118. Return defaultInstance
    119. End If
    120. ' stream und serializer definieren
    121. Dim fs As Stream = New FileStream(fileName, FileMode.OpenOrCreate)
    122. Dim xs As New System.Xml.Serialization.XmlSerializer(GetType(T))
    123. ' deserialisieren
    124. XMLDeserialize = CType(xs.Deserialize(fs), T)
    125. fs.Close()
    126. Catch ex As Exception
    127. MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error)
    128. Throw ex
    129. End Try
    130. End Function
    131. ' overloads XMLDeserialize: NEW als Default
    132. Public Shared Function xmlDeserialize(Of T As New)(ByVal fileName As String) As T
    133. Return xmlDeserialize(Of T)(fileName, New T)
    134. End Function
    135. End Class


    Die versieht schon seit einiger Zeit brav ihren Dienst in meiner Standard-Library und serialisiert so ziemlich alles was die entsprechenden Voraussetzungen erfüllt. Vorsicht natürlich immer bei Laufzeit-kritischen Anwendungen.

    Ich wollte hier schon seit ein paar Tagen ein Mini-Tutorial zum Thema Serialisierung schreiben, die Serializer-Klasse war dafür als praktische Umsetzung gedacht.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Kangaroo“ ()