You are not logged in.
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
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
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
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
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
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
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
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
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
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
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