Recent

Author Topic: [SOLVED] RTTI setValue of property  (Read 871 times)

StraightFree

  • New Member
  • *
  • Posts: 23
[SOLVED] RTTI setValue of property
« on: January 25, 2020, 08:01:06 pm »
Hello,

First of all I would like to thank all the support that I have been having from the community.

Thanks!

Now I have a problem using the RTTI function setValue.

In a function I receive a TObject and an ID. I query the database and with RTTI I fill in the TOject properties.

The procedure is executed without any error.
However, when accessing the properties of this TObject, the values ​​are filled in incorrectly. The value of one property ends up being repeated in others, with no logic in the structure.

Here is the content of the function:

Code: Pascal  [Select]
  1. var
  2.   Contexto: TRttiContext;
  3.   TypObj: TRttiType;
  4.   Prop: TRttiProperty;
  5.   strInsert, strFields: String;
  6.   Atributo: TCustomAttribute;
  7.  
  8.   PK: string;
  9.   q: TZQuery;
  10.  
  11. begin
  12.   strInsert := '';
  13.   strFields := '';
  14.   PK := getFieldPKRTTI(Obj.ClassType);
  15.  
  16.  
  17.   strInsert := 'SELECT * FROM ' + getTableNameRTTI(Obj.ClassType) +
  18.             ' WHERE ' + PK + ' = ' + ID.ToString ;
  19.  
  20.   q := Conexao.getQuery(strInsert);
  21.   q.Open;
  22.  
  23.   Contexto := TRttiContext.Create;
  24.   TypObj := Contexto.GetType(Obj.ClassInfo);
  25.  
  26.   for Prop in TypObj.GetProperties do begin
  27.     for Atributo in Prop.GetAttributes do begin
  28.         t := 0;
  29.  
  30.         if Atributo is TIntegerCampo then
  31.         begin
  32.            t := q.FieldByName(TIntegerCampo(Atributo).NomeCampo).AsInteger;
  33.            Prop.SetValue(PByte(Obj),t);
  34.         end;
  35.  
  36.         if Atributo is TOutroCampo then
  37.         begin
  38.            case TOutroCampo(Atributo).TipoCampo of
  39.            ftBoolean:
  40.              begin
  41.              t := q.FieldByName(TOutroCampo(Atributo).NomeCampo).AsString;
  42.              Prop.SetValue(PByte(Obj),t);
  43.              end;
  44.            ftBlob, ftString:
  45.              begin
  46.              t := q.FieldByName(TOutroCampo(Atributo).NomeCampo).AsString;
  47.              Prop.SetValue(PByte(Obj),t);
  48.              end;
  49.            ftFloat, ftCurrency:
  50.              begin
  51.              t := q.FieldByName(TOutroCampo(Atributo).NomeCampo).AsFloat;
  52.              Prop.SetValue(PByte(Obj),t);
  53.              end;
  54.            ftDate:
  55.              begin
  56.              t := q.FieldByName(TOutroCampo(Atributo).NomeCampo).AsDateTime;
  57.              Prop.SetValue(PByte(Obj),t);
  58.              end;
  59.            ftTime:
  60.              begin
  61.              t := q.FieldByName(TOutroCampo(Atributo).NomeCampo).AsDateTime;
  62.              Prop.SetValue(PByte(Obj),t);
  63.              end;
  64.           end;
  65.         end;
  66.       end;
  67.   end;
  68. end;  
  69.  

When I access the properties of a TObject I would see it like this:
Code: Pascal  [Select]
  1. ID_PESSOA = 1
  2. ID_EMPRESA = 2
  3. ID_UnidNeg = 3
  4. NOME = TESTE
  5. FANTASIA = TESTE FANTASIA
  6. ENDERECO = RUA TESTE
  7. NUMERO = 111
  8. BAIRRO = BAIRRO TESTE
  9. CIDADE = CIDADE TESTE
  10. ESTADO = SP
  11. LIMITE = 1.5
  12. LIMTE CREDITO = 1.99
  13. DATA CADASTRO = 25-1-20
  14. HORA CADASTRO = 15:15:00
  15. ATIVO = f
  16. BLOB = fdsfsdfsfds
  17. LIMITE CREDITO = 1.99
  18.  

The result is this:

Code: Pascal  [Select]
  1. ID_PESSOA = 1
  2. ID_EMPRESA = 2
  3. ID_UnidNeg = 3
  4. NOME = fdsfsdfsfds
  5. FANTASIA = f
  6. ENDERECO = fdsfsdfsfds
  7. NUMERO = f
  8. BAIRRO = fdsfsdfsfds
  9. CIDADE = f
  10. ESTADO = fdsfsdfsfds
  11. LIMITE = 1.5
  12. LIMTE CREDITO = 1.99
  13. DATA CADASTRO = 25-1-20
  14. HORA CADASTRO = 15:15:00
  15. ATIVO = f
  16. BLOB = 15:15:00
  17. LIMITE CREDITO = 1.99
  18.  


Is this a problem with RTTI or my logic?

Any help will be welcome.

Thanks!
« Last Edit: January 29, 2020, 04:27:08 pm by StraightFree »

PascalDragon

  • Hero Member
  • *****
  • Posts: 948
  • Compiler Developer
Re: RTTI setValue of property
« Reply #1 on: January 27, 2020, 09:30:54 am »
What type is t? It might be best to use a TValue (if t isn't one already) and manually use its TValue.Make method (example is for the TDateTime case):

Code: Pascal  [Select]
  1. dt := q.FieldByName(TOutroCampo(Atributo).NomeCampo).AsDateTime;
  2. TValue.Make(@dt, PTypeInfo(TypeInfo(TDateTime)), t);
  3. Prop.SetValue(PByte(Obj),t);

You can also use the following to avoid the use of a local variable for each type:

Code: Pascal  [Select]
  1. t := TValue.From<TDateTime>(q.FieldByName(TOutroCampo(Atributo).NomeCampo).AsDateTime);
  2. Prop.SetValue(PByte(Obj),t);

(and casting Obj to Pointer would be more correct, though it's no error)

mr-highball

  • Full Member
  • ***
  • Posts: 111
Re: RTTI setValue of property
« Reply #2 on: January 27, 2020, 04:31:04 pm »
I've also run into some "quirks" with rtti in general while attempting to write json serialization / deserialization with attributes. While it's not the rtti unit, you could try typinfo SetPropValue()

This is where I'm using it, but I'm sure there is probably some resources on the wiki too
https://github.com/mr-highball/ezjson/blob/master/src/ezjson.pas#L746
-Highball

PascalDragon

  • Hero Member
  • *****
  • Posts: 948
  • Compiler Developer
Re: RTTI setValue of property
« Reply #3 on: January 28, 2020, 09:41:00 am »
I've also run into some "quirks" with rtti in general while attempting to write json serialization / deserialization with attributes. While it's not the rtti unit, you could try typinfo SetPropValue()

It would be nice if thoes "quirks" would be reported, so that we can either fix them or confirm that this is indeed how things are supposed to be.

StraightFree

  • New Member
  • *
  • Posts: 23
Re: RTTI setValue of property
« Reply #4 on: January 29, 2020, 04:26:49 pm »
PascalDragon,

Thanks for your rapid reply.
I was only able to see your post today.

Your solution worked perfectly.

Problem solved.

The only question I didn't quite understand, would be how to convert Obj to Pointer.

Anyway, the proposed solution will solve my initial problem.

Thanks again for the help.

PascalDragon

  • Hero Member
  • *****
  • Posts: 948
  • Compiler Developer
Re: RTTI setValue of property
« Reply #5 on: January 30, 2020, 09:13:35 am »
Your solution worked perfectly.

Problem solved.

Good to know that it worked. I wonder though why FPC did pick the wrong operator overload then...  :(

The only question I didn't quite understand, would be how to convert Obj to Pointer.

In your original code you had PByte(Obj), however Pointer(Obj) is enough though the former is not an error.

mr-highball

  • Full Member
  • ***
  • Posts: 111
Re: [SOLVED] RTTI setValue of property
« Reply #6 on: February 01, 2020, 07:38:32 am »
I've also run into some "quirks" with rtti in general while attempting to write json serialization / deserialization with attributes. While it's not the rtti unit, you could try typinfo SetPropValue()

It would be nice if thoes "quirks" would be reported, so that we can either fix them or confirm that this is indeed how things are supposed to be.

@pascaldragon I'm still unsure of the "why" for the issues I'm seeing. I'm using generic methods (which are new), attributes (which are also new), nested generic methods, and recursion (this one is thankfully old, but can complicate things) all to try and piece a working library together which almost certainly has bugs by me.

I don't want to hijack this post any more, but once I come up with a decent test that can reproduce what I'm seeing (without sifting through all my other code) I'll be sure to write a bug report.
-Highball

PascalDragon

  • Hero Member
  • *****
  • Posts: 948
  • Compiler Developer
Re: [SOLVED] RTTI setValue of property
« Reply #7 on: February 01, 2020, 10:52:16 am »
I don't want to hijack this post any more, but once I come up with a decent test that can reproduce what I'm seeing (without sifting through all my other code) I'll be sure to write a bug report.

I'd appreciate that. :)