#1 2018-05-08 22:33:04

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

How to generate same json as C# does

Hello,

I tried to find as small examples as possible to keep my post short.

I am working on converting a C#.NET project into Delphi. C# project by default uses structures and Newtonsoft.Json DLLs for json serialization/deserialization.
Example C# structure:

    public class _ST_PAYMENT_REQUEST_ORGINAL_DATA
    {
        public UInt32 TransactionAmount;
        public UInt32 LoyaltyAmount;
        public UInt16 NumberOfinstallments;
        public byte[] AuthorizationCode;
        public byte[] rrn;
        public byte[] TransactionDate;
        public byte[] MerchantId;
        public byte TransactionType;
        public byte[] referenceCodeOfTransaction;
    };

C# json generated for this structure:

{  
      "TransactionAmount":0,
      "LoyaltyAmount":0,
      "NumberOfinstallments":0,
      "AuthorizationCode":null,
      "rrn":null,
      "TransactionDate":null,
      "MerchantId":null,
      "TransactionType":0,
      "referenceCodeOfTransaction":null
   }

Delphi record definition for same structure:

  TStPaymentRequestOrginalData = packed record
    TransactionAmount: UInt32;
    LoyaltyAmount: UInt32;
    NumberOfinstallments: UInt16;
    AuthorizationCode: Array of Byte;
    rrn: Array of Byte;
    TransactionDate: Array of Byte;
    MerchantId: Array of Byte;
    TransactionType: Byte;
    referenceCodeOfTransaction: Array of Byte;
  end;

mORMot json for Delphi record:

{  
      "TransactionAmount":0,
      "LoyaltyAmount":0,
      "NumberOfinstallments":0,
      "AuthorizationCode":[],
      "rrn":[],
      "TransactionDate":[],
      "MerchantId":[],
      "TransactionType":0,
      "referenceCodeOfTransaction":[]
   }

These json texts are for communicating with a device and I have to produce same json as C# project. I am having different representation for byte array variables in my mORMot generated json. Moreover, I also have examples that C# project serialize some string variables as "null" (without quotes as in above C# json example) because they are empty (which I suppose is not correct).

My question is: Is there any way other than manually trying to parse and replace mORMot generated json to be identical with C# generated one? I know about "TTextWriter.RegisterCustomJSONSerializerFromText()" but I do not know what to pass as parameter for having null serialization or even if that is possible.

Since there are lots of different structures, I really do not want to do something manual.

I appreciate any help.

Thanks & regards,
Ertan

Last edited by ertank (2018-05-08 22:35:40)

Offline

#2 2018-05-09 08:02:28

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

Re: How to generate same json as C# does

It seems that C# treats everything as Variant, so empty string is null and empty array is null.
Do you need to load that json generated to your records or just create one?
Few ideas:
a) Reformat your Delphi generated json (use StringReplace) so that [] becomes null.
b) Change declaration of your record to use Variant insted of array of Byte and work with it as Variant.
c) Use TDocVariantData instead of records, but then you would also have to treat everything as Variant.

Offline

#3 2018-05-09 08:08:00

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

Re: How to generate same json as C# does

igors233 ideas are good.

But I doubt that there will be any difference between null and and [] for the properties.
Both are acceptable, and will be unserialized just the same, creating a void byte[] array.
Or is the device serialization so dumb that it won't handle the JSON as requested by the standard?

BTW I wonder the reasons why you switch to Delphi, and have some feedback about what should be implemented.

Offline

#4 2018-05-09 09:18:59

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

Re: How to generate same json as C# does

ab wrote:

BTW I wonder the reasons why you switch to Delphi, and have some feedback about what should be implemented.

Actually, C# is a simulator application showing how to manipulate DLL file company provides. Everybody develop its own application and I know Object Pascal way better than C# myself.

Offline

#5 2018-05-09 09:20:03

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

Re: How to generate same json as C# does

igors233 wrote:

It seems that C# treats everything as Variant, so empty string is null and empty array is null.
Do you need to load that json generated to your records or just create one?
Few ideas:
a) Reformat your Delphi generated json (use StringReplace) so that [] becomes null.
b) Change declaration of your record to use Variant insted of array of Byte and work with it as Variant.
c) Use TDocVariantData instead of records, but then you would also have to treat everything as Variant.

igors233, it seems Variant is the way to go in my case. I need to de-serialize replies from device. Need more testing though.

Thanks.

Offline

#6 2018-05-10 07:29:03

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

Re: How to generate same json as C# does

Note that SynCommons will deserialize both [] and null as expected, even if it serializes a void array as [].

Offline

#7 2018-05-15 04:28:07

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

Re: How to generate same json as C# does

I did progress on my work. Now I have just discovered following situation.

C# project has a structure (example below is simplified):

    public class ST_TICKET
    {
        public UInt32 TransactionFlags;
        public UInt32 OptionFlags;
        public UInt16 ZNo;
        public UInt16 FNo;
        public UInt16 EJNo;
        public string szTicketDate;
        public string szTicketTime;

        public ST_TICKET()
        {
            szTicketDate = "";
            szTicketTime = "";
        }
    };

This is serialized and sent to a fiscal device. Then device process and send back information in following json format:

{  
   "TransactionFlags":131074,
   "OptionFlags":7,
   "ZNo":66,
   "FNo":16,
   "EJNo":1,
   "bcdTicketDate":"EAAA",
   "bcdTicketTime":"AFAA",
   "szTicketDate":"180515",
   "szTicketTime":"065643"
}

It is clear that fiscal device included some *additional* fields in returned json. Apparently, json library that is used in C# (Newtonsoft.Json.dll) does not have any problem processing that json with additional fields and it is simply omitting missing fields. As to my checks, there are at least two different places that such a situation happens. Some of them are several structures deep (some structures include several levels of other structures in it).

My question is: Is it possible to tell mORMot omit some missing fields and only process the ones that are present?

I really would like to avoid to prepare copies of record definitions one for sending and another one for receiving.

Thanks & regards,
Ertan

Offline

#8 2018-05-15 06:51:22

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

Re: How to generate same json as C# does

Take a look at soReadIgnoreUnknownFields in 10.1.3.2.4 at online documentation.
Also good alternative for reading external json may be using TDocVariantData instead of records.

Offline

#9 2018-05-15 08:10:42

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

Re: How to generate same json as C# does

I did not know about that option.
These json's are sent and received. It is not just one way.

My case, there is a kind of bug on the fiscal device. Normally it should not include any fields. Those added fields belong to old version of the fiscal device. Company just overlooked it because they do not have any problem with their json library communicating with the device.

Thanks for the info.

Offline

#10 2018-12-28 20:18:02

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

Re: How to generate same json as C# does

igors233 wrote:

Take a look at soReadIgnoreUnknownFields in 10.1.3.2.4 at online documentation.
Also good alternative for reading external json may be using TDocVariantData instead of records.

I have learned from igors233 that there is a way to make de-serialization more flexible.

I have below very simple and small record definition as an example

  TCommand = packed record
    cmd: string;
    name: string;
  end;

Recently, application that uses this record has slightly different json as a response because REST server changed due to company take over. New owner added some additional fields for their own use. These additional fields will not be used by my application in any way. All my json parsing fails now. For above definition new json response is as below.

{"cmd":"905A00000342220100","data":null,"name":"iso cmd"}

This is one small sample record off the application and I have a lot of different records with different additional fields in them. I wanted to set a single option for global JSON de-serialization for that specific project to ignore unknown fields. I could not see such an option myself. "soReadIgnoreUnknownFields" is mentioned only in 10.1.3.2.4 of online documentation. I tried to set something without using RegisterCustomJSONSerializerFromText because I did not want to additionally define text representation of all my record definitions.

initialization

TTextWriter.RegisterCustomJSONSerializerSetOptions(TypeInfo(TCommand), [soReadIgnoreUnknownFields]);

end.

I could not make that work for myself. With above code, parsing still fails for me.

My questions are:
1- Is there any option that I can set globally to ignore unknown fields during de-serialization? (I do very much appreciate such an option myself)
2- If above RegisterCustomJSONSerializerSetOptions is the shortest way to go without additional text representation definition for each record, I appreciate a small code example, please.

Thanks & regards,
Ertan

Offline

Board footer

Powered by FluxBB