分享

艺见钟情: datasnap的进阶 传递自定义类型的类

 quasiceo 2017-02-05

datasnap的进阶 传递自定义类型的类

前面我写了一个返回自定义类的例子,将要返回的类marshal后装进TJSONArray,今天看来真是多余,因为datasnap的对自定义类的marshal做得很自动,直接返回就行。
前面我定义了
1
2
3
4
5
6
7
type
  TPerson = class
    FirstName: string;
    LastName: string;
    Age: Integer;
  end;
  TPersonArray = array of TPerson;
然后写了服务方法
1
function TServerMethods1.GetPersons: TJSONArray;
这样写没问题,如果直接写成
1
function TServerMethods1.GetPersons: TPersonArray;
是不可以的,因为TPersonArray不是类,它的TTypeInfo的TypeKind不是tkClass而是tkDynArray,目前是不datasnap支持的。
于是可以写一个集合类
1
2
3
4
5
6
7
8
  TPersonCollection = class
  private
    FPersons: TArray<tperson>;
  public
    destructor Destroy; override; //别忘了free掉元素
    property Persons: TArray<tperson> read FPersons write FPersons;
  end;
</tperson></tperson>
然后写服务方法
1
function TServerMethods1.GetPersons: TPersonCollection;
这样就可以了,服务器端可以直接写,客户端可以直接读,不用自己去marshal和unmarshal了。

那是不是说,我们的Field只要封到类里就一定ok呢,不是的。
将上面的类修改为如下
1
2
3
4
5
6
7
TPerson = class
  FirstName: string;
  LastName: string;
  Age: Integer;
  Birthday: TDateTime;
  Memo: TStringList;
end;

TDateTime其实是一个double类型,在datasnap里面是被支持的,TStringList却不行了。但是如果客户端是其他语言REST来的,TDataTime在客户端就难被解释了。怎么办呢
其实Datasnap里,我们可以自己定义marshal的规则。
XE2里面,有两个类TConverterEvent给编码,和TReverterEvent来解码。给EMBT封装后,用法也很简单。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var
  DateTimeDecodeFunc: TStringReverter;
  DateTimeEncodeFunc: TStringConverter;
procedure RegisterReverterConverters;
var
  decode: TReverterEvent;
  encode: TConverterEvent;
begin
  DateTimeEncodeFunc := function(Data: TObject; Field: string): string
    var
      ctx: TRttiContext;
      date: TDateTime;
    begin
      date := ctx.GetType(Data.ClassType).GetField(Field).GetValue(Data).AsType<tdatetime>;
      Result := FormatDateTime('yyyy-mm-dd hh:nn:ss', date);
    end;
  DateTimeDecodeFunc := procedure(Data: TObject; Field: string; Arg: string)
    var
      ctx: TRttiContext;
      datetime: TDateTime;
    begin
      datetime := EncodeDateTime(StrToInt(Copy(Arg, 1, 4)), //yyyy
        StrToInt(Copy(Arg, 6, 2)), //mm
        StrToInt(Copy(Arg, 9, 2)), //dd
        StrToInt(Copy(Arg, 12, 2)),//hh
        StrToInt(Copy(Arg, 15, 2)),//nn
        StrToInt(Copy(Arg, 18, 2)),//ss
        0);
      ctx.GetType(Data.ClassType).GetField(Field).SetValue(Data, datetime);
    end;
  decode := TReverterEvent.Create(TPerson, 'Birthday');
  decode.StringReverter := DateTimeDecodeFunc;
  TJSONConverters.AddReverter(R);
  encode := TConverterEvent.Create(TPerson, 'Birthday');
  encode.StringConverter := DateTimeEncodeFunc;
  TJSONConverters.AddConverter(encode);
end;
</tdatetime>
简单的说,就是定义转换规则后,加给TJSONConverters,让TJSONConverters知道要这么转换来转换回去。 至于TStringList的转换,道理明白了,所以也一样可以写了。Delphi2010的DataSnap可以看这里。 转换的一些普通的函数,EMBT已经提供得有。在Data.DBXJSONReflect单元里,能找到
1
2
3
4
5
6
7
8
9
/// <summary>Converts a TStringList into a TSerStringList</summary>
function StringListConverter(Data: TObject): TObject;
/// <summary>Reverts a TSerStringList into a TStringList</summary>
function StringListReverter(Ser: TObject): TObject;
/// converts the pair list of a JSON Object into a serializable structure
function JSONObjectPairListConverter(Data: TObject; Field: String): TListOfObjects;
function JSONArrayElementsConverter(Data: TObject; Field: String): TListOfObjects;
procedure JSONObjectPairListReverter(Data: TObject; Field: String; Args: TListOfObjects);
procedure JSONArrayElementsReverter(Data: TObject; Field: String; Args: TListOfObjects);
除了这个方法,XE2里面另外还用到了描述类,来自动告诉JSON怎么转换。
将上面的TPerson修改为
1
2
3
4
5
6
7
8
TPerson = class
   FirstName: string;
   LastName: string;
   Age: Integer;
   [JSONReflect(ctString, rtString, TDateTimeInterceptor, nil, True)]
   Birthday: TDateTime;
   Memo: TStringList;
 end;
给Birthday增加描述,JSONReflect是个描述类,继承自TCustomAttribute。
看JSONReflect的代码
1
2
3
4
5
6
7
8
9
constructor Create(ConverterType: TConverterType;
    ReverterType: TReverterType; InterceptorType: TClass = nil;
    PopulationCustomizerType: TClass = nil;
    IsMarshalOwned: boolean = false); overload;
TConverterType = (ctObjects, ctStrings, ctTypeObjects, ctTypeStrings,
  ctObject, ctString, ctTypeObject, ctTypeString);
TReverterType = (rtObjects, rtStrings, rtTypeObjects, rtTypeStrings, rtObject,
  rtString, rtTypeObject, rtTypeString);
这几种类型
顾名思义,
第一个参数是表示进去的数据类型,
第二个是出来的类型,
第三个是执行转换的类,必须从TJSONInterceptor继承
第四个先不管,必须从TJSONPopulationCustomizer继承
第五个表示了是否拥有创建出来的类

目前只管写第3个执行转换的类。由于我们只是要转字符串,所以只覆盖字符的转换就行。
1
2
3
4
5
TDateTimeInterceptor = class(TJSONInterceptor)
public
  function StringConverter(Data: TObject; Field: string): string; override;
  procedure StringReverter(Data: TObject; Field: string; Arg: string); override;
end;
怎么转换,和上面的一样的了。我是觉得这个方法比上面的法子好。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多