#1 2015-06-11 06:34:51

willo
Member
From: Cape Town, South Africa
Registered: 2014-11-15
Posts: 67
Website

Serializing a Dynamic Array in Interfaced based services

Hi Ab,

As my adventures in mORMot continues, I now sit with an interesting issue. This is probably a FAQ, but given the following:

type 
  TSupplier = class(TSynPersistent)
  private
    fName : RawUTF8;
  published
     property Name : RawUTF8 read fName write fName;
  end;

  TSuppliers = array of TSupplier;

  ISuppliers = interface(IInvokable)
    ['{2BDC9F78-B9C2-4621-A557-F87F02AC0581}']
    function GetSuppliers(const aLocationID: RawUTF8; out Suppliers: TSuppliers): TCQRSResult;
  end;

When invoking the interface, Suppliers are serialized as an array of integer?

Changing

  TSuppliers = TObjectList<TSupplier>;

Generates Access Violations.

I've also tried to call:

TJSONSerializer.RegisterObjArrayForJSON( TypeInfo(TStores), TStore);

Which generates an error claiming that I have to write a custom serializer for TStore.

Some direction will be appreciated. smile

Offline

#2 2015-06-11 07:59:33

willo
Member
From: Cape Town, South Africa
Registered: 2014-11-15
Posts: 67
Website

Re: Serializing a Dynamic Array in Interfaced based services

Just an update:
1. Declaring TSuppliers as a descendant of TCollection works, but then I can't use TSupplier on it's own.
2. Declaring TSuppliers as a TObjectList serializes the array properly, but adds the ugly ClassName property.

Offline

#3 2015-06-11 09:29:34

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

Re: Serializing a Dynamic Array in Interfaced based services

If your TCollectionItem has the RTTI via {$M+}, you could it on its own, I suppose.

You could define

TSupplierObjArray = array of TSupplier;

Then register it via

  TJSONSerializer.RegisterObjArrayForJSON(TypeInfo(TSupplierObjArray),TSupplier);

Use ObjArray*() functions to manage the dynamic array.

You could even define T*ObjArray published properties to any TPersistent - preferably a TSynAutoCreateFields which would free the array at Destroy - and serialize them.
It sounds to me easier to work with than a TCollection.

There is no built-in support of generic lists by now.
Perhaps in the future.

Offline

#4 2015-06-11 13:41:22

willo
Member
From: Cape Town, South Africa
Registered: 2014-11-15
Posts: 67
Website

Re: Serializing a Dynamic Array in Interfaced based services

Hi Ab,

When the function call ends, I get an access violation in ObjArrayClear(Value^) (mORMot.pas[48685]) from what I traced it looks like it's part of the cleanup after the call was dispatched.

Perhaps I'm not initializing the Array correctly?

function TSupplierQuery.GetSuppliers(const aCompanyID: RawUTF8; out Suppliers: TSupplierObjArray): TCQRSResult;
var
  Res : ISQLDBRows;
  Supplier : TSupplier;
  I : integer;
begin
  Result := cqrsNotFound;
  Res := fDbConnection.Execute( 'select count(*) from SUPPLIERS where COMPANY_ID=?', [aCompanyID] );
  if Res.Step then begin
    ObjArraySetLength( Stores, 0 ); //Res.ColumnInt(0) ); -- Ideally I'd like to pre-allocate the array
    Res := fDbConnection.Execute( 'select SUPPLIER_ID from SUPPLIERS where COMPANY_ID=?', [aCompanyID] );
    while Res.Step do begin
      Supplier := TSupplier.Create;
      GetSupplier(Res['SUPPLIER_ID'], Supplier);
      ObjArrayAdd( Suppliers, Supplier );
    end;
  end;
end;

Offline

#5 2015-06-11 13:52:43

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

Re: Serializing a Dynamic Array in Interfaced based services

What is this "Stores" variable?

No need to call SELECT COUNT before the loop.
ObjArrayAdd() is just fine.

Offline

#6 2015-06-11 15:11:25

willo
Member
From: Cape Town, South Africa
Registered: 2014-11-15
Posts: 67
Website

Re: Serializing a Dynamic Array in Interfaced based services

Oops, that should be Suppliers.

Offline

#7 2015-06-11 15:37:59

willo
Member
From: Cape Town, South Africa
Registered: 2014-11-15
Posts: 67
Website

Re: Serializing a Dynamic Array in Interfaced based services

Reducing and simplifying to:

function TSupplierQuery.GetSuppliers(const aCompanyID: RawUTF8; out Suppliers: TSupplierObjArray): TCQRSResult;
var
  Res : ISQLDBRows;
  Supplier : TSupplier;
begin
  Result := cqrsNotFound;
  Res := fDbConnection.Execute( 'select SUPPLIER_ID from SUPPLIERS where COMPANY_ID=?', [aCompanyID] );
  while Res.Step do begin
    Supplier := TSupplier.Create;
    GetSupplier(Res['SUPPLIER_ID'], Supplier);
    ObjArrayAdd( Suppliers, Supplier );
  end;
end;

Still produces an Access Violation.

Changing mORMot.pas[48685]:

          if IsObjArray then
            ObjArrayClear(Value^) else
            Wrapper.Clear;

to

          if false then//IsObjArray then
            ObjArrayClear(Value^) else
            Wrapper.Clear;

Works perfectly, but I'm pretty sure that will produce a memory leak.

Last edited by willo (2015-06-12 10:54:53)

Offline

#8 2015-06-13 11:46:43

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

Re: Serializing a Dynamic Array in Interfaced based services

In fact, Wrapper.Clear won't produce any memory leak, since TDynArray would identify the array as a T*ObjArray...

But it is faster to use ObjArrayClear() - but the implementation was broken.
Please try http://synopse.info/fossil/info/4318a15a6c

Sorry for the inconvenience.

Offline

#9 2015-06-15 08:18:54

willo
Member
From: Cape Town, South Africa
Registered: 2014-11-15
Posts: 67
Website

Re: Serializing a Dynamic Array in Interfaced based services

Thanks Ab

Offline

#10 2017-02-13 09:44:53

sonadorje
Member
Registered: 2016-12-20
Posts: 13

Re: Serializing a Dynamic Array in Interfaced based services

willo wrote:

while Res.Step do begin
    Supplier := TSupplier.Create;
    GetSupplier(Res['SUPPLIER_ID'], Supplier);
    ObjArrayAdd( Suppliers, Supplier );
  end;

The Array of Supplier,  could be auto free memory by the framework?
if not,when the Array of Supplier should be free by manul?

Offline

#11 2017-02-13 12:19:08

warleyalex
Member
From: Sete Lagoas-MG, Brasil
Registered: 2013-01-20
Posts: 250

Re: Serializing a Dynamic Array in Interfaced based services

In order to be able to use this kinda service, a factory is mandatory. Before, you have to explicity resolve the factory to get automatic injection, you need to register the magic factory via aServer.ServiceContainer.InjectResolver(..). Doing this, it would let the factory be owned by aServer.Services - the parent, so that way you would not need to manually release its instance - the parent will release for you.

Offline

#12 2017-02-13 13:26:05

sonadorje
Member
Registered: 2016-12-20
Posts: 13

Re: Serializing a Dynamic Array in Interfaced based services

@warleyalex thank you explain the detail of it!

Offline

Board footer

Powered by FluxBB