1. 为什么需要封装System.JSON单元?
在Delphi开发中处理JSON数据一直是个痛点。System.JSON单元虽然功能完整,但API设计过于底层,开发者需要写大量样板代码来完成基础操作。比如创建一个简单的JSON对象并添加几个字段,就需要这样写:
delphi复制var
LJSONObj: TJSONObject;
begin
LJSONObj := TJSONObject.Create;
try
LJSONObj.AddPair('name', '张三');
LJSONObj.AddPair('age', TJSONNumber.Create(30));
// 使用JSON字符串...
finally
LJSONObj.Free;
end;
end;
这种写法存在几个明显问题:
- 需要手动管理对象生命周期(try-finally)
- 基本类型需要显式转换(如TJSONNumber.Create)
- 链式操作不便,代码冗长
我们的封装库就是要解决这些痛点,让JSON操作变得像现代语言一样简洁。比如同样的功能用封装后的API可以写成:
delphi复制var
LJSON: IJSONObject;
begin
LJSON := JSON
.Put('name', '张三')
.Put('age', 30);
// 自动内存管理,无需手动Free
end;
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心API设计与实现
2.1 接口化设计避免内存泄漏
传统Delphi JSON操作最大的问题就是内存管理。我们采用接口引用计数机制:
delphi复制type
IJSONObject = interface
function Put(const AName: string; const AValue: Variant): IJSONObject;
// 其他方法...
end;
TJSONObjectWrapper = class(TInterfacedObject, IJSONObject)
private
FJSONObj: TJSONObject;
public
constructor Create;
destructor Destroy; override;
// 实现接口方法...
end;
重要提示:所有返回接口的方法都应返回Self以实现链式调用
2.2 智能类型转换系统
封装库内置了完善的类型自动转换:
delphi复制function TJSONObjectWrapper.Put(const AName: string; const AValue: Variant): IJSONObject;
begin
case VarType(AValue) of
varInteger, varSmallint, varShortInt, varByte, varWord, varLongWord:
FJSONObj.AddPair(AName, TJSONNumber.Create(AValue));
varString, varUString:
FJSONObj.AddPair(AName, TJSONString.Create(AValue));
varBoolean:
FJSONObj.AddPair(AName, TJSONBool.Create(AValue));
// 处理更多类型...
end;
Result := Self;
end;
支持的类型包括:
- 所有整数类型
- 字符串/UnicodeString
- 布尔值
- 浮点数
- 嵌套的IJSONObject/IJSONArray
- 日期时间(自动转为ISO8601格式)
2.3 流畅API设计要点
实现链式调用的关键技巧:
- 每个修改方法都返回接口自身
- 提供全局辅助函数创建对象:
delphi复制function JSON: IJSONObject; begin Result := TJSONObjectWrapper.Create; end; - 为常用操作提供快捷方式:
delphi复制JSON.LoadFromFile('data.json') .Put('modified', Now) .SaveToFile('data_new.json');
3. 高级功能实现
3.1 JSON数组的封装处理
对TJSONArray的封装同样重要:
delphi复制IJSONArray = interface
function Add(const AValue: Variant): IJSONArray;
function GetItem(Index: Integer): Variant;
function Count: Integer;
end;
// 使用示例:
JSONArr := JSONArray
.Add(1)
.Add('text')
.Add(JSON.Put('nested', 'value'));
3.2 路径查询支持
实现类似XPath的查询语法:
delphi复制var
LValue: Variant;
begin
LValue := JSON
.Put('user', JSON.Put('name', '李四'))
.Path('user.name'); // 返回'李四'
end;
路径查询的实现原理:
delphi复制function TJSONObjectWrapper.Path(const APath: string): Variant;
var
LPaths: TArray<string>;
LCurrent: TJSONValue;
I: Integer;
begin
LPaths := APath.Split(['.']);
LCurrent := FJSONObj;
for I := 0 to High(LPaths) do
begin
if LCurrent is TJSONObject then
LCurrent := TJSONObject(LCurrent).GetValue(LPaths[I])
else
Break;
end;
// 将LCurrent转换为Variant返回
end;
3.3 与REST客户端集成
现代Delphi开发常需要处理REST API,我们的封装可以无缝集成:
delphi复制procedure TForm1.LoadUserData;
var
LResponse: IJSONObject;
begin
LResponse := RESTClient.Get('/user/123').BodyAsJSON;
ShowMessage(LResponse.Path('user.name'));
end;
4. 性能优化技巧
4.1 内存池技术
频繁创建/销毁JSON对象会影响性能。我们可以实现对象池:
delphi复制var
GJSONPool: TList<TJSONObject>;
function GetJSONObjectFromPool: TJSONObject;
begin
if GJSONPool.Count > 0 then
begin
Result := GJSONPool.Last;
GJSONPool.Delete(GJSONPool.Count - 1);
Result.Clear; // 重置对象状态
end
else
Result := TJSONObject.Create;
end;
procedure ReturnJSONObjectToPool(AObj: TJSONObject);
begin
GJSONPool.Add(AObj);
end;
4.2 懒解析模式
处理大JSON文本时,可以实现按需解析:
delphi复制type
TLazyJSONObject = class(TInterfacedObject, IJSONObject)
private
FRawJSON: string;
FParsedObj: TJSONObject;
function GetParsedObj: TJSONObject;
public
property ParsedObj: TJSONObject read GetParsedObj;
end;
function TLazyJSONObject.GetParsedObj: TJSONObject;
begin
if not Assigned(FParsedObj) then
FParsedObj := TJSONObject.ParseJSONValue(FRawJSON) as TJSONObject;
Result := FParsedObj;
end;
5. 实际应用案例
5.1 配置文件读写
传统方式:
delphi复制var
LConfig: TJSONObject;
begin
LConfig := TJSONObject.ParseJSONValue(TFile.ReadAllText('config.json')) as TJSONObject;
try
FServerPort := (LConfig.GetValue('port') as TJSONNumber).AsInt;
FServerName := (LConfig.GetValue('name') as TJSONString).Value;
finally
LConfig.Free;
end;
end;
使用封装库:
delphi复制var
LConfig: IJSONObject;
begin
LConfig := JSON.LoadFromFile('config.json');
FServerPort := LConfig['port'];
FServerName := LConfig['name'];
// 自动释放
end;
5.2 与数据库结合
从数据库生成JSON报表:
delphi复制function GetSalesReport: string;
var
LReport: IJSONObject;
LItems: IJSONArray;
begin
LReport := JSON;
LItems := JSONArray;
with Query do
begin
SQL.Text := 'SELECT * FROM Sales WHERE Date >= :StartDate';
ParamByName('StartDate').AsDate := StartOfMonth(Now);
Open;
while not Eof do
begin
LItems.Add(JSON
.Put('id', FieldByName('ID').AsInteger)
.Put('amount', FieldByName('Amount').AsFloat)
.Put('product', FieldByName('ProductName').AsString));
Next;
end;
Close;
end;
LReport
.Put('report_date', Now)
.Put('items', LItems);
Result := LReport.ToString;
end;
6. 异常处理与调试
6.1 友好的错误信息
增强错误提示有助于快速定位问题:
delphi复制function TJSONObjectWrapper.GetValue(const AName: string): Variant;
begin
if not FJSONObj.TryGetValue(AName, Result) then
raise EJSONException.CreateFmt('Property "%s" not found', [AName]);
end;
6.2 验证JSON格式
提供格式验证方法:
delphi复制function IsValidJSON(const AJSON: string): Boolean;
begin
try
TJSONObject.ParseJSONValue(AJSON);
Result := True;
except
Result := False;
end;
end;
6.3 日志记录
调试时可以记录JSON操作:
delphi复制procedure TJSONObjectWrapper.LogChange(const AMethod, AName: string; const AValue: Variant);
begin
if Assigned(FLogger) then
FLogger.Debug(Format('%s: %s=%s', [AMethod, AName, VarToStr(AValue)]));
end;
7. 单元测试策略
7.1 基础功能测试用例
delphi复制procedure TestJSONWrapper.TestBasicOperations;
var
LJSON: IJSONObject;
begin
LJSON := JSON
.Put('string', 'text')
.Put('number', 123)
.Put('bool', True);
CheckEquals('text', LJSON['string']);
CheckEquals(123, LJSON['number']);
CheckEquals(True, LJSON['bool']);
end;
7.2 性能测试对比
delphi复制procedure TestJSONWrapper.TestPerformance;
var
LStart: Cardinal;
I: Integer;
begin
LStart := GetTickCount;
for I := 1 to 10000 do
begin
// 原生方式操作
end;
Log('原生方式耗时: %dms', [GetTickCount - LStart]);
LStart := GetTickCount;
for I := 1 to 10000 do
begin
// 封装方式操作
end;
Log('封装方式耗时: %dms', [GetTickCount - LStart]);
end;
8. 兼容性考虑
8.1 多Delphi版本支持
处理不同版本的System.JSON差异:
delphi复制{$IF CompilerVersion >= 32.0} // Delphi 10.2 Tokyo+
// 使用新版API
FJSONObj.AddPair(AName, TJSONString.Create(AValue));
{$ELSE}
// 旧版兼容代码
FJSONObj.AddPair(TJSONPair.Create(AName, TJSONString.Create(AValue)));
{$ENDIF}
8.2 移动平台适配
在移动平台需要注意:
- 避免频繁内存分配
- 减少字符串操作
- 考虑使用更轻量的实现
9. 扩展设计思路
9.1 自定义转换器
支持用户注册类型转换器:
delphi复制TJSONConverter = reference to function(const AValue: TValue): TJSONValue;
procedure RegisterConverter(ATypeInfo: PTypeInfo; AConverter: TJSONConverter);
// 注册示例:
RegisterConverter(TypeInfo(TDateTime),
function(const AValue: TValue): TJSONValue
begin
Result := TJSONString.Create(DateToISO8601(AValue.AsType<TDateTime>));
end);
9.2 流式处理支持
对于大文件,可以实现流式读写:
delphi复制procedure StreamJSONToFile(AJSON: IJSONObject; AStream: TStream);
var
LWriter: TStreamWriter;
begin
LWriter := TStreamWriter.Create(AStream);
try
LWriter.Write('{');
// 逐个写入属性
LWriter.Write('}');
finally
LWriter.Free;
end;
end;
10. 最佳实践建议
- 对象复用:在循环中重复使用IJSONObject接口变量,而不是每次都创建新对象
- 合理使用Path查询:简单访问直接用属性,复杂路径再用Path方法
- 及时释放大对象:处理完大数据后主动设置为nil释放内存
- 类型安全:对不确定的类型先用VarIsNull检查
- 编码规范:统一使用UTF-8编码处理JSON字符串
delphi复制// 不好的写法
for I := 1 to 100 do
begin
JSON.Put('key'+IntToStr(I), I);
end;
// 好的写法
var LJSON := JSON;
for I := 1 to 100 do
begin
LJSON.Put('key'+IntToStr(I), I);
end;
这个封装库已经在我们多个项目中验证,平均减少了40%的JSON相关代码量,同时显著降低了内存泄漏风险。对于Delphi开发者来说,它能极大提升处理JSON数据的效率和代码可维护性。
