Smart Mobile Studio
  • News
  • Forums
  • Download
  • Store
  • Showcases
    • Featured demos
    • The Smart Contest 2013, Round 1 – Graphics
  • Documentation
    • Get the book
    • System requirements
    • Prerequisites
    • Getting started
      • Introduction
      • Application architecture
      • The application object
      • Forms and navigation
      • Message dialogs
      • Themes and styles
    • Project types
      • Visual project
      • Game project
      • Console project
    • Layout manager
    • Networking
      • TW3HttpRequest
      • TW3JSONP
      • Loading files
  • About

Tag Archives: JSON

Structures, dealing with name pairs

Posted on 26.09.2015 by Jon Lennart Posted in Developers log

When talking to your REST server or nodeJS service, it can sometimes be overkill to ship a huge and complex wad of JSON or XML over the wire. I noticed that WebSocket transferes can sometimes get a tiny delay if the server is dealing with high payloads. Although we are talking milliseconds here, it’s always good to refactor and make things as small as possible.

The solution I came up with is essentially a dynamically created “record”. Where you would normally write:

type
  TMyData = record
    field1: String;
    field2: TDateTime;
    field3: Integer;
  end;

TW3Structure allows you to do this programatically, both for binary and JSON. Which can be handy when playing around with your own protocols. So with TW3Structure you can do stuff like:

var Structure := TW3JsonStructure.Create(null);
Structure.WriteString('field1','some value');
Structure.WriteDateTime('field1',Now);
Structure.WriteInt('field2',1200);

var JSONData:String;
Structure.SaveToJSON(JSONData);

WebSocket.Send(JSONData);

I also added support for TStream which does the exact same thing, but in true binary form. Perfect for storing simple data in the browser cache or your mobile device’s sandbox.

unit system.namepair;

interface

uses
  System.types,
  System.streams,
  SmartCL.System;

type

  EW3Structure = class(EW3Exception);
  TW3Structure = Class(TObject)
  public
    procedure   WriteString(name:string;value:String;
                const encode:Boolean);overload;
    procedure   WriteInt(name:string;Value:Integer);overload;
    procedure   WriteBool(name:string;value:Boolean);overload;
    procedure   WriteFloat(name:String;value:Float);overload;
    procedure   WriteDateTime(name:String;value:TDateTime);overload;

    procedure   Write(name:String;value:variant);overload;virtual;abstract;

    procedure   Clear;virtual;abstract;

    procedure   SaveToJSON(var Data:String);virtual;abstract;
    procedure   LoadFromJSON(Value:String);virtual;abstract;

    procedure   SaveToStream(Stream:TStream);virtual;abstract;
    procedure   LoadFromStream(Stream:TStream);virtual;abstract;
  end;

  TW3JsonStructure = Class(TW3Structure)
  private
    FBuffer:  Variant;
  public
    procedure   Clear;override;
    procedure   SaveToJson(var Data:String);override;
    procedure   LoadFromJson(Value:String);override;
    procedure   SaveToStream(Stream:TStream);override;
    procedure   LoadFromStream(Stream:TStream);override;

    procedure   Write(name:String;value:variant);overload;override;
    Constructor Create(const Data:Variant);virtual;
  end;


  TW3BinProxyItem = Record
    daName:   String;
    daValue:  Variant;
  end;

  TW3BinaryStructure = Class(TW3Structure)
  private
    FData:      Array of TW3BinProxyItem;
    function    IndexOfName(name:String):Integer;
  protected
    procedure   Clear;override;
    procedure   SaveToJson(var Data:String);override;
    procedure   LoadFromJson(Value:String);override;
    procedure   SaveToStream(Stream:TStream);override;
    procedure   LoadFromStream(Stream:TStream);override;
    procedure   Write(name:String;value:variant);overload;override;
  end;


implementation

resourcestring

CNT_ERR_STRUCTURE_FUNCTION  =
'Stream storage failed, structure contains function reference error';

CNT_ERR_STRUCTURE_SYMBOL  =
'Stream storage failed, structure contains symbol reference error';

CNT_ERR_STRUCTURE_UNKNOWN =
'Stream storage failed, structure contains uknown data-type error';

CNT_ERR_STRUCTURE_FAILEDREAD =
'Failed to read structure, invalid datatype error';

CNT_ERR_STRUCTURE_INVALIDSIGN =
'Failed to read structure, invalid data signature error';

//############################################################################
// TW3BinaryStructure
//###########################################################################

const
CNT_STRUCT_SIGNATURE  = $CCCCFF00;

function TW3BinaryStructure.IndexOfName(name:String):Integer;
var
  x:  Integer;
begin
  result:=-1;
  if FData.Count>0 then
  for x:=FData.low to FData.high do
  begin
    if Sametext(Fdata[x].daName,Name) then
    begin
      result:=x;
      break;
    end;
  end;
end;

procedure TW3BinaryStructure.Write(name:String;value:variant);
var
  mIndex: Integer;
  mItem:  TW3BinProxyItem;
Begin
  mIndex:=IndexOfName(name);
  if mIndex<0 then
  begin
    mItem.daName:=name;
    mItem.daValue:=Value;
    FData.add(mItem);
  end else
  FData[mIndex].daValue:=Value;
end;

procedure TW3BinaryStructure.Clear;
Begin
  FData.Clear;
end;

procedure TW3BinaryStructure.SaveToJson(var Data:String);
Begin
  asm
    @Data = json.stringify( (@self.FData) );
  end;
end;

procedure TW3BinaryStructure.LoadFromJson(Value:String);
Begin
  asm
    (@self.FData) = json.parse(@Value);
  end;
end;

procedure TW3BinaryStructure.SaveToStream(Stream:TStream);
var
  x:  Integer;
  mWriter:  TWriter;
Begin
  mWriter:=TWriter.Create(Stream);
  try
    mWriter.WriteInteger(CNT_STRUCT_SIGNATURE);
    mWriter.WriteInteger(FData.Count);
    if FData.Count>0 then
    begin
      for x:=0 to FData.Count-1 do
      begin
        /* Write the name */
        mWriter.WriteString(FData[x].daName);

        /* Write the datatype */
        mWriter.WriteInteger(ord(FData[x].daValue.datatype));

        /* Write the value */
        case FData[x].daValue.datatype of
        vdBoolean:
          begin
            mWriter.WriteBoolean(FData[x].daValue);
          end;
        vdInteger:
          begin
            mWriter.WriteInteger(FData[x].daValue);
          end;
        vdFloat:
          begin
            mWriter.WriteDouble(FData[x].daValue);
          end;
        vdString:
          begin
            mWriter.WriteString(FData[x].daValue);
          end;
        vdFunction:
          begin
            Raise EW3Structure.Create(CNT_ERR_STRUCTURE_FUNCTION);
          end;
        vdSymbol:
          begin
            Raise EW3Structure.Create(CNT_ERR_STRUCTURE_SYMBOL);
          end;
        vdUnknown:
          begin
            Raise EW3Structure.Create(CNT_ERR_STRUCTURE_UNKNOWN);
          end;
        end;
      end;
    end;
  finally
    mWriter.free;
  end;
end;

procedure TW3BinaryStructure.LoadFromStream(Stream:TStream);
var
  mCount:   Integer;
  mReader:  TReader;
  mKind:    Integer;
  mRec:     TW3BinProxyItem;
Begin
  Clear;
  mReader:=TReader.Create(Stream);
  try
    if mReader.ReadInteger = CNT_STRUCT_SIGNATURE then
    begin
      mCount:=mReader.ReadInteger;
      while mCount>0 do
      begin
        mRec.daName:=mReader.ReadString;
        mKind:=mReader.ReadInteger;
        case TW3VariantDataType(mKind) of
        vdBoolean:  mRec.daValue:=mReader.ReadBoolean;
        vdInteger:  mRec.daValue:=mReader.ReadInteger;
        vdFloat:    mRec.daValue:=mReader.ReadDouble;
        vdString:   mRec.daValue:=mReader.ReadString;
        else
          Raise EW3Structure.Create(CNT_ERR_STRUCTURE_FAILEDREAD);
        end;

        FData.add(mRec);
        mRec.daName:="";
        mRec.daValue:=null;

        dec(mCount);
      end;
    end else
    Raise EW3Structure.Create(CNT_ERR_STRUCTURE_INVALIDSIGN);
  finally
    mReader.free;
  end;
end;

//############################################################################
// TW3Structure
//###########################################################################

procedure TW3Structure.WriteString(name:string;
          value:String;const encode:Boolean);
begin
  (* base64 encode string to avoid possible recursion
     should someone store another JSON object as a string *)
  if encode then
  Begin
    asm
      @value = btoa(@value);
    end;
  end;
  Write(name,value);
end;

procedure TW3Structure.WriteInt(name:string;Value:Integer);
begin
  Write(name,value);
end;

procedure TW3Structure.WriteBool(name:string;value:Boolean);
begin
  Write(name,value);
end;

procedure TW3Structure.WriteFloat(name:String;value:Float);
begin
  Write(name,value);
end;

procedure TW3Structure.WriteDateTime(name:String;value:TDateTime);
begin
  Write(name,value);
end;

//############################################################################
// TW3JsonStructure
//###########################################################################

Constructor TW3JsonStructure.Create(const Data:Variant);
begin
  inherited Create;
  if not TVariant.IsNull(Data)
  and not Data.IsUnassigned then
  FBuffer:=Data else
  FBuffer:=TVariant.CreateObject;
end;

procedure TW3JsonStructure.SaveToJSON(var Data:String);
begin
  if (FBuffer<>unassigned)
  and (FBuffer<>NULL) then
  Data:=JSON.Stringify(FBuffer) else
  Data:=null;
end;

procedure TW3JsonStructure.LoadFromJSON(Value:String);
begin
  value:=trim(Value);
  if value.length>0 then
  FBuffer:=JSON.Parse(value) else
  FBuffer:=TVariant.CreateObject;
end;

procedure TW3JsonStructure.SaveToStream(Stream:TStream);
var
  mWriter:  TWriter;
  mtext:    String;
begin
  SaveToJSON(mText);
  mWriter:=TWriter.Create(Stream);
  try
    mWriter.writeString(mText);
  finally
    mWriter.free;
  end;
end;

procedure TW3JsonStructure.LoadFromStream(Stream:TStream);
var
  mText:    String;
  mReader:  TReader;
begin
  mReader:=TReader.Create(stream);
  try
    mText:=mReader.ReadString;
  finally
    mReader.free;
  end;
  LoadFromJSON(mtext);
end;

procedure TW3JsonStructure.Clear;
begin
  FBuffer:=TVariant.CreateObject;
end;

procedure TW3JsonStructure.Write(name:String;value:variant);
begin
  FBuffer[name]:=value;
end;

end.
binary code JSON Records Streams Structures WebSocket

Pages

  • About
  • Feature Matrix
  • Forums
  • News
  • Release History
  • Download
  • Showcases
    • The Smart Contest 2013, Round 1 – Graphics
  • Store
  • Documentation
    • Creating your own controls
    • Debugging, exceptions and error handling
    • Differences between Delphi and Smart
    • Get the book
    • Getting started
      • Introduction
      • Local storage, session storage and global storage
      • Application architecture
      • The application object
      • Forms and navigation
      • Message dialogs
      • pmSmart Box Model
      • Themes and styles
    • Layout manager
    • Networking
      • Loading files
      • TW3HttpRequest
      • TW3JSONP
    • Prerequisites
    • Real data, talking to sqLite
    • System requirements
    • Project types
      • Visual project
      • Game project
      • Console project

Archives

  • December 2019
  • December 2018
  • November 2018
  • July 2018
  • June 2018
  • February 2018
  • September 2017
  • April 2017
  • November 2016
  • October 2016
  • September 2016
  • April 2016
  • March 2016
  • January 2016
  • October 2015
  • September 2015
  • July 2015
  • April 2015
  • January 2015
  • December 2014
  • October 2014
  • September 2014
  • August 2014
  • July 2014
  • June 2014
  • March 2014
  • February 2014
  • January 2014
  • December 2013
  • November 2013
  • October 2013
  • August 2013
  • July 2013
  • June 2013
  • May 2013
  • April 2013
  • March 2013
  • February 2013
  • January 2013
  • December 2012
  • November 2012
  • August 2012
  • July 2012
  • June 2012
  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • November 2011
  • October 2011
  • September 2011

Categories

  • Announcements (25)
  • Developers log (119)
  • Documentation (26)
  • News (104)
  • News and articles (16)

WordPress

  • Register
  • Log in
  • WordPress

Subscribe

  • Entries (RSS)
  • Comments (RSS)
© Optimale Systemer AS