You are not logged in.
Pages: 1
Hello,
I tried to read more about how mORMot handles JSON in online documentation. I couldn't find an answer myself. If I have below definitions in my code can I serialize "TStDepartment" class using mORMot json function(s)? Or, Is it better I prepare a record definition of it somehow?
type
TCurrencyType = (
CURRENCY_NONE = 0,
CURRENCY_TL = 949,
CURRENCY_DOLAR = 840,
CURRENCY_EU = 978,
CURRENCY_GPR = 826
);
TItemUnitTypes = (
ITEM_NONE,
ITEM_NUMBER = 1,
ITEM_KILOGRAM = 2,
ITEM_GRAM = 3,
ITEM_LITRE = 4,
// Adetsel Birimler
ITEM_DUZINE = 11,
ITEM_DEMET, // 12
ITEM_KASA, // 13
ITEM_BAG, // 14
// Agirlik Birimler
ITEM_MILIGRAM = 31,
ITEM_TON, // 32
ITEM_ONS, // 33
ITEM_DESIGRAM, // 34
ITEM_SANTIGRAM, // 35
ITEM_POUND, // 36
ITEM_KENTAL, // 37
// Uzunluk Birimler
ITEM_METRE = 51,
ITEM_SANTIMETRE, // 52
ITEM_MILIMETRE, // 53
ITEM_DEKAMETRE, // 54
ITEM_HEKTAMETRE, // 55
ITEM_KILOMETRE, // 56
ITEM_DESIMETRE, // 57
ITEM_MIKRON, // 58
ITEM_INC, // 59
ITEM_FOOT, // 60
ITEM_YARD, // 61
ITEM_MIL, // 62
// Hacim Birimler
ITEM_METREKUP = 71,
ITEM_DESIMETREKUP, // 72
ITEM_SANTIMETREKUP, // 73
ITEM_MILIMETREKUP, // 74
ITEM_DEKALITRE, // 75
ITEM_HEKTOLITRE, // 76
ITEM_KILOLITRE, // 77
ITEM_DESILITRE, // 78
ITEM_SANTILITRE, // 79
ITEM_MILILITRE, // 80
ITEM_INCKUP, // 81
ITEM_GALLON, // 82
ITEM_BUSHEL, // 83
// Alan Birimler
ITEM_METREKARE = 91,
ITEM_DEKAMETREKARE, // 92
ITEM_AR, // 93
ITEM_KILOMETREKARE, // 94
ITEM_DESIMETREKARE, // 95
ITEM_SANTIMETREKARE, // 96
ITEM_MILIMETREKARE, // 97
ITEM_DONUM, // 98
ITEM_HEKTAR, // 99
ITEM_INCKARE // 100
);
TStDepartment = class
private
FDeptName: string;
FU8TaxIndex: Byte;
FCurrencyType: TCurrencyType;
FUnitType: TItemUnitTypes;
FU64Limit: UInt64;
FU64Price: UInt64;
public
constructor Create();
public
property DeptName: string read FDeptName write FDeptName;
property U8TaxIndex: Byte read FU8TaxIndex write FU8TaxIndex;
property CurrencyType: TCurrencyType read FCurrencyType write FCurrencyType;
property UnitType: TItemUnitTypes read FUnitType write FUnitType;
property U64Limit: UInt64 read FU64Limit write FU64Limit;
property U64Price: UInt64 read FU64Price write FU64Price;
end;
{ TStDepartment }
constructor TStDepartment.Create;
begin
FDeptName := '';
end;
Offline
> I tried to read more about how mORMot handles JSON in online documentation. I couldn't find an answer myself.
Start with this few articles, then go to SynCommons and read comments for those functions and also check in samples for examples.
http://blog.synopse.info/post/2011/02/2 … ialization
http://blog.synopse.info/post/2013/01/1 … ialization
http://blog.synopse.info/post/2014/05/1 … anced-RTTI
> If I have below definitions
> in my code can I serialize "TStDepartment" class using mORMot json function(s)?
Yes, that's easy, important thing when serializing classes (unless you write your custom serialization function) is to put properties you want under published section, so in your case it would be:
TStDepartment = class
...
published
property DeptName: string read FDeptName write FDeptName;
property U8TaxIndex: Byte read FU8TaxIndex write FU8TaxIndex;
property CurrencyType: TCurrencyType read FCurrencyType write FCurrencyType;
property UnitType: TItemUnitTypes read FUnitType write FUnitType;
property U64Limit: UInt64 read FU64Limit write FU64Limit;
property U64Price: UInt64 read FU64Price write FU64Price;
end;
and code itself:
var
json: RawUTF8;
dep: TStDepartment;
begin
dep := TStDepartment.Create;
json := SynCommons.ObjectToJSON(dep, [woHumanReadable, woDontStoreDefault]);
ShowMessage(json);
dep.Free;
end;
Depending on what you need you can try various params for ObjectToJson second argument (TTextWriterWriteObjectOption).
> Or, Is it better I prepare a record definition of it somehow?
It all depends on what you need, where you'll use it and how.
Offline
Some points when serializing a class:
1. Define your fields as "published"
2. To have the needed RTTI for "published" fields, you need to inherit from TPersistent (or even better TSynPersistent, which is slightly faster and has a virtual constructor you may override)
3. As an alternative, you may defined {$M+} ... {$M-} around the class definition (but inheriting from TSynPersitent may be a better approach)
4. For nested classes (i.e. class instances within a class instance), consider using TSynAutoCreateFields which would initialize and finalize any internal published class instances in its constructor/destructor
5. For nested list/arrays of classes, consider defining T*ObjArray types, register them and use ObjArray*() functions: TSynAutoCreateFields would also recognize such published fields and hold them.
6. As an alternative for list/arrays of classes, you may use a TCollection (as detailed in the doc) but from our POV it is more verbose and less efficient.
Search for all those TSynPersistent / TSynAutoCreateFields keywords in the documentation.
Offline
Some points when serializing a class:
5. For nested list/arrays of classes, consider defining T*ObjArray types, register them and use ObjArray*() functions: TSynAutoCreateFields would also recognize such published fields and hold them.
Search for all those TSynPersistent / TSynAutoCreateFields keywords in the documentation.
I have searched documentation. Found part explaining TSynAutoCreateFields. Though, It is beyond my understanding. So, I thought it would be easier for me to provide an example and ask here. Staring with a simpler one since I might get it to my understanding and solve remaining myself.
I have following definitions:
TST_TAX_RATE = record
taxRate: UInt16;
end;
And, I need to define that record as an array:
procedure TForm5.Button3Click(Sender: TObject);
var
Json: AnsiString; // DLL call needs me sending AnsiString as parameters
stTaxRates: Array [1..8] of TST_TAX_RATE;
begin
// Serialize
Json := AnsiString(RecordSaveJson(stTaxRates, TypeInfo(TST_TAX_RATE)));
Memo1.Lines.Add(String(Json));
Exit;
end;
When I run, above code displays following in Memo1:
{"taxRate":0}
What I expect to read in it is something like:
[{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0}]
I didn't understand where I am making a mistake here.
Offline
Try with DynArraySaveJSON instead of RecordSaveJSON.
Offline
Changed related part in my code to:
Json := AnsiString(DynArraySaveJSON(stDept, TypeInfo(TST_DEPARTMENT)));
What I received was:
{"taxRate":0}
Thanks for your help. Unfortunately, that didn't solve it.
Offline
Latest version of the code:
TST_TAX_RATE = record
taxRate: UInt16;
end;
procedure TForm5.Button3Click(Sender: TObject);
var
Json: AnsiString; // DLL call needs me sending AnsiString as parameters
stTaxRates: Array [1..8] of TST_TAX_RATE;
begin
// Serialize
Json := AnsiString(DynArraySaveJSON(stTaxRates, TypeInfo(TST_TAX_RATE), True));
Memo1.Lines.Add(String(Json));
Exit;
end;
my result still is
{"taxRate":0}
EDIT: Just downloaded latest nightly build. Result is still same.
Last edited by ertank (2016-05-12 17:26:00)
Offline
1. taxRate is an UInt16, i.e. a "word".
This is not an enumerate, so TypeInfo(TST_TAX_RATE) would just find a record with a word within!
2. You are mixing dynamic arrays and fixed arrays (Array [1..8]) this won't work...
3. You are passing TypeInfo(TST_TAX_RATE) which is the RTTI info for a record, not a dynamic array.
Offline
I very much appreciate correction of my code, please.
Offline
Tried another code. This is not working for me, too.
TStTaxRate = record
taxRate: UInt16;
end;
TStTaxRates = Array of TStTaxRate;
procedure TForm5.Button3Click(Sender: TObject);
var
Json: AnsiString;
stTaxRates: TStTaxRates;
begin
SetLength(stTaxRates, 8);
// Serialize
Json := AnsiString(DynArraySaveJSON(stTaxRates, TypeInfo(TStTaxRates)));
Memo1.Lines.Add(String(Json));
Exit;
end;
TSTTaxRates is an array type in above code. Result on screen is:
[0,0,0,0,0,0,0,0]
This is close to what I need, however still missing variable name in serialized string. How can I change my code to get below result:
[{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0}]
Thanks.
Last edited by ertank (2016-05-12 21:15:38)
Offline
Hi Ab,
That code solved my problem. Thank you. I was starting to afraid that I won't be able to solve it in 1-2 lines of coding before I saw your post.
Should I use it always just before calling DynArraySaveJSON? Or, it is just fine to call once in the application (for example OnCreate event) and that is enough?
My working code is as follows:
TStTaxRate = packed record
taxRate: string;
end;
TStTaxRates = Array of TStTaxRate;
procedure TForm5.Button3Click(Sender: TObject);
var
Json: AnsiString;
stTaxRates: TStTaxRates;
begin
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TStTaxRates), 'taxRate word');
SetLength(stTaxRates, 8);
// Serialize
Json := AnsiString(DynArraySaveJSON(stTaxRates, TypeInfo(TStTaxRates)));
Memo1.Lines.Add('from mORMot: ' + String(Json));
Exit;
end;
Memo1 display:
from mORMot: [{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0},{"taxRate":0}]
That is exactly what I needed.
Last edited by ertank (2016-05-13 09:42:58)
Offline
Sorry, couldn't find it in Documentation "10.1.3.2.4. Text-based definition" section. Is it written somewhere else?
Offline
Using version '1.18.2639' of framework, I am getting Access Violation when using DynArrayLoadJSON for a specific record definition.
Record definition which gets access violation:
TStTaxRate = packed record
taxRate: string;
end;
TStTaxRates = Array of TStTaxRate;
Below is called at FormCreate:
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TStTaxRate), 'taxRate word');
Button click raises exception:
procedure TForm5.Button3Click(Sender: TObject);
var
s: RawUTF8;
stTaxRates: TStTaxRates;
begin
SetLength(stTaxRates, 8);
s := '[{"taxRate":100},{"taxRate":800},{"taxRate":0},{"taxRate":1800},{"taxRate":0},{"taxRate":100},{"taxRate":1800},{"taxRate":800}]';
DynArrayLoadJSON(stTaxRates, Pointer(s), TypeInfo(TStTaxRates));
end;
I have another dynamic record which I do not have any problems with.
TStDepartment = packed record
szDeptName: string;
u8TaxIndex: Byte;
iCurrencyType: UInt16;
iUnitType: UInt16;
u64Limit: UInt64;
u64Price: UInt64;
end;
TStDepartments = Array of TStDepartment;
// No TTextWriter.RegisterCustomJSONSerializerFromText() call for this record since it works out of the box
procedure TForm5.Button4Click(Sender: TObject);
var
s: RawUTF8;
stDept: TStDepartments;
begin
SetLength(stDept, 12);
s := '[{"szDeptName":"KSM1","u8TaxIndex":0,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM2","u8TaxIndex":1,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM3","u8TaxIndex":2,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM4","u8TaxIndex":3,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM5","u8TaxIndex":4,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM6","u8TaxIndex":5,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM7","u8TaxIndex":6,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM8","u8TaxIndex":7,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM9","u8TaxIndex":0,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM10","u8TaxIndex":1,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM11","u8TaxIndex":2,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0},{"szDeptName":"KSM12","u8TaxIndex":3,"iCurrencyType":949,"iUnitType":1,"u64Limit":99999999,"u64Price":0}]';
DynArrayLoadJSON(stDept, Pointer(s), TypeInfo(TStDepartments));
end;
Offline
Try:
initialization
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TStTaxRate), 'taxRate string');
end.
So, replace "word" with "string" .....
Offline
That did not work (function returned nil). Most likely because JSON formatting is a number formatting.
Last edited by ertank (2016-05-13 14:45:40)
Offline
I could solve my problem as below:
TStTaxRateString = packed record
taxRate: string;
end;
TStTaxRatesString = Array of TStTaxRateString;
TStTaxRateInteger = packed record
taxRate: UInt32;
end;
TStTaxRatesInteger = Array of TStTaxRateInteger;
procedure TfrmMain.btnTaxClick(Sender: TObject);
var
Json: AnsiString;
sRaw:RawUTF8;
RetValTax, RetValDept: TBytes;
RetCode: UInt32;
i, NumberOfTotalTaxRates, NumberOfTotalRecordsReceived: Integer;
Taxes: TStTaxRatesString;
StTaxRates: TStTaxRatesInteger;
begin
SetLength(StTaxRates, 8);
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TStTaxRatesString), 'taxRate word');
// Serialize
Json := AnsiString(DynArraySaveJSON(Taxes, TypeInfo(TStTaxRatesString)));
SetLength(RetValTax, STANDART_BUFFER);
RetCode := Json_FiscalPrinter_GetTaxRates(NumberOfTotalTaxRates, NumberOfTotalRecordsReceived, Json, @RetValTax[0], Length(RetValTax), 8);
if RetCode = 0 then
begin
// Convert TBytes to String which is actually a Json text
s := TEncoding.Default.GetString(RetValTax);
// Manually add " characters at beginning of numbers
sRaw := StringReplaceAll(RawUTF8(s), '":', '":"');
// Manually add " characters at the end of numbers
sRaw := StringReplaceAll(sRaw, '}', '"}');
// Set correct formatting for de-serialization
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TStTaxRatesString), 'taxRate string');
// De-serialization itself
DynArrayLoadJSON(Taxes, Pointer(sRaw), TypeInfo(TStTaxRatesString));
// Copy String Record Type into Integer record type with necessary convertion
for I := 0 to 7 do
StTaxRates[i].taxRate := StrToUInt64(Taxes[i].taxRate);
end;
end;
Offline
Pages: 1