How to use DefineProperties in a custom Class Object for dynamic Arrays - Delphi

zlee

I'm trying to create my own class object and use it to store various data types for my application, this all works fine when using Published Properties, I can stream these to disk and back with no problems. But I need to stream some dynamic Arrays of integer types as well.

            type
              TArrayOfInteger = array of integer;

              TSetting = class(TComponent)
              private
                fIntVal: integer;
                fIntArr: TArrayOfInteger;
                procedure ReadIntArr(Reader: TReader);
                procedure WriteIntArr(Writer: TWriter);
              protected
                procedure DefineProperties(Filer: TFiler); override;
              published
                property intval: integer read fIntVal write fIntVal;
                property intArr: TArrayOfInteger read fIntArr write fIntArr;
              end;

            { TSetting }

            procedure TSetting.DefineProperties(Filer: TFiler);
            begin
              inherited;
              Filer.DefineProperty('intArr', ReadIntArr, WriteIntArr, true);
            end;
            procedure TSetting.ReadIntArr(Reader: TReader);
            var
              i: integer;
              lvVal:Integer;
            begin
              i:=low(fintArr);
              Reader.ReadListBegin;
              {j := Reader.ReadInteger();
              setlength(fIntArr, j);
              for i := 0 to j - 1 do
              begin
                fIntArr[i] := Reader.ReadInteger();
              end;}
            while not Reader.EndOfList do begin
                fIntArr[i]:=Reader.ReadInteger;
                Inc(i);
              end;
              Reader.ReadListEnd;
            end;

            procedure TSetting.WriteIntArr(Writer: TWriter);
            var
              i: integer;
            begin
              Writer.WriteListBegin;
              //Writer.WriteInteger(integer(Length(fIntArr)));
              for i := Low(fIntArr) to High(fIntArr) do
              begin
                Writer.WriteInteger(fIntArr[i]);
              end;
              Writer.WriteListEnd;
            end;

            function ClassToStr(pvClass:TComponent):ansiString;
            var
              inStream, outStream: TMemoryStream;

            begin
              inStream := TMemoryStream.Create;
              outStream := TMemoryStream.Create;
              try
                inStream.WriteComponentRes(pvClass.ClassName, pvClass);
                //inStream.WriteComponent(pvClass);
                inStream.Position := 0;
               ObjectResourceToText(inStream, outStream);
               // ObjectBinaryToText(inStream,outStream);
                outStream.Position := 0;
                SetLength(Result,outStream.Size+1);
                FillChar(result[1],outStream.Size+1,0);
                outStream.ReadBuffer(result[1],outStream.Size);
              finally
                FreeAndNil(inStream);
                FreeAndNil(outStream);
              end;
            end;
            function StrToClass(pvStr:AnsiString;pvComponent:TComponent):tcomponent;
            var
              inStream, outStream: TMemoryStream;
            begin
              inStream := TMemoryStream.Create;
              outStream := TMemoryStream.Create;
              try
                if (pvStr<>'') then
                inStream.WriteBuffer(pvStr[1],length(pvStr));
                inStream.Position:=0;
                ObjectTextToResource(inStream, outStream);
               // ObjectTextToBinary(inStream,outStream);
                outStream.Position:=0;
                result:=outStream.ReadComponentRes(pvComponent); //*****Exception Fired*****
                //result:=outStream.ReadComponent(pvComponent);
              finally
                FreeAndNil(inStream);
                FreeAndNil(outStream);
              end;

            end;

            =============
            //test
            procedure TForm1.btn5Click(Sender: TObject);
            var
              lvObj,lv1: TSetting;
              lvStr:String;
              lvArr:TArrayOfInteger;
            begin
              lvObj := TSetting.Create(nil);
              try
                lvObj.intval := 12345;
                setlength(lvArr, 3);
                lvArr[0] := 222;
                lvArr[1] := 333;
                lvArr[2] := 444;
                lvObj.intArr:=lvArr;
                lvStr:=ClassToStr(lvObj);
                RegisterClass(TSetting);
                lvObj.intval:=1;
                lv1:=TSetting( StrToClass(lvStr,lvObj));
                if (lv1.intval>0) then
                mmo1.Text:=lvStr;
              finally
                FreeAndNil(lvObj);
              end;
              // WriteComponentResFile(ExtractFilePath(ParamStr(0))+ 'd.res',self);
            end;

            //First chance exception at $77925B68. Exception class EReadError with message 'Property  does not exist'. Process Project1.exe (23512)

            //First chance exception at $77925B68. Exception class EReadError with message 'Error reading TSetting.: Property  does not exist'. Process Project1.exe (23512)


result:=outStream.ReadComponentRes(pvComponent); //*****Exception Fired*****
David Heffernan

You are not allocating the array when reading it. You could do that like so:

procedure TSetting.ReadIntArr(Reader: TReader);
begin
  fIntArr := nil;
  Reader.ReadListBegin;
  while not Reader.EndOfList do begin
    SetLength(fIntArr, Length(fIntArr) + 1);
    fIntArr[high(fIntArr)] := Reader.ReadInteger;
  end;
  Reader.ReadListEnd;
end;

The other change that you need to make is to move intArr to be a public property. You cannot have it published, and also define a property with the same name in DefineProperties.

I am somewhat dubious of your use of AnsiString. I would have expected UTF-8 encoded bytes in case of non-ASCII characters. Perhaps you should be using a string stream with the appropriate encoding specified.

Personally I am rather sceptical of using form streaming in this way. I would prefer to use a standard format such as JSON.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How to use Object.defineProperties()?

Is it valid to use Object.defineProperties with symbols?

how can use dynamic array in delphi

How to use the `.loc` method from pandas on a custom class object?

How to correctly re-use pointer object to custom class

How to use STL sort to sort custom class object with template specialization?

Object.defineProperties nested

Migrating JavaScript to TypeScript: how to deal with Object.defineProperties used on `this`?

How to traverse through Dynamic Object of Arrays in VueJS

How to use xlwings asynchronous mode with dynamic arrays?

how to merge custom class arrays in java to be sorted

How to declare an object of arrays of custom type in typescript

Implementing use of 'with object() as f' in custom class in python

How to use MOVE for dynamic arrays with a record as an element and a dynamic array field on it?

Delphi Saving/Loading Dynamic Arrays Failed

How to use dynamic style class in React Native

How to use dynamic property names for a Json object

How to use dynamic object properties in TypeScript

How to use linq Sum in a dynamic object

How to use typescript with dynamic object key

How to get dynamic object keys and use it?

How to set object in state arrays from dynamic form in React

How to share dynamic object of arrays among components in ReactJS?

How to convert an custom class object to a tuple in Python?

How to create a custom string representation for a class object?

How to cache/save custom class object in Android?

How to create custom class from variable? (not Object)

How to convert Array of Object to Custom Class Object (that is array to object)?

Object.defineproperty() vs Object.defineproperties()