2011-01-17 2 views
10

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

//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; 

値を取得する方法を理解できれば、それらの設定は問題ではないはずです。その後、配列に、ああ男の子!

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

+0

ここで改訂されたコードですか? – GolezTrol

+0

そのXEバージョン – Mitch

+1

"クラス"の代わりに "レコード"を保存する特別な理由はありますか?もしそうでなければ、OmniXMLをチェックすると、TPersistent子孫を* .XMLファイルに/からロードする機能があります。 http://www.omnixml.com/ – ComputerSaysNo

答えて

10

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

フィールドの値は、最初にFProp.GetValue(Self)で取得する必要があります。 TValueFieldValueという変数に入れてみましょう。レコードのフィールドは自分自身でフィールドにすることができるので、レコードの値のフィールドを自由に保存することができますが、おそらく再帰的なプロシージャを記述したいと思うでしょう。レコードのフィールドgetterは、セッターと対称性を持つレコードのアドレス(開始位置へのポインタ)を要求します。セッターは値以外のアドレスを期待しています。そうでなければ、別のクラスやレコードでフィールドを "インシトゥ"に変更する簡単な方法はありません。

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

これで、続行するための十分な手がかりが得られたらうれしいです。

+0

ありがとう、私はそれを試してみましょう。 TMyBaseは将来のクラスの祖先になり、これらのクラスを読み書きする作業を行います。したがって、selfは保存される実際の実行時オブジェクトです。 – Mitch

+0

ありがとうございました!上記を参照。 – Mitch

+2

古い学校の人は、 "F:File of MyRecord"を作成し、(F、filename)を割り当ててバイナリレコードの内容をファイルに書き込むことで、Turbo Pascalの時代を覚えていますか? –

4

帰属:はもともと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 ; 
関連する問題