一步一步学习使用LiveBindings(7) 实现对JSON数据的绑定
本课将介绍如何从JSON中获取绑定数据源,并且将更新也写回JSON。可以设想一下有一台远端服务器提供JSON数据,Delphi客户端可以接收这些JSON数据,然后转换成数据绑定对象,在应用程序中处理完数据后,将更新的数据序列化为JSON传回远端服务器,很多移动应用使用了这种模式处理服务器端的数据。好了废话少说,开始打开Delphi 12.3,建项目吧。
本系列课程具有前后关联性,如果你对LiveBindings的诸多细节没有一个大的概念,请看一步一步学习使用LiveBindings的前几课。
1. 单击主菜单中的 File > New > Multi-Device Application - Delphi > Blank Application ,创建一个新的多设备应用程序。
建议立即单击工具栏上的Save All按钮,将单元文件保存为uMainForm.pas,将项目保存为LiveBinding_BindToJSON.dproj。
你的项目结构应该像这样:
首先新建一个名为CollectionObjects.pas的Unit,右键单击Project Manager中的项目名称,选择“Add New > Unit”,保存为CollectionObjects.pas文件名,CollectionObjects.pas包含一个简单的TPerson类,你可以想象为一个业务实体,代码如下:- //
- unit CollectionObjects;
- interface
- type
- TPerson = class
- private
- FAge: Integer;
- FLastName: string;
- FFirstName: string;
- public
- constructor Create(const FirstName, LastName: String; Age: Integer);
- property FirstName: string read FFirstName write FFirstName;
- property LastName: string read FLastName write FLastName;
- property Age: Integer read FAge write FAge;
- end;
- implementation
- { TPerson }
- constructor TPerson.Create(const FirstName, LastName: String; Age: Integer);
- begin
- FFirstName := FirstName;
- FLastName := LastName;
- FAge := Age;
- end;
- end.
复制代码 代码过于简洁,无须过多介绍。
2. 在主窗体上,放一个TTabControl控件,为该控件添加2个TabItem,一个指定Text为"Grid",一个指定Text为“JSON”,这个名为JSON的Tab页用来演示后台的JSON数据的变化,用户将可以编辑这个JSON,同时在Grid上看到更新。
在名为TabGrid的TabItem上,放置一个TGrid和一个TBindNavigator控件,在名为TabJSON的TabItem上,放置一个TMemo控件用来显示JSON内容。
最后放置一个TDataGeneratorAdapter和一个TAdapterBindSource,指定AdapterBindSource1的Adapter为DataGeneratorAdapter1,BindNavigator1的DataSource属性为AdapterBindSource1。
设计窗口如下图所示:
3. 接下来需要完成绑定工作,目前AdapterBindSource1虽然指向了DataGeneratorAdapter1,但是DataGeneratorAdapter1还没有设置字段和相应的数据生成器。现在右击DataGeneratorAdapter1,从弹出的菜单中选择“Fields Editor”菜单项,根据在CollectionObjects.pas单元中定义的TPerson类的属性来创建3个字段,并分别指定如下图所示的数据生成器。
4. 在TGrid上右击鼠标,从弹出的菜单中选择“Bind Visually”,在LiveBinding Designer中,将AdapterBindSource1的字段分别拖放到MainGrid上,可以看到生成的数据会立即显示在Grid上。
注意:拖动单独的列到Grid,可以单独调整Grid列的属性。
到目前为止,就构建了一个具有样例数据的应用程序。
5. 由于示例没有真正的访问远端服务器,为了演示数据绑定的效果,接下来实现AdapterBindSource1的OnCreateAdapter事件,在该事件中添加测试数据,以便在Grid上可以看到“真实”的数据。
首先在Interface区的uses下面添加类引用,并在private区添加一个泛型集合类。- unit uMainForm;
- interface
- uses
- System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
- FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.TabControl,
- Data.Bind.Controls, System.Rtti, FMX.Grid.Style, FMX.Memo.Types,
- Data.Bind.Components, Data.Bind.ObjectScope, FMX.Memo,
- FMX.Controls.Presentation, FMX.ScrollBox, FMX.Grid, FMX.Layouts,
- Fmx.Bind.Navigator, Data.Bind.GenData, Data.Bind.EngExt, Fmx.Bind.DBEngExt,
- System.Bindings.Outputs, Data.Bind.Grid, Fmx.Bind.Grid, Fmx.Bind.Editors,
- //添加如下的单元引用
- CollectionObjects,System.Generics.Collections,REST.Json,System.JSON;
- type
- TfrmMain = class(TForm)
- Tab: TTabControl;
- tabGrid: TTabItem;
- tabJSON: TTabItem;
- NavigatorAdapterBindSource1: TBindNavigator;
- mmJSON: TMemo;
- DataGeneratorAdapter1: TDataGeneratorAdapter;
- AdapterBindSource1: TAdapterBindSource;
- grdMain: TGrid;
- BindingsList1: TBindingsList;
- LinkGridToDataSourceAdapterBindSource1: TLinkGridToDataSource;
- private
- { Private declarations }
- //添加保存人员信息的泛型列表类
- FMyPeople: TObjectList<TPerson>;
- public
- { Public declarations }
- end;
复制代码 在单元引用区,可以看到添加了System.JSON和REST.JSON引用,它们将用来处理JSON的解析与生成。
接下来在AdapterBindSource1的OnCreateAdapter事件中添加一些模拟的人员数据,如下代码所示:- procedure TfrmMain.AdapterBindSource1CreateAdapter(Sender: TObject;
- var ABindSourceAdapter: TBindSourceAdapter);
- begin
- //用来保存人员数据的集合。
- FMyPeople := TObjectList<TPerson>.Create(True);
- //添加单个的人员信息
- FMyPeople.Add(TPerson.Create('Gomez', 'Addams', 40));
- FMyPeople.Add(TPerson.Create('Morticia', 'Addams', 38));
- FMyPeople.Add(TPerson.Create('Pugsley', 'Addams', 8));
- FMyPeople.Add(TPerson.Create('Wednesday', 'Addams', 12));
- FMyPeople.Add(TPerson.Create('Uncle', 'Fester', 55));
- FMyPeople.Add(TPerson.Create('Grandmama', 'Frump', 72));
- FMyPeople.Add(TPerson.Create('', 'Lurch', 50));
- FMyPeople.Add(TPerson.Create('Thing T.', 'Thing', 99));
- FMyPeople.Add(TPerson.Create('Cousin', 'Itt', 21));
- // 使用TListBindSourceAdapter绑定到集合数据。
- ABindSourceAdapter := TListBindSourceAdapter<TPerson>.Create(Self, FMyPeople, True);
- end;
复制代码 现在运行示例,可以看到这些数据已经显示在了Grid上,可以进行上下移动编辑了。
6. 搞定了数据绑定的问题,现在真正要解决的问题是将TObjectList类型的泛型集合转换成JOSN字符串发送给服务器,或者是在接收到JSON字符串后,转换为TObjectList类型的泛型集合以更新绑定UI,在这里需要引入2个过程。
在private区域定义2个过程,用来分别将对象转换为JSON以及将JSON转换为对象。- private
- { Private declarations }
- //添加保存人员信息的泛型列表类
- FMyPeople: TObjectList<TPerson>;
- //将对象转换为JSON数据
- procedure ObjectsToJson;
- //将JSON转换为对象
- procedure JsonToObjects;
- Implementation
- procedure TfrmMain.JsonToObjects;
- begin
- //TODO 将TMemo中的JSON字符串转换为对象
- end;
- procedure TfrmMain.ObjectsToJson;
- begin
- //TODO 将TGrid绑定的对象转换为JSON字符串显示在TMemo中。
- end;
复制代码 由于ObjectsToJson和JsonToObjects涉及到一些JSON相关的操作,咱们需要先想想如何实现,但是UI的布局应该是当Tab切换时,如果切换到Grid这个Tab页,则调用JsonToObjects更新Grid上的数据;如果切换到JSON这个Tab页,则调用ObjectsToJson将Grid上的更新序列化为JSON字符串,所以应该是这样的一个效果:
可以看到,JSON与Grid是同步的,两边都可以更改。
7. 在主窗体中选中TTabControl控件,在属性编辑器中切换到Event标签页,找到OnChange事件,添加如下的代码:- procedure TfrmMain.TabChange(Sender: TObject);
- begin
- //如果当前页面是JSON页
- if Tab.ActiveTab = tabJSON then
- begin
- //如果AdapterBindSource处于编辑模式,则提交。
- if AdapterBindSource1.Editing then
- AdapterBindSource1.Post;
- ObjectsToJson; //将对象转换为JSON。
- end
- else if Tab.ActiveTab = tabGrid then
- JsonToObjects; //反之将JSON转换为对象。
- end;
复制代码 8. 在开始这2个核心的过程之前,良好的可重用的代码设计就显得很重要。System.JSON单元封装了JSON对象操作的逻辑,REST.JSON则提供了单一JSON字符串转换为对象或者对象到JSON字符串的转换功能。在这里封装了一个名为TUtils的类,它包含2个类方法:- type
- TUtils = class
- public
- //将一个泛型列表对象转换为JSON数组
- class function ObjectListToJSON<T: class>(const AObjects: TObjectList<T>): TJSONArray;
- //将一个JSON数组转换为泛型列表对象
- class function JsonToObjectList<T: class, constructor>(const AText: string): TObjectList<T>;
- end;
- { TUtils }
- class function TUtils.JsonToObjectList<T>(const AText: string): TObjectList<T>;
- var
- LObject: T;
- LArray: TJSONArray;
- LValue: TJSONValue;
- LList: TObjectList<T>;
- begin
- LList := TObjectList<T>.Create;
- LArray := nil;
- try
- LArray := TJSONObject.ParseJSONValue(AText) as TJSONArray;
- if LArray = nil then
- raise Exception.Create('Invalid JSON');
- for LValue in LArray do
- if LValue is TJSONObject then
- begin
- //使用REST.Json提供的TJson类的类方法完成转换
- LObject := TJson.JsonToObject<T>(TJSONObject(LValue));
- LList.Add(LObject);
- end;
- Result := LList;
- LList := nil;
- finally
- LArray.Free;
- LList.Free;
- end;
- end;
- class function TUtils.ObjectListToJSON<T>(
- const AObjects: TObjectList<T>): TJSONArray;
- var
- LObject: T;
- LArray: TJSONArray;
- LValue: TPerson;
- LElement: TJSONObject;
- begin
- LArray := TJSONArray.Create;
- try
- for LObject in AObjects do
- begin
- //使用REST.Json提供的TJson类的类方法完成转换
- LElement := TJson.ObjectToJsonObject(LObject);
- LArray.AddElement(LElement);
- end;
- Result := LArray;
- LArray := nil;
- finally
- LArray.Free;
- end;
- end;
复制代码 代码中的TJSONArray,TJSONObject类是由System.Json的供来操纵JSON的,核心部分的TJson类是由REST.Json所提供,顾名思议,这个单元是处理Restful操作的。
9. 现在一切准备就绪,继续完成ObjectsToJson和JsonToObjects这两个过程,代码如下所示:- procedure TfrmMain.JsonToObjects;
- begin
- //使用JsonToObjectList将JSON转换为对象
- fMyPeople := TUtils.JsonToObjectList<TPerson>(mmJSON.Text);
- //将列表数据重新赋给AdapterBindSource1。
- TListBindSourceAdapter<TPerson>(AdapterBindSource1.InternalAdapter).SetList(fMyPeople);
- //刷新用户界面
- AdapterBindSource1.Active := True;
- end;
- procedure TfrmMain.ObjectsToJson;
- var
- LArray: TJSONArray;
- begin
- //将对象转换为TJSONArray数组
- LArray := TUtils.ObjectListToJSON<TPerson>(fMyPeople);
- try
- //将JSON数组稍稍美经后显示在TMemo控件
- mmJSON.Text := PrettyJSON(LArray.ToString);
- finally
- LArray.Free;
- end;
- end;
- function PrettyJSON(AJson: String): String;
- begin
- Result := StringReplace(AJson, '},', '},' + sLineBreak, [rfReplaceAll]);
- Result := StringReplace(Result, '[{', '[' + sLineBreak + '{', [rfReplaceAll]);
- end;
复制代码 10. 万事皆备,只欠一Run了,按下F9,或者是主菜单的“Run > Run”,可以看到JOSN和Grid的数据果然已经同步了。
真实的项目中,JSON生成后,应该是要发送给Server端,或者存储到本地文件,这可以根据需要而定。
本课就讲到这里了,虽然到目前为止笔者还没有深挖TBindingList的内幕,不过可以看到使用LiveBindings Designer已经可以解决不少问题了。当然在实际的项目中还是有很多细节要处理的,比如显式格式的转换,复杂的绑定场景等等。
在本系列的后面的课程中会继续深挖。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |