1. 项目背景与需求解析
在Delphi开发现场,JSON数据处理一直是高频需求场景。System.JSON作为Delphi内置单元虽然功能完整,但原生API设计偏向底层,开发者常需要编写大量样板代码来完成基础操作。比如要获取一个嵌套JSON中的某个字段值,可能需要连续调用GetValue、TryGetValue等多层方法,还要处理各种异常情况。
我最近在维护一个电商后台项目时就深有体会:系统需要处理来自移动端、Web前端和第三方API的数十种JSON数据结构。每次解析新格式都要重复写一堆类型判断和异常处理,不仅效率低下,代码可读性也大打折扣。这种痛点催生了开发一个轻量级封装库的想法——在保持System.JSON可靠性的基础上,提供更符合直觉的链式调用接口。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心设计思路
2.1 架构原则
这个封装库的核心设计遵循三个原则:
- 零依赖:仅基于System.JSON单元,不引入第三方库
- 非侵入式:可与原生System.JSON对象自由转换
- 渐进式增强:基础用法简单,高级功能可扩展
库的主体结构采用门面模式(Facade),对外暴露简洁的TJsonHelper类,内部则通过组合方式复用System.JSON的功能。这种设计既避免了重新发明轮子,又能提供更友好的开发体验。
2.2 关键API设计对比
以解析下列订单JSON为例:
json复制{
"orderId": "20230615001",
"items": [
{
"productId": "P10086",
"quantity": 2
}
]
}
原生System.JSON写法:
delphi复制var
LJsonObj: TJSONObject;
LItemsArray: TJSONArray;
begin
LJsonObj := TJSONObject.ParseJSONValue(JsonStr) as TJSONObject;
try
if LJsonObj.TryGetValue('orderId', LOrderId) then
// 处理orderId...
LItemsArray := LJsonObj.GetValue('items') as TJSONArray;
for I := 0 to LItemsArray.Count - 1 do
begin
LItem := LItemsArray.Items[I] as TJSONObject;
// 处理每个item...
end;
finally
LJsonObj.Free;
end;
end;
封装后的写法:
delphi复制with TJsonHelper.Parse(JsonStr) do
try
OrderId := Path('orderId').AsString;
Items := Path('items').AsArray;
for Item in Items do
begin
ProductId := Item.Path('productId').AsString;
Quantity := Item.Path('quantity').AsInteger;
// 处理逻辑...
end;
finally
Free;
end;
3. 关键技术实现
3.1 链式调用实现
通过返回Self引用实现链式调用是核心创新点。库中定义了TJsonAccessor基类,关键代码如下:
delphi复制type
TJsonAccessor = class
protected
FJsonValue: TJSONValue;
public
function Path(const APath: string): TJsonAccessor; virtual;
function AsString(const ADefault: string = ''): string;
function AsInteger(const ADefault: Integer = 0): Integer;
// 其他类型转换方法...
end;
Path方法实现路径导航:
delphi复制function TJsonAccessor.Path(const APath: string): TJsonAccessor;
var
LNames: TArray<string>;
LCurrent: TJSONValue;
I: Integer;
begin
LNames := APath.Split(['.']);
LCurrent := FJsonValue;
for I := 0 to High(LNames) do
begin
if not (LCurrent is TJSONObject) then Break;
LCurrent := TJSONObject(LCurrent).GetValue(LNames[I]);
if LCurrent = nil then Break;
end;
Result := TJsonAccessor.Create;
Result.FJsonValue := LCurrent;
end;
3.2 内存管理优化
考虑到Delphi的手动内存管理特点,库实现了引用计数机制:
delphi复制type
TJsonHelper = class(TJsonAccessor, IInterface)
private
FRefCount: Integer;
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
class function Parse(const AJson: string): TJsonHelper; static;
end;
function TJsonHelper._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then Destroy;
end;
这样支持了ARC和非ARC环境下的自动释放:
delphi复制// 自动释放写法
TJsonHelper.Parse(JsonStr)
.Path('data.items[0].price').AsCurrency;
// 传统写法
var LHelper := TJsonHelper.Parse(JsonStr);
try
// 使用代码...
finally
LHelper.Free;
end;
4. 高级功能实现
4.1 JSON路径表达式
支持类XPath的路径语法:
user.name:嵌套对象访问items[0]:数组索引访问items[*].id:数组遍历
实现关键点在于路径解析器:
delphi复制procedure ParsePath(const APath: string);
var
LChar: Char;
LState: (InName, InIndex);
LBuffer: string;
begin
LState := InName;
for LChar in APath do
begin
case LState of
InName:
if LChar = '[' then
begin
if LBuffer <> '' then
AddNameNode(LBuffer);
LState := InIndex;
LBuffer := '';
end
else if LChar = '.' then
begin
AddNameNode(LBuffer);
LBuffer := '';
end
else
LBuffer := LBuffer + LChar;
InIndex:
if LChar = ']' then
begin
AddIndexNode(StrToIntDef(LBuffer, -1));
LState := InName;
LBuffer := '';
end
else
LBuffer := LBuffer + LChar;
end;
end;
if LBuffer <> '' then
if LState = InName then
AddNameNode(LBuffer)
else
AddIndexNode(StrToIntDef(LBuffer, -1));
end;
4.2 构建JSON数据
提供流畅的JSON构建接口:
delphi复制var
LJson: string;
begin
LJson := TJsonHelper.New
.Add('orderId', '20230615001')
.BeginArray('items')
.BeginObject
.Add('productId', 'P10086')
.Add('quantity', 2)
.EndObject
.EndArray
.ToString;
end;
对应的实现采用栈式设计:
delphi复制type
TJsonBuilder = class
private
FStack: TStack<TJSONAncestor>;
FCurrent: TJSONAncestor;
public
function BeginObject: TJsonBuilder;
function BeginArray(const AName: string): TJsonBuilder;
function Add(const AName: string; const AValue: Variant): TJsonBuilder;
function EndObject: TJsonBuilder;
function EndArray: TJsonBuilder;
end;
5. 性能优化技巧
5.1 内存池技术
频繁创建/销毁JSON对象会导致内存碎片,采用对象池优化:
delphi复制var
GJsonObjectPool: TObjectPool<TJSONObject>;
initialization
GJsonObjectPool := TObjectPool<TJSONObject>.Create(
function: TJSONObject
begin
Result := TJSONObject.Create;
end,
procedure(Obj: TJSONObject)
begin
Obj.Clear;
end);
5.2 字符串处理优化
避免频繁的字符串拼接:
delphi复制function TJsonHelper.ToString: string;
var
LBuilder: TStringBuilder;
begin
LBuilder := TStringBuilder.Create;
try
InternalToString(FJsonValue, LBuilder);
Result := LBuilder.ToString;
finally
LBuilder.Free;
end;
end;
6. 实际应用案例
6.1 REST API调用封装
delphi复制function GetOrderDetails(const AOrderId: string): TOrder;
begin
Result := TOrder.Create;
with TRestClient.Get('/orders/' + AOrderId) do
try
if StatusCode = 200 then
with TJsonHelper.Parse(Content) do
try
Result.OrderId := Path('id').AsString;
Result.Status := Path('status').AsEnum<TOrderStatus>;
// 其他字段...
finally
Free;
end;
finally
Free;
end;
end;
6.2 配置文件读写
delphi复制procedure LoadConfig;
begin
with TJsonHelper.LoadFromFile('config.json') do
try
Database.Host := Path('database.host').AsString;
Database.Port := Path('database.port').AsInteger;
// 其他配置...
finally
Free;
end;
end;
7. 异常处理策略
7.1 静默失败设计
对于可选字段提供默认值:
delphi复制function TJsonAccessor.AsString(const ADefault: string): string;
begin
if (FJsonValue = nil) or (FJsonValue is TJSONNull) then
Result := ADefault
else
Result := FJsonValue.Value;
end;
7.2 严格模式
通过Strict属性切换验证模式:
delphi复制if TJsonHelper.StrictMode then
begin
// 遇到缺失字段会抛出异常
UserName := Json.Path('user.name').AsString;
end
else
begin
// 返回空字符串
UserName := Json.Path('user.name').AsString('');
end;
8. 测试方案设计
8.1 单元测试覆盖
使用DUnitX框架编写测试用例:
delphi复制[TestFixture]
procedure TestTJsonHelper;
begin
[Test]
procedure TestPathNavigation;
var
LJson: TJsonHelper;
begin
LJson := TJsonHelper.Parse('{"user":{"name":"John"}}');
try
Assert.AreEqual('John', LJson.Path('user.name').AsString);
finally
LJson.Free;
end;
end;
end;
8.2 性能测试
对比原生System.JSON的解析速度:
delphi复制procedure Benchmark;
var
LStopwatch: TStopwatch;
I: Integer;
begin
LStopwatch := TStopwatch.StartNew;
for I := 1 to 10000 do
begin
// 测试代码...
end;
WriteLn(LStopwatch.ElapsedMilliseconds);
end;
9. 兼容性处理
9.1 跨版本支持
通过条件编译处理不同Delphi版本差异:
delphi复制{$IF CompilerVersion >= 32.0} // Delphi 10.4+
LDate := ISO8601ToDate(Path('createTime').AsString);
{$ELSE}
LDate := StrToDateTime(Path('createTime').AsString);
{$ENDIF}
9.2 移动平台适配
针对移动端优化内存使用:
delphi复制{$IFDEF IOS}
// iOS特殊处理
TJSONObject.UseBoolAttribute := True;
{$ENDIF}
10. 扩展设计思路
10.1 自定义类型转换
注册类型转换器:
delphi复制TJsonHelper.RegisterConverter<TDateTime>(
function(AValue: TJSONValue; out AResult: TDateTime): Boolean
begin
Result := TryISO8601ToDate(AValue.Value, AResult);
end);
10.2 流式处理支持
处理大型JSON文件:
delphi复制procedure ProcessLargeJson(AStream: TStream);
var
LReader: TJsonTextReader;
begin
LReader := TJsonTextReader.Create(AStream);
try
while LReader.Read do
begin
case LReader.TokenType of
TJsonToken.StartObject: // 处理对象开始...
// 其他token处理...
end;
end;
finally
LReader.Free;
end;
end;
在实现过程中发现,当JSON结构非常复杂时(超过5层嵌套),采用路径表达式反而会降低可读性。这时更推荐使用传统的对象映射方式,先定义好对应的DTO类,再通过反射机制自动填充数据。这个经验也促使我在后续版本中增加了对象映射功能作为高级特性。
