AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Binärdaten platzsparend in JSON speichern

Ein Thema von Shark99 · begonnen am 24. Jul 2015 · letzter Beitrag vom 27. Jul 2015
Antwort Antwort
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#1

AW: Binärdaten Platzsparent in JSON speichern

  Alt 24. Jul 2015, 20:08
Nein, du komprimierst deinen RTF-Text wie gewohnt und erhälst eine Byte-Folge (Stream, Array ob Byte, whatever).

Diese Daten jagst du dann durch einen BASE64 Codierer und erhälst einen string.

Dieser string geht dann durch den JSON-Parser
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Shark99

Registriert seit: 16. Mai 2007
403 Beiträge
 
#2

AW: Binärdaten Platzsparent in JSON speichern

  Alt 24. Jul 2015, 20:20
Kann leider nicht in einen Ansistring hineinkomprimieren, weil die Daten im Speicher in einem Array hängen, auf den alle möglichen Funktionen angewendet werden die nur mit WideStrings funktionieren. Wäre entweder viel Gefummel die AnsiString kompatibel zu machen, oder ich müsste einen Wrapper drin haben der bei Zugriff auf das Element aus dem AnsiString einen WideString macht.

Die ZLib komprimiert mir direkt von WideString -> WideString (sind dann aus 60 KBytes -> 14 KBytes, nach Umwandlung auf AnsiString 11 KBytes).

Edit: Wenn ich statt der UTF8 Konvertierung AnsiString() nehme funktioniert es auch und statt 11 sind es dann 9 KBytes.

Bleibt noch die Frage ob Base85 möglich ist. So wie es ausschaut sollte es mit Z85 funktionieren, jedoch nicht mit ASCII85. Finde leider keine Z85 Implementierung in Delphi. ASCII85 hab ich hier gefunden: http://codeverge.com/embarcadero.del...decode/1054817

Geändert von Shark99 (24. Jul 2015 um 21:01 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#3

AW: Binärdaten Platzsparent in JSON speichern

  Alt 25. Jul 2015, 19:06
Wenn es um JSON geht, dann geht es auch immer um Austausch mit einem anderen System. Und da würde ich immer etwas wählen, was sich auf beiden Systemen leicht umsetzen lässt.

BASE64 ist ein Standard und BASE85 ist eher ein Exot.

Und nochmal zum Verständnis:

Man kann nicht in einen AnsiString etwas hineinkomprimieren. Wer so etwas versucht/macht, macht auf jeden Fall etwas falsch und es ist eher ein Zufall, wenn das zuverlässig funktioniert.

Auch einen String kann ich nicht ohne weiters komprimieren, dieser benötigt zunächst die Umwandlung in eine Bytefolge und diese wird komprimiert. Und abhängig vom Encodig kommen da auch unterschiedliche Bytefolgen heraus. Ein Blick in die Quellen von ZLib zeigt dir was ich meine.

Im Übrigen habe ich in der ZLib (XE8) einen Bug entdeckt. So wird dort der CompressionLevel nicht berücksichtigt, wenn man die Routine benutzt, die direkt einen String komprimiert.
(Der String wird dort mit TEncoding.UTF16.GetBytes(); in eine Bytefolge umgewandelt).

Die Bytefolge, die ich von dort wieder erhalten, kann man sich dann mit TNetEncoding.Base64 wieder in eine String-Repräsentation überführen.

Hier mal ein Beispiel, wie man dies umsetzt und im Anhang ein komplettes Test-Projekt, denn gerade die Schnittstellen nach draussen sollten immer getestet werden.
Delphi-Quellcode:
unit Outside.Impl.FooService;

interface

uses
  Inside.Foo,
  Outside.IFooWebService,
  Outside.IFooService,
  System.Threading,
  System.ZLib;

type
  TFooDTO = class
  private
    FData: string;
  public
    /// <summary>
    /// Compressed UTF16-String as BASE64
    /// </summary>
    property Data: string read FData write FData;
  end;

  TFooAssembler = class
  private
    FCompressionLevel: TZCompressionLevel;
  public
    property CompressionLevel: TZCompressionLevel read FCompressionLevel write FCompressionLevel;
    function Convert( AFoo: TFooDTO ): TFoo; overload;
    function Convert( AFoo: TFoo ): TFooDTO; overload;
  end;

  TFooService = class( TInterfacedObject, IFooService )
  private
    FFooWebService: IFooWebService;
    FFooAssembler: TFooAssembler;
    procedure PostFoo( AFoo: TFoo ); overload;
    function PostFoo( AFoo: TFoo; callback: TResultAction ): ITask; overload;
    procedure GetFoo( out AFoo: TFoo ); overload;
    function GetFoo( callback: TObjectResultAction<TFoo> ): ITask; overload;
  public
    constructor Create( AFooWebService: IFooWebService );
    destructor Destroy; override;
  end;

implementation

uses
  REST.Json, REST.JsonReflect,
  System.NetEncoding,
  System.SysUtils;

{ TFooAssembler }

function TFooAssembler.Convert( AFoo: TFoo ): TFooDTO;
var
  LFooDTO: TFooDTO;
  LInBuffer, LOutBuffer: TBytes;
begin
  LFooDTO := TFooDTO.Create;
  try
    // String mit der entsprechenden Kodierung in eine Byte-Folge umwandeln
    LInBuffer := TEncoding.Unicode.GetBytes( AFoo.Data );

    // Die Byte-Folge komprimieren
    ZCompress(
      {inBuffer} LInBuffer,
      {outBuffer} LOutBuffer,
      {level} CompressionLevel );

    // Die komprimierte Byte-Folge in einen BASE64-String kodieren
    LFooDTO.Data := TNetEncoding.Base64.EncodeBytesToString( LOutBuffer );

    Result := LFooDTO;
    LFooDTO := nil;
  finally
    LFooDTO.Free;
  end;
end;

function TFooAssembler.Convert( AFoo: TFooDTO ): TFoo;
var
  LFoo: TFoo;
  LInBuffer, LOutBuffer: TBytes;
begin
  LFoo := TFoo.Create;
  try
    // BASE64 String decodieren in eine Byte-Folge
    LInBuffer := TNetEncoding.Base64.DecodeStringToBytes( AFoo.Data );

    // Byte-Folge entkomprimieren
    ZDecompress(
      {inBuffer} LInBuffer,
      {outBuffer} LOutBuffer );

    // UTF16-String aus der entkomprimierten Byte-Folge lesen
    LFoo.Data := TEncoding.Unicode.GetString( LOutBuffer );

    Result := LFoo;
    LFoo := nil;
  finally
    LFoo.Free;
  end;
end;

{ TFooService }

constructor TFooService.Create( AFooWebService: IFooWebService );
begin
  inherited Create;
  FFooWebService := AFooWebService;
  FFooAssembler := TFooAssembler.Create;
  FFooAssembler.CompressionLevel := TZCompressionLevel.zcMax;
end;

destructor TFooService.Destroy;
begin
  FFooAssembler.Free;
  inherited;
end;

function TFooService.GetFoo( callback: TObjectResultAction<TFoo> ): ITask;
begin
  Result := TTask.Run(
    procedure
    var
      LFoo: TFoo;
      LDispose: Boolean;
    begin
      LDispose := True;
      try
        try
          GetFoo( LFoo );
          callback( LFoo, nil, LDispose );
        except
          on E: Exception do
            callback( nil, E, LDispose );
        end;
      finally
        if LDispose then
          LFoo.Free;
      end;
    end );
end;

procedure TFooService.GetFoo( out AFoo: TFoo );
var
  LFooDTO: TFooDTO;
  LFooStr: string;
begin
  FFooWebService.GetFoo( LFooStr );

  LFooDTO := TJson.JsonToObject<TFooDTO>( LFooStr );
  try
    AFoo := FFooAssembler.Convert( LFooDTO );
  finally
    LFooDTO.Free;
  end;
end;

procedure TFooService.PostFoo( AFoo: TFoo );
var
  LFooDTO: TFooDTO;
  LFooStr: string;
begin
  LFooDTO := FFooAssembler.Convert( AFoo );
  try
    LFooStr := TJson.ObjectToJsonString( LFooDTO );
    FFooWebService.PostFoo( LFooStr );
  finally
    LFooDTO.Free;
  end;
end;

function TFooService.PostFoo( AFoo: TFoo; callback: TResultAction ): ITask;
begin
  Result := TTask.Run(
    procedure
    begin
      try
        PostFoo( AFoo );
        callback( nil );
      except
        on E: Exception do
          callback( E );
      end;
    end );
end;

end.
Hinweis: Das Projekt im Anhang ist ein DUnitX-Testprojekt und geschrieben mit Delphi XE8!
Angehängte Dateien
Dateityp: zip dp_185991.zip (5,9 KB, 14x aufgerufen)
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo (25. Jul 2015 um 19:25 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von BUG
BUG

Registriert seit: 4. Dez 2003
Ort: Cottbus
2.094 Beiträge
 
#4

AW: Binärdaten Platzsparent in JSON speichern

  Alt 25. Jul 2015, 19:49
RTF ist doch Text-basiert
Eventuell kommt man besser, den Text direkt als String zu speichern (mit einer passenden Kodierung).
Ich möchte mich nicht aus dem Fenster lehnen, aber vielleicht ist es sogar vergleichbar klein (wegen kein base64) ... verständlicher wäre es auf jeden Fall.

//EDIT: Steht ja schon im Ausgangspost ... und das base64 2/3 kleiner wäre in #2

Geändert von BUG (25. Jul 2015 um 19:53 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#5

AW: Binärdaten Platzsparent in JSON speichern

  Alt 25. Jul 2015, 20:22
Ja, darum kann man RTF (die zum Rendern benötigte Beschreibung mit Steuersequenzn und Text) auch in einem (Unicode-)String speichern.

Da so ein RTF aber auch sehr groß sein kann, macht es schon Sinn, den vor der Übertragung (z.B. per WebService nach Timbuktu) etwas kleiner zu machen.

Bearbeiten muss ich den auf jeden Fall, denn JSON ist UTF8 basiert und dann könnte die Größe u.U. explodieren (z.B. wenn jedes Zeichen im JSON als uXXXX dargestellt wird, dann hat man eine Vergrößerung der Daten um den Faktor 5)

Darum ist der Weg RTF-String -> TEncoding.Unicode.GetBytes() -> Komprimierung -> BASE64 -> JSON schon genau richtig.

Bei einem Projekt verwende ich genau dieses Verfahren (allerdings handelt es sich um (JPEG-)Bild-Daten, die dann BASE64-kodiert im JSON stehen).
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von Phoenix
Phoenix
(Moderator)

Registriert seit: 25. Jun 2002
Ort: Hausach
7.643 Beiträge
 
#6

AW: Binärdaten Platzsparent in JSON speichern

  Alt 25. Jul 2015, 21:03
Da so ein RTF aber auch sehr groß sein kann, macht es schon Sinn, den vor der Übertragung (z.B. per WebService nach Timbuktu) etwas kleiner zu machen.
Jain. Nicht zwangsläufig. Ein JSON (Text) via Webservice wird in den meisten Fällen eh für den Trip vom Webserver gzipped. Es müsste schon seeehr undankbar zugehen, wenn der abrufende Client kein gzip oder deflate versteht. Von daher ist der Gewinn an Platz hier eher irrelevant.
Sebastian Gingter
Phoenix - 不死鳥, Microsoft MVP, Rettungshundeführer
Über mich: Sebastian Gingter @ Thinktecture Mein Blog: https://gingter.org
  Mit Zitat antworten Zitat
Benutzerbild von BUG
BUG

Registriert seit: 4. Dez 2003
Ort: Cottbus
2.094 Beiträge
 
#7

AW: Binärdaten Platzsparent in JSON speichern

  Alt 25. Jul 2015, 21:28
Bearbeiten muss ich den auf jeden Fall, denn JSON ist UTF8 basiert und dann könnte die Größe u.U. explodieren (z.B. wenn jedes Zeichen im JSON als uXXXX dargestellt wird, dann hat man eine Vergrößerung der Daten um den Faktor 5)
Warte mal ... JSON unterstützt so gut wie alle Unicode-Zeichen nicht-escaped in Strings. Im Schnitt sollte ein Text in UTF-8 nicht größer sein als mit UTF-16 (Widestring).
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#8

AW: Binärdaten Platzsparent in JSON speichern

  Alt 25. Jul 2015, 23:06
Bearbeiten muss ich den auf jeden Fall, denn JSON ist UTF8 basiert und dann könnte die Größe u.U. explodieren (z.B. wenn jedes Zeichen im JSON als uXXXX dargestellt wird, dann hat man eine Vergrößerung der Daten um den Faktor 5)
Warte mal ... JSON unterstützt so gut wie alle Unicode-Zeichen nicht-escaped in Strings. Im Schnitt sollte ein Text in UTF-8 nicht größer sein als mit UTF-16 (Widestring).
Kommt darauf an welche Zeichen kodiert werden müssen, dann ist entweder UTF8 oder UTF16 besser. Bei einer (Komprimierung und) BASE64 Kodierung habe ich aber zuverlässig eine Zeichenmenge die in UTF8 immer mit einem Byte dargestellt werden kann.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Antwort Antwort

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:11 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz