2011-01-17 12 views
10

Sto tentando di utilizzare le funzionalità RTTI avanzate in Delphi XE o versioni successive per leggere e scrivere oggetti in XML. Finora ho avuto successo con interi, float, stringhe, tipi enumerati, set e classi ma non posso produrre o leggere correttamente i record. Il problema sembra essere ottenere un'istanza (puntatore) alla proprietà del record.Come utilizzare Delphi RTTI per ottenere e impostare i valori di registrazione

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

Sospetto che se riesco a capire come ottenere i valori, quindi impostarli non dovrebbe essere un problema. Quindi su array, oh ragazzo!

Aggiornamenti: Vedere below. (Migrato come risposta separata per migliorare la visibilità).

+0

Quale versione di Delphi? – GolezTrol

+0

E 'la versione XE – Mitch

+1

C'è qualche ragione particolare per cui vuoi salvare "record" invece di "classi"? in caso contrario, verificare che OmniXML abbia la capacità di salvare e caricare un discendente TPersistent in/da un file * .XML. http://www.omnixml.com/ – ComputerSaysNo

risposta

10

Suppongo che si stia tentando di salvare il valore di un campo tipografico del tipo di runtime di Self, sì?

Devi prima ottenere il valore del campo, con FProp.GetValue(Self). Diciamo che l'hai inserito in una variabile denominata FieldValue di tipo TValue. Puoi quindi salvare i campi del valore del record come preferisci, anche se probabilmente vorrai scrivere una procedura ricorsiva, poiché i campi del record potrebbero essere campi. Il getter di campo per i record si aspetta l'indirizzo del record (un puntatore al suo inizio) per simmetria con il setter; il setter si aspetta l'indirizzo piuttosto che il valore perché altrimenti non ci sarebbe un modo semplice per modificare un campo "in situ" in un'altra classe o record, poiché i record vengono altrimenti passati in giro per valore.

È possibile ottenere ciò con FieldValue.GetReferenceToRawData, che restituirà un puntatore all'inizio dei record memorizzati all'interno di TValue.

Speriamo che questo ti dia abbastanza indizi per continuare.

+0

Grazie, ci provo. TMyBase sarà l'antenato delle future classi e farà il lavoro per leggere e scrivere quelle classi. Quindi self è l'oggetto di esecuzione effettivo da salvare. – Mitch

+0

Grazie che ha funzionato! Vedi sopra. – Mitch

+2

Qualcuno dei tuoi studenti della vecchia scuola ricorda i giorni di Turbo pascal, quando avresti mantenuto un record creando un "F: File di MyRecord", assegnando (F, nome file) e quindi scrivendo il contenuto del record binario nel file? –

4

Attribuzione: Messaggio inserito come gli aggiornamenti di discussione dalle OP (Mitch ) - migrati come risposta separata per migliorare la visibilità.

La soluzione di Barry ha fatto il trucco. Ecco il codice rivisto:

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 ; 

Per coloro che hanno bisogno di trattare con gli array:

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 ; 
Problemi correlati