我正在尝试使用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;
我怀疑如果我能弄清楚如何获取值,那么设置它们应该不是问题。然后到阵列上,哦,男孩!
更新:请参阅below。 (作为单独的答案迁移以提高可见性)。
答案 0 :(得分:11)
我认为你试图保存Self的运行时类型的记录类型字段的值,是吗?
您必须先使用FProp.GetValue(Self)
获取该字段的值。假设您将其放在FieldValue
类型TValue
的变量中。然后,您可以根据需要保存记录值的字段,但您可能希望为其编写递归过程,因为记录的字段本身可能是字段。记录的字段getter需要记录的地址(指向其开头的指针)以便与setter对称; setter期望地址而不是值,因为否则就没有简单的方法来修改另一个类或记录中的“in situ”字段,因为记录会以值传递。
你可以使用FieldValue.GetReferenceToRawData
来获取它,它将返回指向TValue
内存储的记录开头的指针。
希望这能为你提供足够的线索来继续。
答案 1 :(得分: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 ;