Grundlegendes zur Serialisierung

38

Ich bin Softwareentwickler und nach einer Diskussion mit einigen Kollegen habe ich festgestellt, dass ich die Serialisierung des Konzepts nicht gut verstehe. Wie ich verstehe, ist Serialisierung der Prozess des Konvertierens einer Entität, wie z. B. eines Objekts in OOP, in eine Folge von Bytes, so dass die Entität für den nachfolgenden Zugriff gespeichert oder übertragen werden kann (der Prozess der "Deserialisierung").

Das Problem ist, dass nicht alle Variablen (seien es primitive intObjekte oder zusammengesetzte Objekte) bereits durch eine Folge von Bytes dargestellt werden. (Natürlich, weil sie in Registern, im Speicher, auf der Festplatte usw. gespeichert sind.)

Was macht die Serialisierung zu einem so tiefen Thema? Können wir zum Serialisieren einer Variablen diese Bytes nicht einfach in den Speicher nehmen und in eine Datei schreiben? Welche Feinheiten habe ich übersehen?

ddcz
quelle
21
Serialisierung kann für zusammenhängende Objekte trivial sein . Wenn der Objektwert als Zeigergraph dargestellt wird , werden die Dinge viel kniffliger, insbesondere wenn der Graph Schleifen aufweist.
Chi
1
@chi: Dein erster Satz ist ein wenig irreführend, da Zusammenhänge irrelevant sind. Möglicherweise haben Sie ein Diagramm, das zufällig kontinuierlich im Speicher ist und das Ihnen beim Serialisieren immer noch nicht hilft, da Sie immer noch (a) erkennen müssen, dass es tatsächlich zusammenhängend ist, und (b) die darin enthaltenen Zeiger korrigieren müssen. Ich würde nur den zweiten Teil von dem sagen, was du gesagt hast.
Mehrdad
@Mehrdad Ich bin damit einverstanden, dass mein Kommentar aus den von Ihnen genannten Gründen nicht ganz präzise ist. Vielleicht ist Zeiger-frei / Zeiger-Verwendung eine bessere Unterscheidung (auch wenn sie nicht ganz genau ist)
Chi
7
Sie müssen sich auch um die Darstellung auf der Hardware kümmern. Wenn ich ein int 4 bytesauf meinem PDP-11 serialisiere und dann versuche, dieselben vier Bytes in den Speicher meines Macbooks zu lesen, sind sie nicht dieselbe Nummer (wegen Endianes). Sie müssen also die Daten zu einer Darstellung normalisieren, die Sie entschlüsseln können (dies ist Serialisierung). Wie Sie die Daten serialisieren, hat auch Kompromisse Geschwindigkeit / Flexibilität Mensch / Maschine lesbar.
Martin York
Was ist, wenn Sie Entity Framework mit vielen stark verbundenen Navigationseigenschaften verwenden? In einem Fall möchten Sie möglicherweise eine Navigationseigenschaft serialisieren, in einem anderen Fall belassen Sie sie jedoch auf Null (da Sie das tatsächliche Objekt basierend auf der ID in Ihrem serialisierten übergeordneten Objekt erneut aus der Datenbank laden). Dies ist nur ein Beispiel. Da sind viele.
ErikE

Antworten:

40

Wenn Sie eine komplizierte Datenstruktur haben, ist die Darstellung im Speicher normalerweise über den gesamten Speicher verteilt. (Denken Sie zum Beispiel an einen binären Baum.)

Wenn Sie es dagegen auf die Festplatte schreiben möchten, möchten Sie wahrscheinlich eine Darstellung als (hoffentlich kurze) Folge zusammenhängender Bytes haben. Das ist es, was die Serialisierung für Sie bewirkt.

DW
quelle
27

Das Problem, das ich habe, ist: Sind nicht alle Variablen (seien es Primitive wie int oder zusammengesetzte Objekte) bereits durch eine Folge von Bytes dargestellt? (Natürlich, weil sie in Registern, im Speicher, auf der Festplatte usw. gespeichert sind.)

Was macht die Serialisierung zu einem so tiefen Thema? Können wir zum Serialisieren einer Variablen diese Bytes nicht einfach in den Speicher nehmen und in eine Datei schreiben? Welche Feinheiten habe ich übersehen?

Betrachten Sie ein Objektdiagramm in C mit Knoten, die wie folgt definiert sind:

struct Node {
    struct Node* parent;
    struct Node* someChild;
    struct Node* anotherLink;

    int value;
    char* label;
};

//

struct Node nodes[10] = {0};
nodes[5].parent = nodes[0];
nodes[0].someChild = calloc( 1, sizeof(struct Node) );
nodes[5].anotherLink = nodes[3];
for( size_t i = 3; i < 7; i++ ) {
    nodes[i].anotherLink = calloc( 1, sizeof(struct Node) );
}

Zur Laufzeit würde der gesamte Objektgraph im gesamten NodeSpeicherbereich verstreut sein, und auf denselben Knoten könnte von vielen verschiedenen Knoten aus verwiesen werden.

Sie können den Speicher nicht einfach in eine Datei / einen Stream / eine Festplatte sichern und als serialisiert bezeichnen, da die Zeigerwerte (die Speicheradressen sind) nicht de-serialisiert werden konnten (da diese Speicherorte möglicherweise bereits belegt sind, wenn Sie den Speicherauszug zurückladen in Erinnerung). Ein weiteres Problem beim einfachen Speicherauszug ist, dass Sie am Ende alle Arten irrelevanter Daten und nicht verwendeten Speicherplatzes speichern. Auf x86 verfügt ein Prozess über bis zu 4 GB Speicherplatz, und ein Betriebssystem oder eine MMU hat nur eine allgemeine Vorstellung davon, was Speicher tatsächlich ist Sinnvoll oder nicht (basierend auf den Speicherseiten, die einem Prozess zugewiesen wurden). Notepad.exeDaher erscheint es etwas verschwenderisch , 4 GB unformatierte Bytes auf meiner Festplatte abzulegen, wenn ich eine Textdatei speichern möchte.

Ein weiteres Problem ist die Versionierung: Was passiert, wenn Sie Ihr NodeDiagramm an Tag 1 serialisieren , dann an Tag 2 ein weiteres Feld hinzufügen Node(z. B. einen anderen Zeigerwert oder einen primitiven Wert) und dann an Tag 3 Ihre Datei de-serialisieren Tag 1?

Sie müssen auch andere Dinge berücksichtigen, wie Endianness. Einer der Hauptgründe, warum MacOS- und IBM / Windows / PC-Dateien in den 1980er und 1990er Jahren inkompatibel waren, obwohl sie angeblich mit denselben Programmen (Word, Photoshop usw.) erstellt wurden, waren die Multi-Byte-Ganzzahlwerte für x86 / PC wurden in Little-Endian-Reihenfolge, aber in Big-Endian-Reihenfolge auf dem Mac gespeichert - und die Software wurde nicht mit Blick auf plattformübergreifende Portabilität erstellt. Heutzutage sieht es dank einer verbesserten Entwicklerausbildung und unserer zunehmend heterogenen Computerwelt besser aus.

Dai
quelle
2
Aus Sicherheitsgründen wäre es auch schrecklich, alles im Prozessspeicher abzulegen. In einer Programmnacht sind sowohl 1) einige öffentliche Daten als auch 2) ein Passwort, eine geheime Nonce oder ein privater Schlüssel gespeichert. Bei der Serialisierung von ersteren möchte man keine Informationen über letztere preisgeben.
Chi
8
Ein sehr interessanter Hinweis zu diesem Thema: Warum sind die Microsoft Office-Dateiformate so kompliziert?
Streik am
15

Die knifflig eigentlich schon im Wort beschrieben selbst: „ serielle isierung“.

Die Frage ist im Grunde: Wie kann ich einen beliebig komplexen zusammenhängenden zyklisch gerichteten Graphen beliebig komplexer Objekte als lineare Folge von Bytes darstellen?

Denken Sie darüber nach: Eine lineare Sequenz ähnelt einem entarteten gerichteten Graphen, bei dem jeder Scheitelpunkt genau eine ankommende und abgehende Kante hat (mit Ausnahme des "ersten Scheitelpunkts", der keine ankommende Kante hat, und des "letzten Scheitelpunkts", der keine abgehende Kante hat). . Und ein Byte ist offensichtlich weniger komplex als ein Objekt .

So scheint es sinnvoll, dass wir von einem beliebig komplexen Graphen zu einem viel mehr eingeschränkt „Graph“ gehen (eigentlich nur eine Liste) und von beliebig komplexe Objekte auf einfache Bytes, Informationen werden verloren gehen, wenn wir dies naiv tun und don‘ t Codieren Sie die "fremden" Informationen in irgendeiner Weise. Und genau das leistet die Serialisierung: Sie codiert die komplexen Informationen in ein einfaches lineares Format.

Wenn Sie mit YAML vertraut sind , sehen Sie sich möglicherweise die Anker- und Alias- Funktionen an, mit denen Sie die Vorstellung vertreten können, dass "dasselbe Objekt an verschiedenen Stellen auftreten kann".

ZB wenn Sie folgendes Diagramm haben:

A → B → D
↓       ↑
C ––––––+

Sie können dies in YAML als Liste linearer Pfade wie folgt darstellen:

- [&A A, B, &D D]
- [*A, C, *D]

Sie können es auch als Adjazenzliste oder Adjazenzmatrix oder als Paar darstellen, dessen erstes Element eine Menge von Knoten und dessen zweites Element eine Menge von Knotenpaaren ist. In all diesen Darstellungen müssen Sie jedoch über Folgendes verfügen Eine Möglichkeit, auf vorhandene Knoten, dh auf Zeiger , die in einer Datei oder einem Netzwerk-Stream normalerweise nicht vorhanden sind, vorwärts und rückwärts zu verweisen . Am Ende haben Sie nur noch Bytes.

(Was übrigens bedeutet, dass die oben genannte YAML-Textdatei selbst auch "serialisiert" werden muss, wofür die verschiedenen Zeichenkodierungen und Unicode-Übertragungsformate vorgesehen sind. Es handelt sich nicht ausschließlich um "Serialisierung", sondern nur um Kodierung, da die Textdatei bereits eine serielle Datei ist / lineare Liste von Codepunkten, aber Sie können einige Ähnlichkeiten sehen.)

Jörg W. Mittag
quelle
13

Die anderen Antworten befassen sich bereits mit komplexen Objektgraphen, es sei jedoch darauf hingewiesen, dass die Serialisierung von Primitiven ebenfalls nicht trivial ist.

Berücksichtigen Sie bei der Verwendung von C-Primitivtypnamen Folgendes:

  1. Ich serialisiere a long. Einige Zeit später de-serialisiere ich es, aber ... auf einer anderen Plattform, und jetzt longist es int64_teher als das, was int32_tich gespeichert habe. Daher muss ich entweder sehr vorsichtig mit der genauen Größe jedes von mir gespeicherten Typs sein oder einige Metadaten speichern, die den Typ und die Größe jedes Felds beschreiben.

    Beachten Sie, dass diese andere Plattform nach einer zukünftigen Neukompilierung möglicherweise dieselbe Plattform ist.

  2. Ich serialisiere eine int32_t. Einige Zeit später habe ich es de-serialisiert, aber ... auf einer anderen Plattform, und jetzt ist der Wert beschädigt. Leider habe ich den Wert auf einer Big-Endian-Plattform gespeichert und auf eine Little-Endian-Plattform geladen. Jetzt muss ich eine Konvention für mein Format erstellen oder weitere Metadaten hinzufügen , die die Endianität jeder Datei / jedes Streams / was auch immer beschreiben. Und natürlich tatsächlich die entsprechenden Konvertierungen durchführen.

  3. Ich serialisiere einen String. Diesmal verwendet eine Plattform charUTF-8 und eine wchar_tUTF-16.

Daher würde ich behaupten, dass Serialisierung in angemessener Qualität auch für Grundelemente im zusammenhängenden Speicher nicht trivial ist. Es gibt viele Kodierungsentscheidungen, die Sie entweder dokumentieren oder mit Inline-Metadaten beschreiben müssen.

Objektgraphen erhöhen die Komplexität zusätzlich.

Nutzlos
quelle
6

Es gibt mehrere Aspekte:

Lesbarkeit mit dem gleichen Programm

Ihr Programm hat Ihre Daten irgendwie als Bytes im Speicher abgelegt. Aber es kann willkürlich über verschiedene Register verteilt sein, wobei Zeiger zwischen ihren kleineren Teilen hin und her gehen. . Denken Sie nur an eine verknüpfte Ganzzahlliste. Jedes Listenelement kann an einem völlig anderen Ort gespeichert werden, und alles, was die Liste zusammenhält, sind die Zeiger von einem Element zum nächsten. Wenn Sie diese Daten unverändert übernehmen und versuchen, sie auf einen anderen Computer zu kopieren, auf dem dasselbe Programm ausgeführt wird, treten Probleme auf:

  1. Zuallererst kann das Register, in dem Ihre Daten auf einem Computer gespeichert sind, bereits für etwas völlig anderes auf einem anderen Computer verwendet werden (jemand durchsucht Stack Exchange, und der Browser hat den gesamten Speicher bereits belegt). Also, wenn Sie einfach diese Register überschreiben, auf Wiedersehen Browser. Daher müssten Sie die Zeiger in der Struktur neu anordnen, um sie an die Adressen anzupassen, die Sie auf dem zweiten Computer frei haben. Das gleiche Problem tritt auf, wenn Sie versuchen, die Daten zu einem späteren Zeitpunkt auf demselben Computer erneut zu laden.
  2. Was passiert, wenn eine externe Komponente in Ihre Struktur zeigt oder Ihre Struktur Zeiger auf externe Daten enthält, die Sie nicht übertragen haben? Segfaults überall! Dies würde ein Debugging-Albtraum werden.

Lesbarkeit durch ein anderes Programm

Angenommen, Sie können genau die richtigen Adressen auf einem anderen Computer zuweisen, damit Ihre Daten darin passen. Wenn Ihre Daten von einem separaten Programm auf diesem Computer verarbeitet werden (in einer anderen Sprache), hat dieses Programm möglicherweise ein völlig anderes Grundverständnis für Daten. Angenommen, Sie haben C ++ - Objekte mit Zeigern, aber Ihre Zielsprache unterstützt auf dieser Ebene nicht einmal Zeiger. Wiederum haben Sie keine saubere Möglichkeit, diese Daten im zweiten Programm zu adressieren. Am Ende befinden sich einige Binärdaten im Speicher, aber dann müssen Sie zusätzlichen Code schreiben, der die Daten umschließt und sie irgendwie in etwas übersetzt, mit dem Ihre Zielsprache arbeiten kann. Klingt nach Deserialisierung, nur dass Ihr Ausgangspunkt jetzt ein seltsames Objekt ist, das in Ihrem Hauptspeicher verstreut ist und für verschiedene Ausgangssprachen unterschiedlich ist. anstelle einer Datei mit einer klar definierten Struktur. Das Gleiche gilt natürlich, wenn Sie versuchen, die Binärdatei mit den Zeigern direkt zu interpretieren. Sie müssen Parser für jede mögliche Art und Weise schreiben, in der eine andere Sprache möglicherweise Daten im Speicher darstellt.

Lesbarkeit durch einen Menschen

Zwei der bekanntesten modernen Serialisierungssprachen für die webbasierte Serialisierung (xml, json) sind für den Menschen leicht verständlich. Anstelle eines binären Haufens von Daten ist die tatsächliche Struktur und der Inhalt der Daten auch ohne ein Programm zum Lesen der Daten klar. Dies hat mehrere Vorteile:

  • Einfacheres Debuggen -> Wenn es ein Problem in Ihrer Service-Pipeline gibt, sehen Sie sich einfach die Daten an, die aus einem Service stammen, und prüfen, ob dies sinnvoll ist (als erster Schritt). Sie sehen auch direkt, ob die Daten so aussehen, wie Sie es sich vorgestellt haben, wenn Sie in erster Linie Ihre Exportschnittstelle schreiben.
  • Archivierbarkeit: Wenn Sie Ihre Daten als reine Binärdatei haben und das Programm verlieren, das sie interpretieren soll, verlieren Sie die Daten (oder Sie müssen einige Zeit aufwenden, um tatsächlich etwas darin zu finden). Wenn Ihre serialisierten Daten für den Menschen lesbar sind, können Sie sie problemlos als Archiv verwenden oder Ihren eigenen Importer für ein neues Programm programmieren
  • die deklarative Natur der so serialisierten Daten bedeutet auch, dass sie völlig unabhängig vom Computersystem und seiner Hardware sind; Sie könnten es in einen völlig anders aufgebauten Quantencomputer laden oder eine außerirdische KI mit alternativen Fakten infizieren, damit sie versehentlich in die nächste Sonne fliegt Film)
Frank Hopkins
quelle
Meine Daten befinden sich wahrscheinlich hauptsächlich im Hauptspeicher, nicht in Registern. Wenn meine Daten in Register passen, ist die Serialisierung kaum ein Problem. Ich denke, Sie haben falsch verstanden, was ein Register ist.
David Richerby
In der Tat habe ich den Begriff Register hier zu locker verwendet. Der wichtigste Punkt ist jedoch, dass Ihre Daten möglicherweise Zeiger auf den Adressraum enthalten, um die eigenen Komponenten zu identifizieren oder auf andere Daten zu verweisen. Es spielt keine Rolle, ob es sich um ein physisches Register oder eine virtuelle Adresse im Hauptspeicher handelt.
Frank Hopkins
Nein, Sie haben den Begriff "Register" völlig falsch verwendet. Die Dinge, die Sie als Register bezeichnen, befinden sich in einem völlig anderen Teil der Speicherhierarchie als die tatsächlichen Register.
David Richerby
6

Zusätzlich zu dem, was die anderen Antworten gesagt haben:

Manchmal möchten Sie Dinge serialisieren, die keine reinen Daten sind.

Denken Sie beispielsweise an ein Datei-Handle oder eine Verbindung zu einem Server. Obwohl das Dateihandle oder der Socket ein ist int, ist diese Nummer beim nächsten Ausführen des Programms bedeutungslos. Um Objekte, die Handles für solche Dinge enthalten, ordnungsgemäß neu zu erstellen, müssen Sie Dateien erneut öffnen und Verbindungen neu erstellen und entscheiden, was zu tun ist, wenn dies fehlschlägt.

Heutzutage unterstützen viele Sprachen das Speichern anonymer Funktionen in Objekten, beispielsweise ein onBlah()Handler in Javascript. Dies ist eine Herausforderung, da dieser Code Verweise auf zusätzliche Datenelemente enthalten kann, die wiederum serialisiert werden müssen. (Und dann gibt es das Problem der plattformübergreifenden Serialisierung von Code, das für interpretierte Sprachen offensichtlich einfacher ist.) Auch wenn nur eine Teilmenge der Sprache unterstützt werden kann, kann es sich dennoch als recht nützlich erweisen. Nicht viele Serialisierungsmechanismen versuchen, Code zu serialisieren, sehen aber serialize-javascript .

In Fällen, in denen Sie ein Objekt serialisieren möchten, das jedoch etwas enthält, das von Ihrem Serialisierungsmechanismus nicht unterstützt wird, müssen Sie den Code so umschreiben, dass dies umgeht. Beispielsweise können Sie anstelle anonymer Funktionen Aufzählungen verwenden, wenn eine begrenzte Anzahl von Funktionen möglich ist.

Oft möchten Sie, dass die serialisierten Daten kurz sind.

Wenn Sie Daten über das Netzwerk senden oder sogar auf einer Festplatte speichern, kann es wichtig sein, die Größe klein zu halten. Eine der einfachsten Möglichkeiten, dies zu erreichen, besteht darin, Informationen zu verwerfen, die wiederhergestellt werden können (z. B. das Löschen von Caches, Hash-Tabellen und alternativen Darstellungen derselben Daten).

Natürlich muss der Programmierer manuell auswählen, was gespeichert und was verworfen werden soll, und sicherstellen, dass die Dinge neu erstellt werden, wenn das Objekt neu erstellt wird.

Denken Sie über das Speichern eines Spiels nach. Objekte können viele Zeiger auf Grafikdaten, Sounddaten und andere Objekte enthalten. Das meiste davon kann jedoch aus den Spieledatendateien geladen werden und muss nicht in einer Sicherungsdatei gespeichert werden. Es kann mühsam sein, es zu verwerfen, so dass oftmals kleine Dinge übrig bleiben. Ich habe einige Sicherungsdateien in meiner Zeit verhext und Daten entdeckt, die eindeutig redundant waren, wie z. B. textuelle Artikelbeschreibungen.

Manchmal ist der Speicherplatz nicht wichtig, aber die Lesbarkeit. In diesem Fall können Sie stattdessen ein ASCII-Format (möglicherweise JSON oder XML) verwenden.

Artelius
quelle
3

Definieren wir, was eine Folge von Bytes ist. Eine Folge von Bytes besteht aus einer nicht negativen Ganzzahl, die als Länge bezeichnet wird, und einer beliebigen Funktion / Entsprechung, die eine beliebige Ganzzahl i , die mindestens null und kürzer als die Länge ist , einem Bytewert (einer Ganzzahl von 0 bis 255) zuordnet.

Viele der Objekte, mit denen Sie sich in einem typischen Programm befassen, haben nicht diese Form, da die Objekte tatsächlich aus vielen verschiedenen Speicherzuordnungen bestehen, die sich an verschiedenen Stellen im RAM befinden und durch Millionen von Bytes voneinander getrennt sein können ist mir egal. Stellen Sie sich eine einfache verknüpfte Liste vor: Jeder Knoten in der Liste besteht aus einer Folge von Bytes, aber die Knoten befinden sich an vielen verschiedenen Stellen im Arbeitsspeicher Ihres Computers und sind mit Zeigern verbunden. Oder stellen Sie sich eine einfache Struktur vor, die einen Zeiger auf eine Zeichenfolge variabler Länge enthält.

Der Grund, warum wir Datenstrukturen in eine Folge von Bytes serialisieren möchten, liegt normalerweise darin, dass wir sie auf der Festplatte speichern oder an ein anderes System senden möchten (z. B. über das Netzwerk). Wenn Sie versuchen, einen Zeiger auf der Festplatte zu speichern oder an ein anderes System zu senden, ist dies ziemlich unbrauchbar, da das Programm, das diesen Zeiger liest, über einen anderen Satz von verfügbaren Speicherbereichen verfügt.

David Grayson
quelle
1
Ich bin mir nicht sicher, ob das eine gute Definition einer Sequenz ist. Die meisten Leute würden eine Sequenz als eine Sequenz definieren: eine Reihe von Dingen nach der anderen. Nach Ihrer Definition int seq(int i) { if (0 <= i < length) return i+1; else return -1;}ist eine Sequenz. Wie kann ich das auf der Festplatte speichern?
David Richerby
1
Wenn die Länge 4 ist, speichere ich eine 4-Byte-Datei mit folgenden Inhalten: 1, 2, 3, 4.
David Grayson
1
@DavidRicherby Seine Definition entspricht "einer Reihe von Dingen nach der anderen", es ist nur eine mathematischere und präzisere Definition als Ihre intuitive Definition. Beachten Sie, dass Ihre Funktion keine Sequenz ist, da Sie für eine Sequenz diese Funktion und eine andere Ganzzahl benötigen, die als Länge bezeichnet wird.
user253751
1
@FreshAir Mein Punkt ist, dass die Sequenz 1, 2, 3, 4, 5 ist. Das, was ich aufgeschrieben habe, ist eine Funktion . Eine Funktion ist keine Sequenz.
David Richerby
1
Eine einfache Möglichkeit, eine Funktion auf die Festplatte zu schreiben, habe ich bereits vorgeschlagen: Speichern Sie für jede mögliche Eingabe die Ausgabe. Ich denke, vielleicht verstehst du es immer noch nicht, aber ich weiß nicht, was ich sagen soll. Wussten Sie, dass es in eingebetteten Systemen üblich ist, teure Funktionen wie sinin eine Nachschlagetabelle zu konvertieren , die eine Folge von Zahlen ist? Wussten Sie, dass Ihre Funktion für die Eingänge, die uns wichtig sind, dieselbe ist wie diese? int seq(n) { int a[] = [1, 2, 3, 4]; return a[n]; } Warum genau sagen Sie, dass meine 4-Byte-Datei eine unzureichende Darstellung ist?
David Grayson
2

Die Feinheiten spiegeln die Feinheiten von Daten und Objekten selbst wider. Diese Objekte können Objekte der realen Welt oder Computerobjekte sein. Die Antwort liegt im Namen. Serialisierung ist die lineare Darstellung mehrdimensionaler Objekte. Es gibt viele andere Probleme als fragmentierten RAM.

Wenn Sie 12 fünfdimensionale Arrays und Programmcode reduzieren können, können Sie durch Serialisierung auch ein gesamtes Computerprogramm (und Daten) zwischen Computern übertragen. Verteilte Computerprotokolle wie RMI / CORBA verwenden die Serialisierung in großem Umfang, um Daten und Programme zu übertragen.

Betrachten Sie Ihre Telefonrechnung. Es kann sich um ein einzelnes Objekt handeln, das aus all Ihren Anrufen (Liste der Zeichenfolgen), dem zu zahlenden Betrag (Ganzzahl) und dem Land besteht. Oder Ihre Telefonrechnung kann von oben nach unten verwechselt werden und aus diskreten Einzelanrufen bestehen, die mit Ihrem Namen verknüpft sind. Jede Abflachung sieht anders aus und spiegelt wider, wie Ihre Telefongesellschaft diese Version der Software geschrieben hat und warum objektorientierte Datenbanken nie veröffentlicht wurden.

Einige Teile einer Struktur befinden sich möglicherweise überhaupt nicht im Speicher. Wenn Sie einen langsamen Cache haben, werden einige Teile eines Objekts möglicherweise nur auf eine Festplattendatei referenziert und nur geladen, wenn auf diesen Teil des bestimmten Objekts zugegriffen wird. Dies ist häufig bei schwerwiegenden Persistenz-Frameworks der Fall. BLOBs sind ein gutes Beispiel. Getty Images speichert möglicherweise ein riesiges Multi-Megabyte-Bild von Fidel Castro sowie einige Metadaten wie den Namen des Bildes, die Mietkosten und das Bild selbst. Vielleicht möchten Sie das 200-MB-Bild nicht jedes Mal in den Speicher laden, es sei denn, Sie sehen ihn tatsächlich an. Serialisiert würde die gesamte Datei über 200 MB Speicherplatz benötigen.

Einige Objekte können überhaupt nicht serialisiert werden. Im Land der Java-Programmierung können Sie ein Programmierobjekt haben, das den Grafikbildschirm oder eine physikalische serielle Schnittstelle darstellt. Es gibt kein wirkliches Konzept für eine Serialisierung. Wie würden Sie Ihren Port über ein Netzwerk an eine andere Person senden?

Einige Dinge wie Passwörter / Verschlüsselungsschlüssel sollten nicht gespeichert oder übertragen werden. Sie können als solche gekennzeichnet werden (flüchtig / vorübergehend usw.), und der Serialisierungsprozess überspringt sie, aber sie können im RAM leben. Wenn Sie diese Tags weglassen, werden die Verschlüsselungsschlüssel versehentlich in reinem ASCII gesendet / gespeichert.

Dies und die anderen Antworten sind der Grund, warum es kompliziert ist.

Paul Uszak
quelle
2

Das Problem, das ich habe, ist: Sind nicht alle Variablen (seien es Primitive wie int oder zusammengesetzte Objekte) bereits durch eine Folge von Bytes dargestellt?

Ja, sind Sie. Das Problem hierbei ist das Layout dieser Bytes. Ein einfaches intkann 2, 4 oder 8 Bits lang sein. Es kann im großen oder kleinen Endian sein. Es kann unsigniert, mit 1er-Komplement signiert oder sogar mit einer super-exotischen Bitcodierung wie Negabinary versehen werden.

Wenn Sie die intBinärdatei nur aus dem Speicher sichern und als "serialisiert" bezeichnen, müssen Sie so ziemlich den gesamten Computer, das Betriebssystem und Ihr Programm anschließen, damit sie deserialisierbar sind. Oder zumindest eine genaue Beschreibung.

Was macht die Serialisierung zu einem so tiefen Thema? Können wir zum Serialisieren einer Variablen diese Bytes nicht einfach in den Speicher nehmen und in eine Datei schreiben? Welche Feinheiten habe ich übersehen?

Die Serialisierung eines einfachen Objekts besteht darin, es nach bestimmten Regeln aufzuschreiben. Diese Regeln sind zahlreich und nicht immer offensichtlich. ZB wird ein xs:integerin XML in Base-10 geschrieben. Nicht Basis-16, nicht Basis-9, sondern 10. Es ist keine versteckte Annahme, es ist eine tatsächliche Regel. Und solche Regeln machen Serialisierung zu einer Serialisierung. Weil es so ziemlich keine Regeln für das Bit-Layout Ihres Programms im Speicher gibt .

Das war nur eine Spitze eines Eisbergs. Nehmen wir ein Beispiel aus einer Folge von jenen einfachsten Primitiven: C struct. Das könnte man denken

struct {
short width;
short height;
long count;
}

hat ein definiertes Speicherlayout auf einem bestimmten Computer + Betriebssystem? Nun, das tut es nicht. Abhängig von der aktuellen #pragma packEinstellung füllt der Compiler die Felder auf. In den Standardeinstellungen der 32-Bit-Kompilierung werden beide shortsauf 4 Bytes aufgefüllt, sodass structsich tatsächlich 3 Felder mit 4 Bytes im Speicher befinden. Sie müssen also nicht nur eine shortLänge von 16 Bit angeben , sondern auch eine Ganzzahl, die im negativen 1er-Komplement, im großen oder im kleinen Endian geschrieben ist. Sie müssen auch die Strukturpackungseinstellung notieren, mit der Ihr Programm kompiliert wurde.

Das ist so ziemlich das, worum es bei der Serialisierung geht: Regeln aufstellen und sie einhalten.

Diese Regeln können dann erweitert werden, um noch komplexere Strukturen (wie Listen variabler Länge oder nichtlineare Daten), zusätzliche Funktionen wie Lesbarkeit, Versionierung, Abwärtskompatibilität und Fehlerkorrektur usw. zu akzeptieren. Aber selbst das Aufschreiben einer einzelnen intist für Sie schon kompliziert genug Ich möchte nur sicherstellen, dass Sie es zuverlässig zurücklesen können.

Agent_L
quelle