前面我定义了
type
TPerson = class
FirstName: string;
LastName: string;
Age: Integer;
end;
TPersonArray = array of TPerson;
然后写了服务方法function TServerMethods1.GetPersons: TJSONArray;这样写没问题,如果直接写成
function TServerMethods1.GetPersons: TPersonArray;是不可以的,因为TPersonArray不是类,它的TTypeInfo的TypeKind不是tkClass而是tkDynArray,目前是不datasnap支持的。
于是可以写一个集合类
TPersonCollection = class
private
FPersons: TArray;
public
destructor Destroy; override; //别忘了free掉元素
property Persons: TArray read FPersons write FPersons;
end;
然后写服务方法function TServerMethods1.GetPersons: TPersonCollection;这样就可以了,服务器端可以直接写,客户端可以直接读,不用自己去marshal和unmarshal了。
那是不是说,我们的Field只要封到类里就一定ok呢,不是的。
将上面的类修改为如下
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封装后,用法也很简单。
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;
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;
简单的说,就是定义转换规则后,加给TJSONConverters,让TJSONConverters知道要这么转换来转换回去。
至于TStringList的转换,道理明白了,所以也一样可以写了。Delphi2010的DataSnap可以看这里。
转换的一些普通的函数,EMBT已经提供得有。在Data.DBXJSONReflect单元里,能找到///除了这个方法,XE2里面另外还用到了描述类,来自动告诉JSON怎么转换。Converts a TStringList into a TSerStringList function StringListConverter(Data: TObject): TObject; ///Reverts a TSerStringList into a TStringList 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);
将上面的TPerson修改为
TPerson = class
FirstName: string;
LastName: string;
Age: Integer;
[JSONReflect(ctString, rtString, TDateTimeInterceptor, nil, True)]
Birthday: TDateTime;
Memo: TStringList;
end;
给Birthday增加描述,JSONReflect是个描述类,继承自TCustomAttribute。看JSONReflect的代码
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个执行转换的类。由于我们只是要转字符串,所以只覆盖字符的转换就行。
TDateTimeInterceptor = class(TJSONInterceptor)
public
function StringConverter(Data: TObject; Field: string): string; override;
procedure StringReverter(Data: TObject; Field: string; Arg: string); override;
end;
怎么转换,和上面的一样的了。我是觉得这个方法比上面的法子好。
没有评论:
发表评论