2012-07-11 87 views
3

發送是否有辦法來本記錄轉換記錄到序列化表格數據,通過HTTP

TError = record 
    code: Word; 
    message: String; 
end; 

TState = record 
    caption: String; 
    address: Cardinal; 
    counters: TArray<Word>; 
    errors: TArray<TError>; 
end; 

轉換爲串行化的表單數據的字符串(Content-Type: application/x-www-form-urlencoded)像

caption=Foo& 
address=175896& 
counters[]=2& 
counters[]=2& 
errors[0][code]=52& 
errors[0][message]=ERR_NOT_AVAILABLE 

用於通過HTTP發送?

也許有一些功能類似於JQuery.param()

+0

這可能會幫助http://stackoverflow.com/questions/2984362/delphi-win32-serialization-libraries – Hardik 2012-07-11 07:40:46

+0

@hardik謝謝,但我已經讀過它。我需要一些與通常序列化到xml或json有所不同的東西。 – 2012-07-11 08:23:10

+2

如果這就是你所要求的,你可以做你自己的序列化格式。如果你問怎麼做,請學習json和xml的delphi實現以及如何使用rtti。有關示例,請參閱[將任何記錄轉換爲字符串並返回?](http://stackoverflow.com/q/10956790/576719)。或Robert Love的[XMLSerial](http://code.google.com/p/robstechcorner/source/browse/trunk/Delphi/rtti/xmlserial.pas) – 2012-07-11 09:58:26

回答

5

好的,這裏是一個樣板解決方案,它可以適用於您的特定序列化或其他用途。

A記錄,TSerializer,執行所有序列化作業,結果存儲在一個字符串列表中。

要使用它,請從TSerializer實例調用方法Serialize('state', TValue.From(state),sList);

您可以添加適合TValue的大部分類型,包括記錄,靜態數組,動態數組和簡單類。所有元素的展開都是通過遞歸完成的。 (免責聲明,這是對XE2測試,但我認爲德爾福-2010支持這裏使用的所有增強型RTTI調用)

從您的示例中的輸出是這樣的:

record state:TState 
    caption:string=Foo 
    address:Cardinal=175896 
    dynamic array counters:Word 
    counters[0]:Word=2 
    counters[1]:Word=2 
    end 
    dynamic array errors:TError 
    record errors[0]:TError 
     code:Word=52 
     message:string=ERR_NOT_AVAILABLE 
    end 
    end 
end 

這裏是源單元:

unit SerializerBoilerplate; 

interface 

uses 
    System.SysUtils, Classes, RTTI, TypInfo; 

Type 
    TSerializer = record 
    private 
    FSumIndent: string; 
    procedure IncIndent; 
    procedure DecIndent; 
    public 
    procedure Serialize(const name: string; thing: TValue; 
     sList: TStrings; first: boolean = true); 
    end; 

implementation 

procedure TSerializer.IncIndent; 
begin 
    FSumIndent := FSumIndent + ' '; 
end; 

procedure TSerializer.DecIndent; 
begin 
    SetLength(FSumIndent, Length(FSumIndent) - 2); 
end; 

procedure TSerializer.Serialize(const name: string; thing: TValue; 
    sList: TStrings; first: boolean); 
type 
    PPByte = ^PByte; 
var 
    LContext: TRTTIContext; 
    LField: TRTTIField; 
    LProperty: TRTTIProperty; 
    LRecord: TRTTIRecordType; 
    LClass: TRTTIInstanceType; 
    LStaticArray: TRTTIArrayType; 
    LDynArray: TRTTIDynamicArrayType; 
    LDimType: TRttiOrdinalType; 
    LArrayIx: array of integer; 
    LArrayMinIx: array of integer; 
    LArrayMaxIx: array of integer; 
    LNewValue: TValue; 
    i: integer; 
    // Generic N-dimensional array indexing 
    procedure IncIx(var ArrayIx, ArrayMinIx, ArrayMaxIx: array of integer); 
    var 
    dimIx: integer; 
    begin 
    dimIx := Length(ArrayIx) - 1; 
    repeat 
     if (ArrayIx[dimIx] < ArrayMaxIx[dimIx]) then 
     begin 
     Inc(ArrayIx[dimIx]); 
     break; 
     end 
     else 
     begin 
     ArrayIx[dimIx] := ArrayMinIx[dimIx]; 
     Dec(dimIx); 
     if (dimIx < 0) then 
      break; 
     end; 
    until (true = false); 
    end; 
    // Convert N-dimensional index to a string 
    function IxToString(const ArrayIx: array of integer): string; 
    var 
    i: integer; 
    begin 
    Result := ''; 
    for i := 0 to High(ArrayIx) do 
     Result := Result + '[' + IntToStr(ArrayIx[i]) + ']'; 
    end; 
    // Get correct reference 
    function GetValue(Addr: Pointer; Typ: TRTTIType): TValue; 
    begin 
    TValue.Make(Addr, Typ.Handle, Result); 
    end; 

begin 
    if first then 
    FSumIndent := ''; 

    case thing.Kind of 
    { - Number calls } 
    tkInteger, // Identifies an ordinal type. 
    tkInt64, // Identifies the Int64/UInt64 types. 
    tkPointer: // Identifies a pointer type. 
     begin 
     sList.Add(FSumIndent + name + ':' + thing.TypeInfo.name + '=' + 
      thing.ToString); 
     end; 
    tkEnumeration: 
     begin 
     if (thing.TypeInfo = TypeInfo(boolean)) then 
     begin 
      sList.Add(FSumIndent + name + ':' + thing.TypeInfo.name + '=' + 
      BoolToStr(thing.AsBoolean)); 
     end 
     else begin 
      // ToDO : Implement this 
     end; 
     end; // Identifies an enumeration type. 
    tkSet: // Identifies a set type. 
     begin 
     // ToDO : Implement this 
     end; 
    { - Float calls } 
    tkFloat: // Identifies a floating-point type. (plus Date,Time,DateTime) 
     begin 
     if (thing.TypeInfo = TypeInfo(TDate)) then 
     begin 
      sList.Add(FSumIndent + name + ':' + thing.TypeInfo.name + '=' + 
      DateToStr(thing.AsExtended)); 
     end 
     else if (thing.TypeInfo = TypeInfo(TTime)) then 
     begin 
      sList.Add(FSumIndent + name + ':' + thing.TypeInfo.name + '=' + 
      TimeToStr(thing.AsExtended)); 
     end 
     else if (thing.TypeInfo = TypeInfo(TDateTime)) then 
     begin 
      sList.Add(FSumIndent + name + ':' + thing.TypeInfo.name + '=' + 
      DateTimeToStr(thing.AsExtended)); 
     end 
     else 
     begin 
      sList.Add(FSumIndent + name + ':' + thing.TypeInfo.name + '=' + 
      FloatToStr(thing.AsExtended)); 
     end; 
     // ToDO : Handle currency 
     end; 

    { - String,character calls } 
    tkChar, // Identifies a single-byte character. 
    tkString, // Identifies a short string type. 
    tkLString: // Identifies an AnsiString type. 
     begin 
     sList.Add(FSumIndent + name + ':' + thing.TypeInfo.name + '=' + 
      thing.AsString); 
     end; 
    tkWString: // Identifies a WideString type. 
     begin 
     sList.Add(FSumIndent + name + ':' + thing.TypeInfo.name + '=' + 
      thing.ToString); 
     end; 
    tkInterface: // Identifies an interface type. 
     begin 
     // ToDO : Implement this 
     end; 
    tkWChar, // Identifies a 2-byte (wide) character type. 
    tkUString: // Identifies a UnicodeString type. 
     begin 
     sList.Add(FSumIndent + name + ':' + thing.TypeInfo.name + '=' + 
      thing.AsString); 
     end; 

    tkVariant: // Identifies a Variant type. 
     begin 
     // ToDO : Implement this 
     end; 

    { - Generates recursive calls } 
    tkArray: // Identifies a static array type. 
     begin 
     LStaticArray := LContext.GetType(thing.TypeInfo) as TRTTIArrayType; 
     SetLength(LArrayIx, LStaticArray.DimensionCount); 
     SetLength(LArrayMinIx, LStaticArray.DimensionCount); 
     SetLength(LArrayMaxIx, LStaticArray.DimensionCount); 
     sList.Add(FSumIndent + 'static array ' + name + ':' + 
      LStaticArray.ElementType.name); 
     IncIndent(); 
     for i := 0 to LStaticArray.DimensionCount - 1 do 
     begin 
      LDimType := LStaticArray.Dimensions[i] as TRttiOrdinalType; 
      LArrayMinIx[i] := LDimType.MinValue; 
      LArrayMaxIx[i] := LDimType.MaxValue; 
      LArrayIx[i] := LDimType.MinValue; 
     end; 
     for i := 0 to LStaticArray.TotalElementCount - 1 do 
     begin 
      Serialize(Name + IxToString(LArrayIx), 
      GetValue(PByte(thing.GetReferenceToRawData) + 
       LStaticArray.ElementType.TypeSize * i, 
       LStaticArray.ElementType), 
      sList,false); 
      IncIx(LArrayIx, LArrayMinIx, LArrayMaxIx); 
     end; 
     DecIndent(); 
     sList.Add(FSumIndent + 'end'); 
     end; 
    tkDynArray: // Identifies a dynamic array type. 
     begin 
     LDynArray := LContext.GetType(thing.TypeInfo) as TRTTIDynamicArrayType; 
     sList.Add(FSumIndent + 'dynamic array ' + name + ':' + 
      LDynArray.ElementType.name); 
     IncIndent(); 
     for i := 0 to thing.GetArrayLength - 1 do 
     begin 
      Serialize(Name + '[' + IntToStr(i) + ']', 
      GetValue(PPByte(thing.GetReferenceToRawData)^ + 
       LDynArray.ElementType.TypeSize * i, 
       LDynArray.ElementType), 
      sList,false); 
     end; 
     DecIndent(); 
     sList.Add(FSumIndent + 'end'); 
     end; 
    tkRecord: // Identifies a record type. 
     begin 
     sList.Add(FSumIndent + 'record ' + name +':' +thing.TypeInfo.name); 
     LRecord := LContext.GetType(thing.TypeInfo).AsRecord; 
     IncIndent(); 
     for LField in LRecord.GetFields do 
     begin 
      Serialize(LField.name, LField.GetValue(thing.GetReferenceToRawData), 
      sList, false); 
     end; 
     DecIndent(); 
     sList.Add(FSumIndent + 'end'); 
     end; 
    tkClass: // Identifies a class type. 
     begin 
     sList.Add(FSumIndent + 'object ' + name + ':' + thing.TypeInfo.name); 
     IncIndent(); 
     LClass := LContext.GetType(thing.TypeInfo).AsInstance; 
     for LField in LClass.GetFields do 
     begin 
      Serialize(LField.name, 
      // A hack to get a reference to the object 
      // See https://stackoverflow.com/questions/2802864/rtti-accessing-fields-and-properties-in-complex-data-structures 
      GetValue(PPByte(thing.GetReferenceToRawData)^ + LField.Offset, 
      LField.FieldType), 
      sList,false); 
     end; 
     // ToDO : Implement a more complete class serializer 
     DecIndent(); 
     sList.Add(FSumIndent + 'end'); 
     end; 

    { - Not implemented } 
    tkClassRef: ; // Identifies a metaclass type. 
    tkMethod: ; // Identifies a class method type. 
    tkProcedure: ; // Identifies a procedural type. 
    tkUnknown: ; // Identifies an unknown type that has RTTI. 
    end; 
end; 

end. 

和測試單元:

program SerializerProj; 

{$APPTYPE CONSOLE} 
{$R *.res} 

uses 
    Classes, 
    SysUtils, 
    RTTI, 
    SerializerBoilerplate; 

Type 
    TMyObj = Class 
    private 
    fI: integer; 
    fS: string; 
    end; 

    TInnerRec = record 
    A, B, C: string; 
    end; 

    TDim1 = 1 .. 3; 
    TDim2 = 2 .. 5; 
    TMyArr = array [TDim1, TDim2] of integer; // Must be typed dimensions 

    TTestRec = record 
    s: string; 
    ws: WideString; 
    st: ShortString; 
    ansiCh: AnsiChar; 
    ansiS: AnsiString; 
    wChar: Char; 
    B: boolean; 
    i: integer; 
    t: TTime; 
    d: TDate; 
    dt: TDateTime; 
    fd: Double; 
    fS: Single; 
    r: TInnerRec; 
    arr: TMyArr; 
    dArr: array of string; 
    o: TMyObj; 
    end; 

    TError = record 
    code: Word; 
    message: String; 
    end; 

    TState = record 
    caption: String; 
    address: Cardinal; 
    counters: TArray<Word>; 
    errors: TArray<TError>; 
    end; 

var 
    tr: TTestRec; 
    state: TState; 
    sList: TStringList; 
    s: string; 
    Serializer: TSerializer; 

begin 
    state.caption := 'Foo'; 
    state.address := 175896; 
    SetLength(state.counters,2); 
    state.counters[0] := 2; 
    state.counters[1] := 2; 
    SetLength(state.errors,1); 
    state.errors[0].code := 52; 
    state.errors[0].message := 'ERR_NOT_AVAILABLE'; 

    tr := Default (TTestRec); 
    sList := TStringList.Create; 
    try 
    tr.s := 'A'; 
    tr.ws := 'WS'; 
    tr.st := '[100]'; 
    tr.ansiCh := '@'; 
    tr.ansiS := '@!'; 
    tr.wChar := 'Ö'; 
    tr.B := true; 
    tr.i := 100; 
    tr.t := Now; 
    tr.d := Now; 
    tr.dt := Now; 
    tr.fd := Pi; 
    tr.fS := 2 * Pi; 
    tr.r.A := 'AA'; 
    tr.r.B := 'BB'; 
    tr.r.C := 'CC'; 
    tr.arr[1, 2] := 12; 
    tr.arr[1, 3] := 13; 
    tr.arr[1, 4] := 14; 
    tr.arr[1, 5] := 15; 
    tr.arr[2, 2] := 22; 
    tr.arr[2, 3] := 23; 
    tr.arr[2, 4] := 24; 
    tr.arr[2, 5] := 25; 
    tr.arr[3, 2] := 32; 
    tr.arr[3, 3] := 33; 
    tr.arr[3, 4] := 34; 
    tr.arr[3, 5] := 35; 
    SetLength(tr.dArr, 3); 
    tr.dArr[0] := 'A'; 
    tr.dArr[1] := 'B'; 
    tr.dArr[2] := 'C'; 
    tr.o := TMyObj.Create; 
    tr.o.fI := 11; 
    tr.o.fS := '22'; 

    Serializer.Serialize('tr', TValue.From(tr), sList); 
    for s in sList do 
     WriteLn(s); 
    sList.Clear; 
    Serializer.Serialize('state', TValue.From(state),sList); 
    for s in sList do 
     WriteLn(s); 

    ReadLn; 
    finally 
    sList.Free; 
    end; 

end. 

我有一個幫助研究Rtti accessing fields and properties in complex data structures有點幫助。

+0

感謝您的詳細解答,但我已經使用RTTI和類似的算法編寫了自己的序列化程序。我認爲你的例子會對其他人有用。 – 2012-07-25 11:51:20