11

Delphi XE 以降で強化された RTTI 機能を使用して、オブジェクトを XML に読み書きしようとしています。これまでのところ、整数、浮動小数点数、文字列、列挙型、セット、およびクラスで成功していますが、レコードを正しく出力または読み取ることはできません。問題は、レコード プロパティへのインスタンス (ポインター) を取得しているようです。

//Outputs Properties To XML
procedure TMyBase.SaveToXML(node: TJclSimpleXMLElem);
var
  child , subchild : TjclSimpleXMLElem ;
  FContext : TRttiContext ;
  FType    : TRttiType ;
  FProp    : TRttiProperty ;
  Value    : TValue ;
  MyObj    : TMyBase ;
  FField   : TRttiField ;
  FRecord  : TRttiRecordType ;
  Data     : TValue ;
begin
  FContext := TRttiContext.Create ;
  FType := FContext.GetType ( self.ClassType ) ;
  Child := node.Items.Add ( ClassName ) ;
  for FProp in FType.GetProperties do begin
    if FProp.IsWritable then begin
      case FProp.PropertyType.TypeKind of
        tkClass : begin
          MyObj := TMyBase ( FProp.GetValue ( self ).AsObject ) ;
          MyObj.SaveClass ( Child.Items.Add ( FProp.Name ) , FContext ) ;
          end ;
        tkRecord : begin
          subchild := Child.Items.Add ( FProp.Name ) ;
          FRecord := FContext.GetType(FProp.GetValue(self).TypeInfo).AsRecord ;
          for FField in FRecord.GetFields do begin
            >>> self is not the correct instance <<<
            Value := FField.GetValue ( self ) ;
            subchild.Items.Add ( FField.Name ).Value := Value.ToString ;
            end;
          end ;
        else begin
          Value := FProp.GetValue(self) ;
          Child.Items.Add ( FProp.Name ).Value := Value.ToString ;
          end;
        end;
      end ;
    end ;
  FContext.Free ;
end;

値を取得する方法がわかれば、値を設定することは問題にならないと思います。それから配列に、ああ、少年!

更新:以下を参照してください。(可視性を向上させるために別の回答として移行されました)。

4

2 に答える 2

12

ランタイム型 Self のレコード型フィールドの値を保存しようとしていると思いますよね?

を使用して、最初にフィールドの値を取得する必要がありますFProp.GetValue(Self)FieldValueそれを typeという変数に入れたとしましょうTValue。その後、必要に応じてレコード値のフィールドを保存できますが、レコードのフィールド自体がフィールドである可能性があるため、おそらく再帰的な手順を記述したいと思うでしょう。レコードのフィールド getter は、setter との対称性のためにレコードのアドレス (先頭へのポインター) を想定しています。セッターは値ではなくアドレスを期待します。そうしないと、別のクラスまたはレコードのフィールドを「その場で」変更する簡単な方法がないためです。レコードは値によって渡されるためです。

FieldValue.GetReferenceToRawData内に格納されているレコードの先頭へのポインターを返す でそれを取得できますTValue

うまくいけば、これが続行するための十分な手がかりになります。

于 2011-01-18T04:45:34.370 に答える
5

アトリビューション:元々はOP( Mitch )によって質問の更新として投稿されました -可視性を向上させるために個別の回答として移行されました。

バリーの解決策がそのトリックを成し遂げました。改訂されたコードは次のとおりです。

    tkRecord : begin
      subchild := Child.Items.Add ( FProp.Name ) ;
      Value := FProp.GetValue(self) ;
      FRecord := FContext.GetType(FProp.GetValue(self).TypeInfo).AsRecord ;
      for FField in FRecord.GetFields do begin
        Data := FField.GetValue ( Value.GetReferenceToRawData ) ;
        subchild.Items.Add ( FField.Name ).Value := Data.ToString ;
        end;
      end ;

配列を処理する必要がある場合:

    tkDynArray : begin
      Value := FProp.GetValue ( self ) ;
      FArray := FContext.GetType(Value.TypeInfo) as TRttiDynamicArrayType ;
      subchild := child.Items.Add ( FProp.Name ) ;
      cnt := Value.GetArrayLength ;
      subchild.Properties.Add ( 'Count' , cnt ) ;
      case FArray.ElementType.TypeKind of
        tkInteger ,
        tkFloat   : begin
          for a := 0 to cnt-1 do begin
            Data := Value.GetArrayElement ( a ) ;
            subchild.Items.Add ( IntToStr(a) , Data.ToString ) ;
            end;
          end ;
        tkRecord  : begin
          FRecord := FArray.ElementType as TRttiRecordType ;
          for a := 0 to cnt-1 do begin
            Data := Value.GetArrayElement ( a ) ;
            subsubchild := subchild.Items.Add ( IntToStr(a) ) ;
            for FField in FRecord.GetFields do
              SaveField ( subsubchild , FContext , FField , Data.GetReferenceToRawData ) ;
            end;
          end ;
于 2012-02-26T05:52:47.963 に答える