AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Binärdaten platzsparend in JSON speichern

Ein Thema von Shark99 · begonnen am 24. Jul 2015 · letzter Beitrag vom 27. Jul 2015
Antwort Antwort
Seite 1 von 2  1 2      
Shark99

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

Binärdaten platzsparend in JSON speichern

  Alt 24. Jul 2015, 19:04
Ich habe als Datenformat UTF-8 JSON (kann nicht geändert werden), und stehe nur vor der Aufgabe RTF Texte möglichst platzsparend darin abzuspeichern.

Habe dazu einen Beispiel-RTF-Code genommen der 30 KB als Widestring hat.

Mit der ZLIB komprimiere ich den auf 7 KB runter.

Wenn ich den jedoch an den JSON Parser übergebe werden in der Datei 29 KB daraus. Ich verschwende also CPU Zeit ohne wirklich Platz zu sparen.

Hat jemand eine andere Idee wie ich hier vorgehen soll? Mit einer End-Kompression von 40% wäre ich schon sehr zufrieden. Es handelt sich um Delphi 2009 mit dem SuperObject JSON Parser.

Danke im Voraus!

Geändert von TBx (26. Jul 2015 um 04:46 Uhr) Grund: Titel korrigiert
  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
 
#2

AW: Binärdaten Platzsparent in JSON speichern

  Alt 24. Jul 2015, 19:20
Du musst den Stream in Base64 umwandeln, dann hast du ein 3:4 Verhältnis (3 Bytes RAW => 4 Bytes BASE64) und aus deinen 7KB sollten dann so ca. 9-10KB werden.

Ein kleiner Tip noch am Rande:
Ich erstelle mir immer Klassen, die exakt die Struktur wiederspiegelt, die ich in eine Schnittstelle hineingebe, bzw. von dort engegennehme (ein DataTrasnferObject).

Ich verbiege mir meine Business-Klassen nicht derart, dass die der Schnittstelle folgen.

Soll ein Business-Object nun durch so eine Schnittstelle laufen, gibt es dort einen Assembler, der die Konvertierung vom BO zum DTO und auch wieder zurück vornimmt.
Delphi-Quellcode:
TFooBO = class
  property RTFText: string; // RTF-Text
end;

TFooDTO = class
  property RTFData: string; // RTF komprimiert und als BASE64 kodiert
end;

TFooAssembler = class
  function Convert( AFooBO: TFooBO ): TFooSTO; override;
  function Convert( AFooDTO: TFooSTO ): TFooBO; override;
end;
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 (24. Jul 2015 um 19:34 Uhr)
  Mit Zitat antworten Zitat
Shark99

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

AW: Binärdaten Platzsparent in JSON speichern

  Alt 24. Jul 2015, 19:49
Funktioniert Base64 auch mit Widestrings, oder muss ich nach UTF8 wandeln? Würde auch Base85 gehen?
  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
 
#4

AW: Binärdaten Platzsparent in JSON speichern

  Alt 24. Jul 2015, 19:57
Funktioniert Base64 auch mit Widestrings, oder muss ich nach UTF8 wandeln? Würde auch Base85 gehen?
BASE64 funktioniert ausschließlich mit binären Daten -> eine Folge von Bytes.
Jede Komprimierung funktioniert ausschließlich mit binären Daten -> eine Folge von Bytes.
Jede Verschlüsselung funktioniert ausschließlich mit binären Daten -> eine Folge von Bytes.

Um einen String in BASE64 zu kodieren muss man sich erst die Byte-Folge von dem String holen. Dabei ist es wichtig die Kodierung zu beachten, die zwischen den beiden Systemen vereinbart wurde.

Beachten: JSON ist hier nur das Transport-Medium!
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
 
#5

AW: Binärdaten Platzsparent in JSON speichern

  Alt 24. Jul 2015, 19:58
Soll ich also aus meinem binären Widestring erstmals einen UTF8 AnsiString machen und dann Base64 daraus? Oder gibt es einen besseren weg?
  Mit Zitat antworten Zitat
Shark99

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

AW: Binärdaten Platzsparent in JSON speichern

  Alt 24. Jul 2015, 20:05
Hab mal gemacht, also -> UTF8 -> Base64 und es steigt nur auf 14 KB (statt 29). Bleibt noch die Frage ob Base85 mit Jason möglich ist. Google ist da widersprüchlich.
  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
 
#7

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
 
#8

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
 
#9

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, 13x 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
 
#10

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
Antwort Antwort
Seite 1 von 2  1 2      


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 23:03 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz