You are not logged in.
@ab, There is also an unanswered question about this subject here https://synopse.info/forum/viewtopic.php?pid=28888
Can you explain this subject more?
AsynchRedirect seems the best way to implement asynchronous calls especially when you do not have a UI and using Linux so does not have Windows messages and want to prevent locks in case of calling server in a callback.
@ab,
It is the only search result about AsynchRedirect that has sample code.
Can you make documentation more clear or give a simple sample on this subject? because as you mentioned in the doc it seems using AsynchRedirect is the only good way to prevents locks (For example calling server again in the callback)
Great, Thanks.
Thanks you very much ab.
Can I ask why TShort63?
@ab I found the bug, it was because of pointers.
I solved it temporarily.
@@ -49046,7 +49046,7 @@ type
Reader: TJSONSerializerCustomReader;
Writer: TJSONSerializerCustomWriter;
Props: PPropInfoDynArray;
- Fields: PShortStringDynArray; // match Props[] order
+ Fields: TShortStringDynArray; // match Props[] order
Kind: TJSONObject;
end;
TJSONCustomParsers = array of TJSONCustomParser;
@@ -49136,7 +49136,7 @@ var i: integer;
begin
if Parser^.Props<>nil then begin // search from RegisterCustomSerializerFieldNames()
for i := 0 to length(Parser^.Fields)-1 do
- if IdemPropNameU(Parser^.Fields[i]^,PropName,PropNameLen) then begin
+ if IdemPropNameU(Parser^.Fields[i],PropName,PropNameLen) then begin
result := Parser^.Props[i];
exit;
end;
@@ -49148,7 +49148,7 @@ end;
class procedure TJSONSerializer.RegisterCustomSerializerFieldNames(aClass: TClass;
const aClassFields, aJsonFields: array of ShortString);
var prop: PPropInfoDynArray;
- field: PShortStringDynArray;
+ field: TShortStringDynArray;
n,p,f: integer;
found: boolean;
parser: PJSONCustomParser;
@@ -49167,7 +49167,7 @@ begin
for f := 0 to high(aClassFields) do // check customized field name
if IdemPropName(prop[p].Name,aClassFields[f]) then begin
if aJsonFields[f]<>'' then begin // '' to ignore this property
- field[n] := @aJsonFields[f];
+ field[n] := aJsonFields[f];
prop[n] := prop[p];
inc(n);
end;
@@ -49175,7 +49175,7 @@ begin
break;
end;
if not found then begin // default serialization of published property
- field[n] := @prop[p].Name;
+ field[n] := prop[p].Name;
prop[n] := prop[p];
inc(n);
end;
@@ -52475,7 +52475,7 @@ begin
raise EParsingException.CreateUTF8('%.WriteObject woDontStoreInherited '+
'after RegisterCustomSerializerFieldNames(%)', [self,aClassType]) else
for i := 0 to length(parser^.Props)-1 do begin
- CustomPropName := parser^.Fields[i];
+ CustomPropName := @parser^.Fields[i];
WriteProp(parser^.Props[i]);
end;
end else
Yes, so I will wait for him to validate my findings.
Thank you Ehab.
Yes you are right about the JSON, but the problem is still there.
TClass1 and TClass2 are inherited from same class but I checked with different classes that inherited from TSynAutoCreateFields and RegisterCustomSerializerFieldNames will use only the last custom serializer.
@ab , can you validate this?
Hi,
It seems RegisterCustomSerializerFieldNames has a bug when I use more than one custom field name for more than one class.
The second register will override the first one and it is odd because they are different classes.
I can't use record serialization because inheritance is a need.
program project1;
uses
SynCommons,
mORMot;
type
{ TBaseClass }
TBaseClass = class(TSynAutoCreateFields)
private
fName: string;
published
property Name: string read fName write fName;
end;
{ TClass1 }
TClass1 = class(TBaseClass)
private
ftype_: string;
published
property type_: string read ftype_ write ftype_;
end;
{ TClass2 }
TClass2 = class(TBaseClass)
private
fclass_: string;
published
property class_: string read fclass_ write fclass_;
end;
{ TTestClass }
TTestClass = class(TSynAutoCreateFields)
private
fCount: integer;
fObject1: TClass1;
fObject2: TClass2;
published
property Count: integer read fCount write fCount;
property Object1: TClass1 read fObject1;
property Object2: TClass2 read fObject2;
end;
var
JsonString: RawUTF8;
Obj: TTestClass;
begin
TJSONSerializer.RegisterCustomSerializerFieldNames(TClass1, ['type_'], ['type']);
TJSONSerializer.RegisterCustomSerializerFieldNames(TClass2, ['class_'], ['class']);
JsonString := '{"Count":2,"Object1":{"Name":"C1","type_":"TYPE"},"Object2":{"Name":"C2","class_":"CLASS"}}';
Obj := TTestClass.Create;
WriteLn(ObjectLoadJSON(Obj, JsonString, nil, JSONTOOBJECT_TOLERANTOPTIONS));
WriteLn(ObjectToJSON(Obj, [woHumanReadable]));
Obj.Free;
ReadLn;
end.
I just did it and it seems you are right. 430mb for 2000 user.
@ab Can you comment on this and what settings I can set?
Nice point.
So what can I do for supporting many concurrent users?
Hi,
I wanted to try WebSockets chat test with many clients so I made this example that runs many clients at once.
The problem is for 50 clients, Server will use around 50mb of memory and it seems very high.
Using last mORMot, Windows10 and FPC 3.1.1.
Can somebody show me how should I config server and clients?
Server code:
program Server;
{$R *.res}
uses
ChatUnit,
SynCommons,
mORMot,
SynBidirSock,
mORMotHttpServer;
var
HttpServer: TSQLHttpServer;
Srv: TSQLRestServerFullMemory;
begin
Srv := TSQLRestServerFullMemory.CreateWithOwnModel([]);
try
Srv.ServiceDefine(TChatService, [IChatService], sicShared).SetOptions([], [optExecLockedPerInterface]).ByPassAuthentication := True;
HttpServer := TSQLHttpServer.Create('8888', [Srv], '+', useBidirSocket);
try
HttpServer.WebSocketsEnable(Srv, ChatKey);
TextColor(ccLightGreen);
WriteLn('Server is live');
ReadLn;
finally
HttpServer.Free;
end;
finally
Srv.Free;
end;
end.
Client code:
unit ManyClientsMain;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
ChatUnit, SynCommons, mORMot, mORMotHttpClient;
type
TClient = record
HttpClient: TSQLHttpClientWebsockets;
Service: IChatService;
Callback: TChatCallback;
end;
{ TForm1 }
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
const
CltsCount = 100;
var
Clts: array of TClient;
procedure NewMsg(Sender: TObject);
public
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
var
i: integer;
begin
Randomize;
SetLength(Clts, CltsCount);
for i := 0 to CltsCount - 1 do
with Clts[i] do
begin
HttpClient := TSQLHttpClientWebsockets.Create('127.0.0.1', '8888', TSQLModel.Create([]));
HttpClient.Model.Owner := HttpClient;
HttpClient.WebSocketsUpgrade(ChatKey);
HttpClient.ServerTimeStampSynchronize;
HttpClient.ServiceDefine([IChatService], sicShared);
HttpClient.Services.Resolve(IChatService, Service);
Callback := TChatCallback.Create(HttpClient, IChatCallback);
Callback.OnNotify := @NewMsg;
Service.Join('User: ' + IntToStr(Random(1000)), Callback);
end;
end;
procedure TForm1.NewMsg(Sender: TObject);
begin
end;
end.
Any comment on this @ab?
Thanks.
Do you allow me to correct it if I can? Becuase the code is still there but not working and I have projects dependent on it and it still seems a good feature in my view.
And also I don't get your explanation in the replacement with DTO because in my usage I have complicated classes with inheritance and my need is very simple, just rename trouble maker names.
Can you example more? I use classes and I cant go with records if you mean that.
Also, can I ask why you removed that? When I use mORMot with servers that made in another language there is a high chance of using Pascal keywords like Class, Type, To, From and ...
So is there any replacement for such feature?
Making a custom reader for just renaming seems overwork.
Unfrotuanalty I cant find the remove commit.
This ability seems broken in last Lazarus and mORMot with 64bit fpc.
I checked the code and somehow pointer of the specific field will change in the process so in JSONCustomParsersFieldProp it cant find the correct property.
@ab do you have a test case to check?
@ab is there a way for implementing such thing without problem or I'm missing something?
I changed TJSONSerializerCustomReader and removed const so I can change value and made a very simple sample, I have 2 class that maybe I need them to put them in there and one has a string value and another has an integer.
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
function ValueReader(aValue: TObject; aFrom: PUTF8Char; var aValid: boolean; aOptions: TJSONToObjectOptions): PUTF8Char;
public
end;
{ TTestValue }
TTestValue = class(TSynAutoCreateFields)
private
FName: string;
published
property Name: string read FName write FName;
end;
{ TTestValue1 }
TTestValue1 = class(TTestValue)
private
FValue: string;
published
property Value: string read FValue write FValue;
end;
TTestValue2 = class(TTestValue)
private
FValue: integer;
published
property Value: integer read FValue write FValue;
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
var
tv: TTestValue;
begin
TJSONSerializer.RegisterCustomSerializer(TTestValue, @ValueReader, nil);
tv := TTestValue.Create;
ObjectLoadJSON(tv, ReadFileToString('1.json'), nil, [j2oIgnoreUnknownProperty]);
WriteLn(tv.Name);
ObjectLoadJSON(tv, ReadFileToString('2.json'), nil, [j2oIgnoreUnknownProperty]);
WriteLn(tv.Name);
end;
function TForm1.ValueReader(aValue: TObject; aFrom: PUTF8Char; var aValid: boolean; aOptions: TJSONToObjectOptions): PUTF8Char;
var
op: RawUTF8;
Values: TPUtf8CharDynArray;
N: PUTF8Char;
begin
FreeAndNil(aValue);
aValid := False;
Result := nil;
op := UTF8ToString(aFrom);
Values := nil;
JSONDecode(op, ['Name'], Values);
N := Values[0];
WriteLn(N);
if N = 'V1' then
aValue := TTestValue1.Create
else if N = 'V2' then
aValue := TTestValue2.Create;
Result := JSONToObject(aValue, aFrom, aValid, nil, JSONTOOBJECT_TOLERANTOPTIONS);
end;
for a sample JOSN like:
{
"Name": "V1",
"Value": "String"
}
and 2.json
{
"Name": "V2",
"Value": 10
}
It is a naive solution but it does not work anyway because when I call JSONToObject it brings me back to the custom reader because TTestValue1 is inherited from TTestValue. Is there a way for me to prevent it or another good way to decode a JSON and read it in 2 different class by a condition?
As you can see I don't want to write all the read and write by myself because the classes are very complicated and if I force to use a custom reader, using mORMot reader will lose the point.
I have a sample JSON file that has a name and a class that I don't know what class will be there, for example, a button or a label so I should use TControl, but all of them have a Classname so I can decide based on it but how?
RegisterCustomSerializer will make a class and then just let me fill the properties, but I just want to choose what object it should make and the rest will be on JSON serializer.
For example:
"Name" : "Cmpt1"
//It is maybe a button or a label but it always has a ClassName
"Obj": {
"ClassName"="TButton"
......}
and my class is like:
TMyClass= class
published
property Name:String read FName write FName;
property Obj:TControl read FObj;
How can I choose what inherited class it should choose?
Hi ab,
I install newpascal and lazarus with fpcdeluxe and rebuild mORMot and project with that , Broken pipe exception goes away and project works fine.
I compile project with delphi and fpc in windows and all tests passed successful.
Yes, server side codes are thread safe. Broken pipe occur in client side before sending and receiving data to/from server.
Pardon me.My goal of the code is an example of my program.
Please follow the forum rules.
https://synopse.info/forum/misc.php?action=rules
Don't post such extensive source code in the forum.
Thank you for attention.
I can't use StringFromFile function because I have process data before saving them in file.
After many tries I found a way to reproduce exception, if Calling server methods in a loop all process work fine but if I make a delay between request Broken pipe exception raised. Maybe it's occur because some threads closed.
Lazarus Thread window doesn't show any thread change activity except TProcessThread running and closing.
When I press continue button of Broken Pipe exception other process work fine and file send to server and receive by client.
I use below code to create HttpServer.
var
ApiMdl: TSQLModel;
ApiSrv: TSQLRestServerDB;
HTTPSrv: TSQLHttpServer;
begin
//Enable logging
with TSQLLog.Family do
begin
Level := LOG_VERBOSE;
EchoToConsole := LOG_VERBOSE;
end;
//Create SQLModel to serve database
ApiMdl := TSQLModel.Create([], ROOT_NAME);
ApiSrv := TSQLRestServerDB.Create(ApiMdl, {'1.db'}SQLITE_MEMORY_DATABASE_NAME, False);
with ApiSrv do
begin
//Create missing tabales
CreateMissingTables;
//Register services
ServiceDefine(TProcessService, [IProcess], sicSingle);
end;
//Start HttpServer
HTTPSrv := TSQLHttpServer.Create(DEFAULT_PORT, [ApiSrv], '+', HTTP_DEFAULT_MODE);
//Allow cross-site AJAX queries
HTTPSrv.AccessControlAllowOrigin := '*';
WriteLn('Processor is running. press enter to terminate Processor');
ReadLn;
WriteLn('Processor going to stop');
//ApiCon.Free;
HTTPSrv.Free;
ApiSrv.Free;
ApiMdl.Free;
WriteLn('Processor stoped successfull');
end.
And I think server use socket because when change port to 80 it say port 80 is in use.
I build project with lazarus 1.9 and FPC stable 3.0.2 and my os is linux (Manjaro) .
Hi ab,
I wrote a client/server RESTFul to serve image processing in server. To send files from client to server first read all file data and convert stream to RawByteString and pass data as Interface method parameter to service , because reading and writing files is too slow and cause UI freeze I create a thread to serve reading, converting , sending , receving and saving data, to determine when data are ready using another method of service (GetProgress) and if data are ready call another method (DownloadFile) , download file method return a rawbytestring and I write data to stream.
When I calling service methods in main thread all methods work fine but when running methods in separated thread randomly two exception occurs : 1 - Broken Pipe , 2 - File not found
1 - Broken Pipe : I don't now why this error occur when calling SendFile method of service.
2 - File not found : when calling DownloadFile method immediately after SendFile File not found exception occur while in SendFile method I only save file and send file id as result and in DownloadFile read that saved file and send it to client. It look like file bounded operators in service run after finished SendFile method.
Thread Execute method
procedure TFileThread.Execute;
var
fs: TStream;
Index: integer;
begin
Synchronize(@SyncProgress);
begin
fs := TFileStream.Create(FFilePath, fmOpenRead);
Index := FI.SendFile(StreamToRawByteString(fs));
fs.Free;
while True do
begin
FProgress := FI.GetProgress(Index);
if (FProgress = 100) then
break;
Synchronize(@SyncProgress);
end;
FData := FI.DownloadFile(Index);
Synchronize(@SyncFinish);
end;
end;
Service Interface
IProcessor = interface(IInvokable)
['{8B1806EB-39CF-4ADC-B582-CF1C27F77B3E}']
function SendFile(Stream: RawByteString): integer;
function GetProgress(Index: integer): integer;
function DownloadFile(Index: integer): RawByteString;
end;
Service class
TProcessorService = class(TInterfacedObject, IProcessor)
public
function SendFile(StreamData: RawByteString): integer;
function GetProgress(Index: integer): integer;
function DownloadFile(Index: integer): RawByteString;
end;
Service implementation
function TProcessorService.SendFile(StreamData: RawByteString): integer;
var
fs: TFileStream;
pm: TProcessModel;
frs: TStream;
begin
//Result := FLastIndex;
//Save File
fs := TFileStream.Create(IntToStr(FLastIndex) + '.jpg', fmCreate);
frs := RawByteStringToStream(StreamData);
frs.Position := 0;
fs.CopyFrom(frs, frs.Size);
fs.Free;
Result := FLastIndex;
Inc(FLastIndex);
end;
function TProcessorService.GetProgress(Index: integer): integer;
var
I: integer;
pm: TProcessModel;
begin
Result := 100;
end;
function TProcessorService.DownloadFile(Index: integer): RawByteString;
var
I: integer;
fs: TFileStream;
begin
fs := TFileStream.Create(inttostr(Index) + '.file', fmOpenRead);
Result := StreamToRawByteString(fs);
fs.Free;
end;
Thank you very much George.
It helped me.
About mORMot it is great but it lacks simple samples or doc for learning for newbies. This is no one responsibility but that makes hard for new comers adn I think one of the reasons of why mORMot users seems less that it's many abilities and much power.
Thanks turrican, you are very kind.
I got many multiple question in last few days and here are their links, if you git time to help:
In JSON Serializer:
https://synopse.info/forum/viewtopic.ph … 757#p23757
https://synopse.info/forum/viewtopic.ph … 821#p23821
And this is more for ab but he didint respond unfortunately:
https://synopse.info/forum/viewtopic.php?id=3893
And about making a client working offline:
https://synopse.info/forum/viewtopic.php?id=3910
I appreciate if you can help in any one.
Thanks edwinsn and turrican. Yes I know @ab made a very well framework that can do very simple to very complicated stuff easily and I aperciate it and love to have it in the Pascal world, but the problem is, it is not welcome for new comers, yes if you spend a lot of time in the doc and source you will eventually find your way and yes a complicated framework has a learning curve but for mORMot is like you are in the middle of the jungle.
Samples folder is very good but as I said it is like a showing off mORMot abilities not a path for learning it (it needs showing off, it is FAST, BIG and GOOD).
Take OmniThreadLibrary as an example, it is complicated, not as mORMot but complicated enough in a sensitive filed, threading. but did you saw the samples? if you are very new still you can find out how it works and how it can used in your projects.
I think the reason for not having such a good way for learning it is, author dont need sample and didn't made them and everyone like you @edwinsn spent a lot of time to learn it and then again dont fell the need of making learning samples either. Also another reason is when you make a sample for such big library, you need to know it well, and maybe there is not enough people know it like that much to teach it to new comers.
Even take me as an example, I worked with multiple stuff of mORMot and the best I done until know was using it's JSON serialiser and even after days I dont know much of it so I still cant make a sample for others.
Although, One can say if you want it, work for it and yes many ones do; my point is, such a brilliant tool dont need to be like a jungle of great code and doc and samples without a map.
For someone new mORMot's documentation is like a atlas not a learning book.
Thank you warleyalex.
Hi, It is my thought in last days working with mORMot and wanted to share with you hope to get your opinion about it.
Dear ab I know you dont like to give the last answer to lazy people but Maybe it is clear for you (ab as the maker or anyone experience with the huge doc) but it is not easy to mess around with a huge doc that in the explanation will link to many places and expect you to know most of the previous doc to understand what is going on.
And the samples seem not for a newbie, they seem they added while you develop mORMot and wanted to show how it is powerful not for a new learner to learn basic unless he read the whole doc because it will link to many places like a story (and many key parts are like blog posts so it like you need to know previous back story) , you cant read a session for learning just that.
I tried to learn JSON serialization and made it work somehow with you helps but that was it can work without the other mORMot tools.
I hope you dont look at this comment as a rude one, but I read many of your posts in more than 5 years in this forum that they are explaining a task that can be very clear if there was a simple sample for it.
As an example I was trying to make a demo for JSON serialization when I learned it completely but I couldnt end it and lost in the code and couldn't get more help from you or another person.
Hope it was clear.
Sorry but I cant understand how it can help and I looked in the samples list an there is not one to show how it works.
I tried to change Sample 4 to make it like the doc and add TRecordVersion property and do RecordVersionSynchronizeSlave but it is not clear in the doc how it can help.
Can you point me to the working sample?
There was sample in doc but I could not make it work and I used array for that and only for auto reader not custom readers.
Hi,
What is my best choice to have an offline read/write cache of data and changes when network connection is lost and then sync back to/from server when it back?
Can I do it with mORMot and record version? can I only have read only cache? should I make it by hand or there is a ready to use tools for it?
Hi,
I did that just like this part: https://synopse.info/files/html/Synopse … #TITLE_244
If you got problem, I will try to help you.
Is this way right from your view?
Can I have your technical opinion?
Works very well. Thanks.
Thanks!
I checked that and it makes SIGSEGV error on line 47213 "Writer := nil; // exclusive" in FPC truck.
Thanks, but where is it?
If you mean this https://synopse.info/files/html/Synopse … ml#TITL_71 I cant understand how it can help because it need "ClassName" value but I get this JSON from another sever and the value is "Type" for example.
I'm still looking for a solution in documentation and the code and the best I found is something like this and it seems it works.
procedure TestIt;
var
it: TParentItem;
s: RawUTF8;
begin
TJSONSerializer.RegisterObjArrayForJSON([TypeInfo(TParentItems), TParentItem]);
TTextWriter.RegisterCustomJSONSerializer(TypeInfo(TParentItems), TSReaderWriter.ParentItemsReader, nil);
s := StringFromFile('test.json');
it := TParentItem.Create;
ObjectLoadJSON(it, s, nil, [j2oIgnoreUnknownProperty]);
ObjectToJSONFile(it, 'output.json');
it.Free;
end;
{ TSReaderWriter }
class function TSReaderWriter.ParentItemsReader(P: PUTF8Char; var aValue; out aValid: boolean): PUTF8Char;
var
V: TObject absolute aValue;
typ: string;
op:RawUTF8;
begin
aValid := False;
Result := nil;
if (p = nil) then
exit;
if (V = nil) then
begin
op:=UTF8ToString(P);
if typ = 'C1' then
V := TParentItem1.Create
else if typ = 'C2' then
V := TParentItem2.Create
else
V := TParentItem.Create;
end;
result := JSONToObject(aValue, P, aValid, nil, JSONTOOBJECT_TOLERANTOPTIONS);
end;
Hi,
I have JSON like this:
{
"ID": 1,
"Childs": [{
"ID": 2,
"Typ": "C1",
"Name1":"X"
}, {
"ID": 3,
"Typ": "C2",
"Name2":"Y"
}
]
}
Childs can have different classes with different types but I have an array with base Child class, so I made something like this:
type
TParentItem = class;
TParentItems = array of TParentItem;
{ TItem }
TItem = class(TSynAutoCreateFields)
private
FID: integer;
published
property ID: integer read FID write FID;
end;
{ TParentItem }
TParentItem = class(TItem)
private
FChilds: TParentItems;
FTestItem: TItem;
published
property TestItem: TItem read FTestItem;
property Childs: TParentItems read FChilds write FChilds;
end;
{ TParentItem1 }
TParentItem1 = class(TParentItem)
private
FName1: string;
published
property Name1: string read FName1 write FName1;
end;
{ TParentItem2 }
TParentItem2 = class(TParentItem)
private
FName2: string;
published
property Name2: string read FName2 write FName2;
end;
How can I choose what class should be made for array item? I have "Typ" value to choose that but making a CustomSerializer wont help becuase it pass a const object and I can change the value of it.
Is there a way to pass a custom create function so in there I can check the "Typ" value and choose that?
I checked the code and it seems InternalClassPropInfo give the prop list in revers order, but I cant clearly understand how you do that but I checked FPC implementation like this and it write the list in order.
uses typinfo,Rtti;
c: TRttiContext;
RttiType: TRttiType;
PropList: {$ifdef fpc}specialize{$endif} TArray<TRttiProperty>;
i: Integer;
begin
c := TRttiContext.Create;
RttiType := c.GetType(TParentItem.ClassInfo);
PropList:=RttiType.GetProperties;
WriteLn(Length(PropList));
for i:=0 to Length(PropList)-1 do
begin
WriteLn(PropList[i].Name);// will print ID,Childs but InternalClassPropInfo give Childs first
end;
c.Free;
What do you think?
Great.
Yes I know, its a test and honestly I still trying to understand the flow of your codes. they are clean but very complicated and optimized.
So I done something like this:
DA.Init(TypeInfo(TItem),V.FChilds);
DA.LoadFromJSON(Values[2]);
The only question is how can I set the value with the property "Childs" not the exact value like "FChilds"?
So I work more with it and find out my implementation was silly so I made a new one so you can now register custom names for a class.
TJSONSerializer.RegisterCustomNames(TItem, ['Type', 'Type_', 'Class', 'Class_']);
Please tell me what you think.
Patch:
https://ufile.io/722201
Hi,
In my journey in reading JSON with mORMot, as @ab said I went to a road that I make custom serializer for my classes that need special care.
In many of these 3rd party JSONs there is only one problem; they are using identifier names like "Type", "Class" or "To" and I obviously I cant use them as property name so I saw my self making many custom reader an writer for only one property name and I think it does not seem wise so I made some changes in JSONToObject so it takes care of these "CustomNaming" and here is my changes and it works well so I can read JSON like this:
{
"ID": 1,
"Type": "Item",
"Class":"Book"
}
like this:
ObjectLoadJSONWithCustomNames(it, s,nil,[],['Type','Type_','Class','Class_']);
I changed the function code like this:
...
PropName := GetJSONPropName(From,@PropNameLen); // get property name
//CustomNaming
if Length(CustomNames) mod 2 =0 then
begin
cni:=0;
while cni<=(Length(CustomNames) div 2) do
begin
if UTF8ToString(CustomNames[cni])=UTF8ToString(PropName) then
begin
PropName:=CustomNames[cni+1];
PropNameLen:=Length(PropName);
end;
inc(cni,2);
end;
end;
if (From=nil) or (PropName=nil) then
...
So I wanted to ask you @ab, is it a good practice to do the job like this?
PS, Here is the link to the patch of my changes:
https://ufile.io/c2983
Hi,
AS suggested in forum I'm trying to make a new custom reader for my JSON like document https://synopse.info/files/html/Synopse … ml#TITL_52 and it goes well.
But I dont know who to handle array values and I cant find any sample.
One of the properties is array and trying many function including JSONArrayDecode cant decode the JSON array.
What function I should use for arrays?
So I need a custom one. For now automated serialization works very fine on most of the classes and I wished I can change the order so it does not make me to write all of the classes by hand.
I should say your automated serialization works very well and it is the best I found in Pascal language, these options I'm talking about make it more configurable to use it easily out of an ORM too.
I can change the source if you think it is right though.
Yes I know but it makes everything much more complicated, I have many classes and automated serialization works fine for most of them, I just want a log for automated serialization.
Yes but only for parent but not all the childs, I want to only make it for parent not all childs.
Hi,
In Lazarus packages make develop easier and because mORMot has not a package I think, I made a simple one, it makes managing project very easier so you dont need to add path to mORMot every time or add include path or SQLite ... . Also it let you to chose custom compile options and debug modes. As I said very useful for manage project in Lazarus.
This sample package should save in mORMot folder under "Package" folder.
For now it only includes only same base unit, if ab like the idea I can add all the needed units.
Download link:
https://ufile.io/c4aa2
@ab, what do you think?
PS, its good to have attachment ability in the forum.
For future reference there is the topics:
https://synopse.info/forum/viewtopic.php?id=3891
https://synopse.info/forum/viewtopic.php?id=3892
https://synopse.info/forum/viewtopic.php?id=3893