You are not logged in.
Dear ab,
when a TSQLRecord descendant contains an array of record, which contains another array of record, I could not figure how to customize JSON serializer for nested record array from text . Could you help to comment ? Many thanks !
When using the code at the end of this post, the exception is raised as shown below:
Project Project1.exe raised exception class ESynException with message
'Unregistered ptCustom for TJSONRecordTextDefinition.AddItem(NestedRecordArray: TNESTEDRECORDARRAY)'. Process stopped.
program Project1;
{$APPTYPE CONSOLE}
uses
FastMM4,
SynCommons, SynLog, SynTests, SynSQLite3, SynSQLite3Static, mORMot, mORMotSQLite3,
Contnrs, SysUtils;
type
TNestedRecord = packed record
Description: RawUTF8;
end;
TNestedRecordArray = array of TNestedRecord;
const
__TNestedRecord = 'Description: RawUTF8';
__TNestedRecordArray = '[Description: RawUTF8]';
type
TOuterRecord = packed record
NestedRecordArray: TNestedRecordArray;
end;
TOuterRecordArray = array of TOuterRecord;
const
__TOuterRecord = 'NestedRecordArray: TNestedRecordArray';
type
TSQLMainRecord = class(TSQLRecord)
private
FOuterRecordArray: TOuterRecordArray;
published
property OuterRecordArray: TOuterRecordArray read FOuterRecordArray write FOuterRecordArray;
end;
TTestManipulateNestedRecordArray = class(TSynTestCase)
published
procedure Test;
end;
TTestSuite = class(TSynTests)
published
procedure MyTestSuite;
end;
procedure TTestManipulateNestedRecordArray.Test;
var
ILog: ISynLog;
DBFileName: string;
Model: TSQLModel;
Rest: TSQLRest;
Rec: TSQLMainRecord;
RecID: Int64;
NestedRecord: TNestedRecord;
OuterRecord: TOuterRecord;
GroupA: TDynArray;
begin
ILog := TSynLog.Enter;
DBFileName := ChangeFileExt(ChangeFileExt(ExeVersion.ProgramFileName, '') +
'_' + FormatDateTime('yyyy_mm_dd_hh_nn_ss_zzz', Now),'.db3');
Model := TSQLModel.Create([TSQLMainRecord]);
try
Rest := TSQLRestClientDB.Create(Model, nil, DBFileName, TSQLRestServerDB, False);
try
TSQLRestClientDB(Rest).Server.CreateMissingTables;
Rec := TSQLMainRecord.Create;
try
RecordClear(OuterRecord, TypeInfo(TOuterRecord));
GroupA.Init(TypeInfo(TNestedRecordArray), OuterRecord.NestedRecordArray);
RecordClear(NestedRecord, TypeInfo(TNestedRecord));
NestedRecord.Description := 'string 1';
GroupA.Add(NestedRecord);
RecordClear(NestedRecord, TypeInfo(TNestedRecord));
NestedRecord.Description := 'string 2';
GroupA.Add(NestedRecord);
Rec.DynArray('OuterRecordArray').Add(OuterRecord);
RecID := Rest.Add(Rec, True);
Assert(RecID > 0, 'Error adding the data');
SynCommons.FileFromString(ObjectToJSON(Rec, [woHumanReadable, woObjectListWontStoreClassName]), 'Project1.json');
finally
Rec.Free;
end;
Rec := TSQLMainRecord.Create(Rest, RecID);
try
Check(Rec.DynArray('OuterRecordArray').Count = 1);
Check(Length(Rec.OuterRecordArray[0].NestedRecordArray) = 2);
Check(Rec.OuterRecordArray[0].NestedRecordArray[0].Description = 'string 1');
Check(Rec.OuterRecordArray[0].NestedRecordArray[1].Description = 'string 2');
finally
Rec.Free;
end;
finally
Rest.Free;
end;
finally
Model.Free;
end;
end;
procedure TTestSuite.MyTestSuite;
begin
AddCase([TTestManipulateNestedRecordArray]);
end;
begin
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TNestedRecord), __TNestedRecord);
// makes no difference
// TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TNestedRecordArray), __TNestedRecordArray);
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TOuterRecord), __TOuterRecord);
with TSynLog.Family do
begin
Level := LOG_VERBOSE;
PerThreadLog := ptIdentifiedInOnFile;
RotateFileCount := 5;
RotateFileSizeKB := 20*1024; // rotate by 20 MB logs
end;
with TTestSuite.Create do
begin
try
Run;
readln;
finally
Free;
end;
end;
end.
Offline
__TNestedRecord is indeed enough to define both the record and the dynamic array.
Try
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TNestedRecordArray), __TNestedRecord);
Online
Thank you for your efforts to help !
I still get the same exception:
Project Project1.exe raised exception class ESynException with message
'Unregistered ptCustom for TJSONRecordTextDefinition.AddItem(NestedRecordArray: TNESTEDRECORDARRAY)'. Process stopped.
Could you please help me with this problem ?
Offline
Dear ab,
the problem can be described with a much shorter program as shown below.
Could you please help to comment whether the second call to TJSONCustomParserRTTI.CreateFromTypeName should return nil by design ?
If not, could you please help me with this problem ?
program Project2;
{$APPTYPE CONSOLE}
uses
FastMM4,
SynCommons, // mORMot,
Contnrs, SysUtils;
type
TNestedRecord = packed record
Description: RawUTF8;
end;
TNestedRecordArray = array of TNestedRecord;
const
__TNestedRecord = 'Description: RawUTF8';
__TNestedRecordArray = '[Description: RawUTF8]';
var
Parser: TJSONCustomParserRTTI;
begin
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TNestedRecord), __TNestedRecord);
// Make no difference.
// TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TNestedRecordArray), __TNestedRecordArray);
// TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TNestedRecordArray), __TNestedRecord);
// Parser created !
Parser := TJSONCustomParserRTTI.CreateFromTypeName('NestedRecord', 'TNestedRecord');
Assert(Assigned(Parser), 'Parser is nil');
// Parser = nil !
Parser := TJSONCustomParserRTTI.CreateFromTypeName('NestedRecordArray', 'TNestedRecordArray');
Assert(Assigned(Parser), 'Parser is nil');
end.
Offline
Dear ab, I do not think I made the situation clear to you.
The direct cause of the problem described in the first post, i.e., failure of the line below:
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TOuterRecord), __TOuterRecord);
, is the failure of the line below (which is isolated and shown in the fourth post):
TJSONCustomParserRTTI.CreateFromTypeName('NestedRecordArray', 'TNestedRecordArray');
This latter line is in your library, i.e., SynCommons.pas.
Therefore, I do not see how I can use TypeInfo(TNestedRecordArray) as input.
I wonder if you could be kind enough to try the sample code as in the first post ?
Last edited by ComingNine (2015-09-19 14:41:46)
Offline
Dear ab,
I now understand your design principle better.
Specifically, for my problem, both calls below will register a "global parser" with the key "TNestedRecord". That is to say, there will not be a "global parser" with the name ''TNestedRecordArray".
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TNestedRecord), __TNestedRecord);
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TNestedRecordArray), __TNestedRecord);
The problem can be solved by modifying the definition of the layout of the TOuterRecord as shown below:
type
TOuterRecord = packed record
NestedRecordArray: TNestedRecordArray;
end;
const
// __TOuterRecord = 'NestedRecordArray: TNestedRecordArray';
__TOuterRecord = 'NestedRecordArray: array of TNestedRecord';
Thank you very much for your efforts to help !
Offline