user1769184
user1769184

Reputation: 1621

access all elements of a record using RTTI

I want to dump a complex / long record into a memo for debugging purpose

 TmyRecord =
     aValue : String 
     aNumber : Real;
     Morenumbers   :  Integer ;
     ....
     ....
  end;

I think Delphi XE 2 RTTI should give me the chance to get the Fieldname , Fieldtype and value within a loop, to write this record to a memo or .....

Upvotes: 6

Views: 9381

Answers (2)

rsrx
rsrx

Reputation: 1473

Here is my try at this. I have similar task as you (refer to this thread). It's work in progress, but does the job good enough so far. This would enumerate all properties inside the TObject thou, so you'll have to adapt it to enumerate records:

function EnumerateProperties(const AObject: TObject): String;
var
  rt: TRttiType;
  prop: TRttiProperty;
  value, value2: TValue;
  valstr: String;
  propstr: String;
  fullstr: String;
  bres: Boolean;
  meth: TRttiMethod;
  bytes: TBytes;
  bytes_arr: TArray<TBytes>;
  uints: TArray<UINT32>;
  C1: Integer;
begin
  if not Assigned(AObject) then
    Exit('');

  rt := TRttiContext.Create.GetType(AObject.ClassType);

  fullstr := '';
  // iterate through public properties
  for prop in rt.GetDeclaredProperties do
  begin
    value := prop.GetValue(AObject); // get property value
    valstr := '?';

    // check property type
    case prop.PropertyType.TypeKind of
      tkInteger,
      tkInt64,
      tkFloat: valstr := value.AsVariant;

      tkString,
      tkChar,
      tkWChar,
      tkLString,
      tkWString,
      tkUString: valstr := QuotedStr(value.AsString);

      tkEnumeration: begin
        valstr := 'ENUM';
        if value.TryAsType<Boolean>(bres) then
          valstr := BoolToStr(bres, TRUE)
        else
        begin
          valstr := GetEnumName(value.TypeInfo, prop.GetValue(AObject).AsOrdinal);
        end;
      end;

      tkClass: begin
        // check if property is TList or any of its descendants,
        // then iterate through each of it's members
        meth := prop.PropertyType.GetMethod('ToArray');
        if Assigned(meth) then
        begin
          value2 := meth.Invoke(value, []);
          Assert(value2.IsArray);
          for C1 := 0 to value2.GetArrayLength - 1 do
            valstr := valstr + Format('(%s), ', [EnumerateProperties(value2.GetArrayElement(C1).AsObject)]);
          if valstr <> '' then
            Delete(valstr, Length(valstr) - 1, 2);
          valstr := Format('[%s]', [valstr]);
        end
        else // otherwise, process it as normal class
          valstr := Format('[%s]', [EnumerateProperties(value.AsObject)]);
      end;

      // dynamic arrays
      tkDynArray: begin
        if value.TryAsType<TBytes>(bytes) then // TBytes
          valstr := BytesToHex(bytes)
        else
          if value.TryAsType<TArray<TBytes>>(bytes_arr) then // TArray<TBytes>
          begin
            valstr := '';
            for C1 := Low(bytes_arr) to High(bytes_arr) do
              valstr := valstr + QuotedStr(BytesToHex(bytes_arr[C1])) + ', ';
            if valstr <> '' then
              Delete(valstr, Length(valstr) - 1, 2);
            valstr := Format('(%s)', [valstr]);
          end
          else
            if value.TryAsType<TArray<UINT32>>(uints) then // TArray<UINT32>
            begin
              valstr := '';
              for C1 := Low(uints) to High(uints) do
                valstr := valstr + IntToStr(uints[C1]) + ', ';
              if valstr <> '' then
                Delete(valstr, Length(valstr) - 1, 2);
              valstr := Format('(%s)', [valstr]);
            end;
      end;

      tkUnknown: ;
      tkSet: ;
      tkMethod: ;
      tkVariant: ;
      tkArray: ;
      tkRecord: ;
      tkInterface: ;
      tkClassRef: ;
      tkPointer: ;
      tkProcedure: ;
    end;

    propstr := Format('%s: %s', [prop.Name, valstr]);
    fullstr := fullstr + propstr + '; ';
  end;

  if fullstr <> '' then
    Delete(fullstr, Length(fullstr) - 1, 2);

  result := fullstr;
end;

Upvotes: 2

MBo
MBo

Reputation: 80287

As starting point - record with simple types. For complex fields (array, class etc) explore RTTI unit

type
  TmyRecord = record
    aValue: String;
    aNumber: Real;
    Morenumbers: Integer;
  end;
var
  m: TMyRecord;
  rtype: TRTTIType;
  fields: TArray<TRttiField>;
  i: Integer;
begin
  m.aValue := 'OK';
  m.aNumber := Pi;
  m.Morenumbers := 666;
  rtype := TRTTIContext.Create.GetType(TypeInfo(TMyrecord));
  Memo1.Lines.Add(rtype.ToString);
  fields := rtype.GetFields;
  for i := 0 to High(fields) do
    Memo1.Lines.Add(Format('%s: %s :: %s', [
      fields[i].Name,
      fields[i].FieldType.ToString,
      fields[i].GetValue(@m).ToString]));

output:

TmyRecord
aValue: string :: OK
aNumber: Real :: 3.14159265358979
Morenumbers: Integer :: 666

Upvotes: 20

Related Questions