Jedes Programm benötigt wahlfreien Zugriff, dafür gibt es Variablen und Datentypen, für den Transport hingegen wird serialisiert
Definition: Ein abstrakter Datentyp beschreibt nur die Art und Weise, wie der wahlfreie Zugriff erfolgt, währendem Variablen primitiver Datentypen direkt adressierbar sind, Beispiel:
# Integer, primitiver Datentyp my $number = 123; # direkt adressierbar print $number * 2; # abstrakter Datentyp my $addrs = [ { name => 'Puffbohne', vname => 'Horst', ort => 'Erfurt', plz => '99099' }, { name => 'Fettgusche', vname => 'Ernst', ort => 'Gera', plz => '07552', suburb => 'Langer Berg' }, {}, {} ]; # wahlfreier Zugriff über den Array-Index # und Hash-Key print $addrs->[0]{ort};
Das letzte Beispiel zeigt eine Datenstruktur, auf die das Muster Entity/Attribute/Value
im Grunde genommen schon passt, mit der Besonderheit, dass die Entities anonym sind, also lediglich über einen Array-Index adressierbar sind. Ändern wir die Struktur wie folgt:
# Hashreferenz auf eine Datenstruktur my $addrs = { Erfurter => { name => 'Puffbohne', vname => 'Horst', ort => 'Erfurt', plz => '99099' }, Geraer => { name => 'Fettgusche', vname => 'Ernst', ort => 'Gera', plz => '07552', suburb => 'Langer Berg' }, {}, {} }; print $addrs->{Erfurter}{ort};
So hat jeder Eintrag einen eigenen Namen. Für den Transport, beispielsweise via HTTP oder Speichern in einer Datei gibt es mehrere Möglichkeiten:
# ini-Datei [Erfurter] name = Puffbohne vname = Horst ort = Erfurt plz = 99099 [Geraer] name = Fettgusche vname = Ernst ort = Gera plz = 07552 suburb = Langer Berg # xml <addrs> <Erfurter name='Puffbohne' vname='Horst' ort='Erfurt' plz='99099' /> <Geraer name='Fettgusche' vname='Ernst' ort='Gera' plz='07552' suburb='Langer Berg' /> </addrs>
Und selbstverständlich kann die XML-Sequenz auch anders aufgebaut sein oder die EAV-Struktur wird gänzlich anders serialisiert. Auch hierbei haben wir wieder einen schlüssigen Beweis dafür vorliegen, dass eine Datenstruktur nicht etwa infolge eines Datei-Aufbaus bestimmt wird, sondern durch die Art und Weise des wahlfreien Zugriff. Das bedeutet im Umkehrschluss, dass nicht der Serializer über den Aufbau einer Datenstruktur entscheidet sondern der Entwickler. In der Regel ist eine Adressierung direkt über den Entity-Namen zweckmäßiger als über einen namenlosen Index.
Aufgabe: Für einen HTTP-Client zum Dateiupload soll es die Möglichkeit zur Auswahl des Upload-Verzeichnisses geben. Des Weiteren soll der Anwender sofort nach der Auswahl seiner lokalen Dateien sehen, ob sie bereits auf dem Server liegen. Programmiertechnisch gestaltet es sich so, dass der Name des Uploadverzeichnisses(updir
) vom lokalen Dateinamen(tabfile
) getrennt vorliegt. Ergo bietet sich eine Datenstruktur wie folgt an für die Prüfung:
if( DIRMAP[updir][tabfile] ){ // Datei ist Online } else{ // Datei ist hochzuladen }
Bevor wir einen solchen Komfort nutzen können, wäre noch zu klären, wie das Objekt DIRMAP
mit Daten betankt wird und wie diese Daten zur Übertragung in einer AJAX
-Response serialisiert werden können: Am Einfachsten per JSON
was auch offensichtlich am Zweckmäßigsten erscheint. Aber ist das auch wirklich so? Betrachten wir den zu übertragenden Informationsgehalt, werden wir feststellen, dass es ja genügt, einfach eine Liste/Array
mit voll qualifizierten Dateinamen zu übertragen. In diesem Fall wird das JavaScript-Objekt erst nach dem Empfang der Response zusammengebaut.
Eine andere Möglichkeit zum Verpacken der Daten wäre der gute alte HTTP
QUERY_STRING
wie folgt:
dira=datei1.jpg;dira=datei2.jpg;dirb=bild1.jpg;dirb=bild2.jpg ... usw.
Clientseitig wird die übertragene Sequenz deserialisiert und das JS-Objekt erzeugt:
DIRMAP = { dira: { 'datei1.jpg' = true, 'datei2.jpg' = true }, dirb:{ 'bild1.jpg': true, 'bild2.jpg': true } };
In Perl ein Hash-of-Hashes, in PHP ein Array, in JavaScript eine Sammlung von Objekten, EAV ist nichts Neues. Der wahlfreie Zugriff (Random Access) auf die Daten wird über zwei Schlüssel erreicht, stets sind die Werte direkt addressierbar. Ein Vergleich EAV vs. XML ist unsinnig, weil das Eine die Datenstruktur und das Andere die Verpackung ist wobei Letztere auf ganz unterschiedliche Art und Weise erfolgen kann wie dieser Artikel zeigt. Über die Art und Weise der Verpackung für den Transport entscheiden Faktoren, die von der gewählten Datenstruktur unabhängig sind.
Eine EAV-Datenstruktur bytesemantisch zu serialisieren ist extrem einfach und in jeder Programmiersprache umsetzbar. Untenstehend ein Beispiel in Perl:
use bytes; sub eav2str{ my $pkg = shift; my $ref = shift; my $CONTENT = ''; foreach my $ent(keys %{$ref}){ foreach my $att(sort {$b cmp $a} keys %{$ref->{$ent}}){ my $val = $ref->{$ent}{$att} || ''; $CONTENT .= pack("VVV", length $ent, length $att, length $val).$ent.$att.$val; } } return $CONTENT; }
Das c-Äquivalent zur pack-Schablone "V" (Vax, Little Endian) ist der Datentyp uint32_t, d.h., daß beim Serializer die Byteorder zu berücksichtigen ist, weil die Längenangabe mit 4 Bytes kodiert wird. Infolge der Möglichkeit der Umkehrung der Byteorder ist es jedoch kein Problem, sich beim Serialisieren der Offsetangaben auf eine Byteorder festzulegen.
Untenstehend der JavaScript Code für einen Deserializer der die Networkorder (N Schablone, Big Endian) benutzt:
buffer2eav: function(buffer, raw){ let eav = {}; let dv = new DataView(buffer); let offs = 0; while (offs < dv.byteLength ){ let elen = dv.getUint32(offs, false); offs += 4; let alen = dv.getUint32(offs, false); offs += 4; let vlen = dv.getUint32(offs, false); offs += 4; let ent = new StringView(buffer.slice(offs, offs + elen)); offs += elen; let att = new StringView(buffer.slice(offs, offs + alen)); offs += alen; let val = buffer.slice(offs, offs + vlen); offs += vlen; if(eav[ent] == null) eav[ent] = {}; eav[ent][att] = raw != null ? val : new StringView(val); } return eav; }
Und das alles ist selbstverständlich auch recht einfach in jeder anderen Programmiersprache umsetzbar.
Der Vollständigkeit halber: Meine Routingtable, die als Binary vorliegt auf eine EAV-Datenstruktut einlesen
void thaw( map< string, map<string, string> > &eav ){
// mode "rb": lesen in binary mode
FILE *fp = fopen(BINPATH, "rb");
if(fp == NULL){
throw ios_base::failure("Binary not found or error open file");
}
// Puffer für die Längenangaben
uint32_t lens[3];
// Algorithmus: Lese 3 mal jeweils 4 Bytes
while( fread(lens,4,3,fp) ){
// Net-Order zu Vax-Order
int elen = endian( lens[0] );
int alen = endian( lens[1] );
int vlen = endian( lens[2] );
string ent(elen,'0');
string att(alen,'0');
string val(vlen,'0');
fread(&ent[0], elen, 1, fp);
fread(&att[0], alen, 1, fp);
fread(&val[0], vlen, 1, fp);
eav[ent][att] = val;
}
fclose(fp);
}
Datenschutzerklärung: Diese Seite dient rein privaten Zwecken. Auf den für diese Domäne installierten Seiten werden grundsätzlich keine personenbezogenen Daten erhoben. Das Loggen der Zugriffe mit Ihrer Remote Adresse erfolgt beim Provider soweit das technisch erforderlich ist. sos@rolfrost.de.