#1 2016-04-27 16:40:45

ertank
Member
Registered: 2016-03-16
Posts: 163

JSON serialization and de-serialization - Delphi 10

Hi,

I have very low to no knowledge of JSON. So, I maybe asking my question wrongly here. Hope it will be understandable.

There is this C# application with code available to me which uses a C# DLL. Application is communicating with a POS device using that DLL.

I am writing a Delphi application which uses that same C# DLL. I see that at some point C# application using JsonConvert.SerializeObject() and JsonConvert.DeserializeObject() functions, when I check the code. Sample lines from C# code is as follows:

        public static UInt32 GMP_StartPairingInit(ref ST_GMP_PAIR pStPair, ref ST_GMP_PAIR_RESP pStPairResp, int TimeoutInMiliseconds = Defines.TIMEOUT_ECHO)
        {
            string szJsonPairing = JsonConvert.SerializeObject(pStPair);
            byte[] szJsonPairing_Out = new byte[Defines.GMP_TICKET_BUFFER];

            UInt32 retcode = Json_GMP_StartPairingInit(szJsonPairing, szJsonPairing_Out, szJsonPairing_Out.Length);
            if (retcode == 0)
            {
                string retJsonString = GMP_Tools.SetEncoding(szJsonPairing_Out);
                pStPairResp = JsonConvert.DeserializeObject<ST_GMP_PAIR_RESP>(retJsonString);
            }
            return retcode;
        }

I do not know how to do that on Delphi. I have no sample data sent/received. I just know that mORMot project has JSON routines. So, I appreciate if someone can help me out.

Record structure to Serialize at Delphi:

    ST_GMP_PAIR : record
      In_ProcOrderNum : array [0..3] of Byte;
      In_ProcDate : array [0..3] of Byte;
      In_ProcTime : array [0..3] of Byte;
      In_DeviceBrand : array [0..16] of Byte;
      In_DeviceModel : array [0..16] of Byte;
      In_DeviceSerialNum : array [0..16] of Byte;
      In_DeviceEcrRegisterNo : array [0..17] of Byte;
    end;

Record structure to DeSerialize at Delphi:

    ST_GMP_PAIR_RESP: record
      Out_ProcOrderNum : array[0..6] of Byte;
      Out_ProcDate : array[0..6] of Byte;
      Out_ProcTime : array[0..6] of Byte;
      Out_DeviceBrand : array[0..16] of Byte;
      Out_DeviceModel : array[0..16] of Byte;
      Out_DeviceSerialNum : array[0..16] of Byte;
      Out_ErrorRespCode : array[0..2] of Byte;
      Out_StatusCode : array[0..2] of Byte;
      Out_VersionNumber : array[0..8] of Byte;
    end;

Thanks.

Last edited by ertank (2016-04-27 16:41:25)

Offline

#2 2016-04-27 17:10:58

igors233
Member
Registered: 2012-09-10
Posts: 234

Re: JSON serialization and de-serialization - Delphi 10

You have to declare records as packed, and then use RecordSaveJSON and RecordLoadJSON to save/restore values.
But you'll have to compare serialized values in C# and one you'll get, and if necessary adjust something.

type
 ST_GMP_PAIR = packed record
   In_ProcOrderNum : array [0..3] of Byte;
   In_ProcDate : array [0..3] of Byte;
   In_ProcTime : array [0..3] of Byte;
   In_DeviceBrand : array [0..16] of Byte;
   In_DeviceModel : array [0..16] of Byte;
   In_DeviceSerialNum : array [0..16] of Byte;
   In_DeviceEcrRegisterNo : array [0..17] of Byte;
 end;

Procedure TestJSON;
var
  s: RawUTF8;
  r1, r2: ST_GMP_PAIR;
begin
  FillChar(r1, SizeOf(r1), 0);
  r1.In_ProcOrderNum[2] := 1;
  r1.In_DeviceBrand[1] := 2;

  s := RecordSaveJSON(r1, TypeInfo(ST_GMP_PAIR));
  RecordLoadJSON(r2, s, TypeInfo(ST_GMP_PAIR));

  if CompareMem(@r1, @r2, SizeOf(r1)) then
    ShowMessage('Test OK');
end;

Offline

#3 2016-04-27 17:16:41

ertank
Member
Registered: 2016-03-16
Posts: 163

Re: JSON serialization and de-serialization - Delphi 10

I've found out that C# application uses "Newtonsoft.Json.7.0.1" for converting Json to and from. Which seems to be a open source .NET framework.

http://www.newtonsoft.com/json

I think this is as far as I can get to the Json details of C# application.

Offline

#4 2016-04-27 18:29:17

ertank
Member
Registered: 2016-03-16
Posts: 163

Re: JSON serialization and de-serialization - Delphi 10

I was wrong. I could get a detail of "szJsonPairing" that is as follows:

C# Code for setting parameters for DLL call:

            ST_GMP_PAIR pairing = new ST_GMP_PAIR();
            pairing.In_DeviceBrand = Encoding.Default.GetBytes("INGENICO");
            pairing.In_DeviceModel = Encoding.Default.GetBytes("IWE280");
            pairing.In_DeviceEcrRegisterNo = Encoding.Default.GetBytes("12344567");
            pairing.In_DeviceSerialNum = Encoding.Default.GetBytes("JHWE20000079");
            pairing.In_ProcOrderNum = parsClass.GMP_AscToBcd("000001");
            Array.Copy(parsClass.GMP_AscToBcd(m_lblProcDate.Text.Substring(0, 2) + m_lblProcDate.Text.Substring(3, 2) + m_lblProcDate.Text.Substring(6, 2)), pairing.In_ProcDate, pairing.In_ProcDate.Length);
            Array.Copy(parsClass.GMP_AscToBcd(m_lblProcTime.Text.Substring(0, 2) + m_lblProcTime.Text.Substring(3, 2) + m_lblProcTime.Text.Substring(6, 2)), pairing.In_ProcTime, pairing.In_ProcTime.Length);

            ST_GMP_PAIR_RESP pairingResp = new ST_GMP_PAIR_RESP();

            resp = Json_GMPSmartDLL.GMP_StartPairingInit(ref pairing, ref pairingResp);

C# function making DLL call itself:

 public static UInt32 GMP_StartPairingInit(ref ST_GMP_PAIR pStPair, ref ST_GMP_PAIR_RESP pStPairResp, int TimeoutInMiliseconds = Defines.TIMEOUT_ECHO)
        {
            string szJsonPairing = JsonConvert.SerializeObject(pStPair);
            byte[] szJsonPairing_Out = new byte[Defines.GMP_TICKET_BUFFER];

            UInt32 retcode = Json_GMP_StartPairingInit(szJsonPairing, szJsonPairing_Out, szJsonPairing_Out.Length);
            if (retcode == 0)
            {
                string retJsonString = GMP_Tools.SetEncoding(szJsonPairing_Out);
                pStPairResp = JsonConvert.DeserializeObject<ST_GMP_PAIR_RESP>(retJsonString);
            }
            return retcode;
        }

Value of "szJsonPairing" after it is Serialized in DLL call function:

{"In_ProcOrderNum":"AAABAAAAAAAAAAAAAAAAAA==","In_ProcDate":"JwQW","In_ProcTime":"IEYg","In_DeviceBrand":"SU5HRU5JQ08=","In_DeviceModel":"SVdFMjgw","In_DeviceSerialNum":"SkhXRTIwMDAwMDc5","In_DeviceEcrRegisterNo":"MTIzNDQ1Njc="}

Since I have the hardware with me and Visual Studio installed some other city, I won't be able to get details of "szJsonPairing_Out" for about 2 weeks I assume.

Offline

#5 2016-04-27 20:06:19

igors233
Member
Registered: 2012-09-10
Posts: 234

Re: JSON serialization and de-serialization - Delphi 10

This seems to be a custom format, I guess your only solution is to rewrite serialization/deseralization code, you can use same code (RecordSave[Load]JSON) and just provide your custom reader/writter with a call to TTextWriter.RegisterCustomJSONSerializer, take a look at "25 - JSON performance", from Samples dir for an example.

Offline

#6 2016-04-28 11:05:28

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,206
Website

Re: JSON serialization and de-serialization - Delphi 10

Or just use "array of byte" instead of "array [0..#] of byte" on Delphi side.
On older version of Delphi, you need to define the record type as text.

Or you may just define not a record, but a TPersistent class, with TByteDynArray as published properties.
Then it would work directly with ObjectToJson/JsonToObject functions.

Offline

#7 2016-04-29 09:31:54

ertank
Member
Registered: 2016-03-16
Posts: 163

Re: JSON serialization and de-serialization - Delphi 10

Thanks for all suggestions. I have one more question. When I use RecordSaveJSON on byte variables. Does it automatically convert it into Base64 string?

Offline

#8 2016-04-29 12:07:48

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,206
Website

Re: JSON serialization and de-serialization - Delphi 10

Before Delphi 2010, there is no extended RTTI for records so you need to supply the info as text.
If you do not supply the info as text, the serialization would take place as binary, using RecordSave(), then they would be transmitted as Base64.

It is all explained in the doc.
http://synopse.info/files/html/Synopse% … ml#TITL_51

Offline

#9 2016-04-29 14:58:05

ertank
Member
Registered: 2016-03-16
Posts: 163

Re: JSON serialization and de-serialization - Delphi 10

I believe, I got it to work now. Code is as below. Tests seems to be working OK. Had to convert everything to AnsiString since that is what DLL requests.

There is this one thing I would like to get some suggestions though. Is there any faster function in mORMot to replace "TNetEncoding.Base64.EncodeBytesToString()" ?

Thanks.



  // DLL import definitions
  procedure GetErrorMessage(ErrorCode: UInt32; Buffer: TBytes); cdecl; external 'GMPSmartDLL.dll';
  function Json_FiscalPrinter_Echo(szEcho_Out: TBytes; EchoLen_Out, TimeoutInMiliseconds:integer): UInt32; cdecl; external 'GMPSmartDLL.dll';
  function Json_GMP_StartPairingInit(szPairing: AnsiString; var szPairingResp: TBytes; PairingRespLen: Integer): Uint32; cdecl; external 'GMPSmartDLL.dll';

type
  TST_GMP_PAIR = packed record
      In_ProcOrderNum : AnsiString;
      In_ProcDate : AnsiString;
      In_ProcTime : AnsiString;
      In_DeviceBrand : AnsiString;
      In_DeviceModel : AnsiString;
      In_DeviceSerialNum : AnsiString;
      In_DeviceEcrRegisterNo : AnsiString;
  end;

function TForm5.AscToBcd(const Value: string): TBytes;
begin
  if Length(Value) > 32 then
  begin
    // because static buffers are the best, we fail like the original code
  end else
  begin
    SetLength(Result, 16);
    HexToBin(PChar(Value), Result, Length(Result));
  end;
end;

procedure TForm5.btnPairClick(Sender: TObject);
var
  i:Integer;
  sent:TST_GMP_PAIR;
  //recv:TST_GMP_PAIR_RESP;
  s,json:AnsiString;
  pResp,b:TBytes;
begin
  // Fill record structure.
  sent.In_ProcOrderNum        := AnsiString(TNetEncoding.Base64.EncodeBytesToString(Copy(AscToBcd('000001'), 0, 16)));
  sent.In_ProcDate            := AnsiString(TNetEncoding.Base64.EncodeBytesToString(Copy(AscToBcd(FormatDateTime('ddmmyy', Now)), 0, 3)));
  sent.In_ProcTime            := AnsiString(TNetEncoding.Base64.EncodeBytesToString(Copy(AscToBcd(FormatDateTime('hhmmss', Now)), 0, 3)));
  sent.In_DeviceBrand         := AnsiString(TNetEncoding.Base64.EncodeBytesToString(TEncoding.ASCII.GetBytes('INGENICO')));
  sent.In_DeviceModel         := AnsiString(TNetEncoding.Base64.EncodeBytesToString(TEncoding.ASCII.GetBytes('IWE280')));
  sent.In_DeviceSerialNum     := AnsiString(TNetEncoding.Base64.EncodeBytesToString(TEncoding.ASCII.GetBytes('JHWE20000079')));
  sent.In_DeviceEcrRegisterNo := AnsiString(TNetEncoding.Base64.EncodeBytesToString(TEncoding.ASCII.GetBytes('12344567')));

  json := AnsiString(RecordSaveJSON(sent, TypeInfo(TST_GMP_PAIR)));
  Memo1.Lines.Add(EmptyStr);
  Memo1.Lines.Add('New Code Produced JSON:');
  Memo1.Lines.Add(string(json));

  SetLength(pResp, 200000);
  i := Json_GMP_StartPairingInit(json, pResp, Length(pResp));
  if i <> 0 then begin
    SetLength(b, 1000);
    GetErrorMessage(i, b);
    s := AnsiString(TEncoding.ASCII.GetString(b));
    Memo1.Lines.Add(EmptyStr);
    Memo1.Lines.Add('ERROR: ' + String(s));
  end else begin
    Memo1.Lines.Add(EmptyStr);
    Memo1.Lines.Add('Pairing: OK.');
  end;
end;

Last edited by ertank (2016-04-29 14:58:26)

Offline

#10 2016-05-01 12:15:36

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,206
Website

Re: JSON serialization and de-serialization - Delphi 10

Try in SynCommons.pas, Base64ToBin / BinToBase64.
You may do some benchmarks, but they are meant to be much faster than TNetEncoding.

But why didn't you customize the record serialization as I proposed above, to avoid Base64 encoding?

Offline

#11 2016-05-01 17:17:27

ertank
Member
Registered: 2016-03-16
Posts: 163

Re: JSON serialization and de-serialization - Delphi 10

I might not understood your proposition. I tried to define my record variables as TBytes and resulting json was not what I have posed in #4 in this thread.

It is quite customized json format DLL requires. Otherwise, I am getting "json format error" as a result from function calls. Some of the variables are first converted to hex before converting them to Base64. Those are mainly numbers as far as I can see.

Offline

Board footer

Powered by FluxBB