1. 项目概述:Delphi JSON封装库的设计初衷
在Delphi开发中,处理JSON数据一直是个既基础又麻烦的工作。虽然System.JSON单元提供了基础功能,但原生API用起来实在不够优雅。每次都要写一堆GetValue、TryGetValue,处理嵌套结构时更是要层层判断,代码又长又难维护。
这个封装库就是为了解决这些痛点而生的。它基于Delphi原生的System.JSON单元构建,但提供了更符合人类思维的链式调用接口。举个例子,原来要取个深层值得这么写:
delphi复制var
JsonObj: TJSONObject;
begin
JsonObj := TJSONObject.ParseJSONValue(JsonString) as TJSONObject;
if Assigned(JsonObj) then
try
if JsonObj.TryGetValue('user', userObj) then
if userObj.TryGetValue('address', addressObj) then
if addressObj.TryGetValue('city', cityValue) then
ShowMessage(cityValue.Value);
finally
JsonObj.Free;
end;
end;
用了封装库后,一行搞定:
delphi复制ShowMessage(TJsonHelper.Parse(JsonString).Path('user.address.city').AsString);
2. 核心功能设计
2.1 链式调用接口
这个库最核心的设计理念就是链式调用(Fluent Interface)。我们封装了一个TJsonHelper类,所有方法都返回自身实例,这样就可以像搭积木一样把操作串联起来。
关键实现要点:
- 每个方法都返回Self,保持链式调用
- 内部维护一个FCurrentValue指针,指向当前操作的JSON节点
- 每次操作后自动更新FCurrentValue
- 支持nil安全,任何一步出错都不会报错而是返回空值
典型用法示例:
delphi复制TJsonHelper
.Parse(JsonString) // 解析JSON字符串
.Path('user.address') // 导航到address节点
.Field('city').AsString; // 获取city字段值
2.2 智能类型转换
原生System.JSON需要手动处理类型转换,这个封装库内置了智能转换逻辑:
delphi复制// 自动处理各种类型转换
.AsString // 转为字符串
.AsInteger // 转为整数
.AsBoolean // 转为布尔值
.AsDouble // 转为浮点数
.AsDateTime // 转为日期时间(支持ISO8601格式)
.AsArray // 转为数组迭代器
特别的是.AsDateTime实现:
delphi复制function TJsonHelper.AsDateTime: TDateTime;
begin
if FCurrentValue is TJSONString then
Result := ISO8601ToDate(FCurrentValue.Value)
else if FCurrentValue is TJSONNumber then
Result := UnixToDateTime(FCurrentValue.AsInt64)
else
Result := 0;
end;
2.3 路径导航功能
.Path()方法支持类似XPath的导航语法:
delphi复制// 基本路径导航
.Path('user.name')
// 数组索引支持
.Path('items[0].price')
// 通配符搜索(返回第一个匹配项)
.Path('orders.*.product')
内部实现用了递归解析:
delphi复制function TJsonHelper.ParsePath(const APath: string): TJSONValue;
var
Segments: TArray<string>;
I: Integer;
begin
Segments := APath.Split(['.']);
Result := FRootValue;
for I := 0 to High(Segments) do
begin
if Result = nil then Break;
Result := ResolveSegment(Result, Segments[I]);
end;
Exit(Result);
end;
3. 高级功能实现
3.1 构建JSON数据
除了解析,封装库也提供了流畅的JSON构建接口:
delphi复制// 构建复杂JSON结构
TJsonHelper
.CreateObject
.Add('name', '张三')
.Add('age', 30)
.Add('address',
TJsonHelper.CreateObject
.Add('city', '北京')
.Add('street', '朝阳区')
)
.Add('hobbies',
TJsonHelper.CreateArray
.Add('游泳')
.Add('读书')
)
.ToString;
生成结果:
json复制{
"name": "张三",
"age": 30,
"address": {
"city": "北京",
"street": "朝阳区"
},
"hobbies": ["游泳", "读书"]
}
3.2 数组遍历
处理JSON数组时,封装库提供了更优雅的迭代方式:
delphi复制// 传统方式
var
Arr: TJSONArray;
I: Integer;
begin
Arr := JsonObj.GetValue('items') as TJSONArray;
for I := 0 to Arr.Count - 1 do
ProcessItem(Arr.Items[I]);
end;
// 封装库方式
TJsonHelper.Parse(JsonString)
.Path('items')
.AsArray
.ForEach(
procedure(Item: TJsonHelper)
begin
ProcessItem(Item);
end
);
3.3 条件查询
.where()方法实现了简单的条件过滤:
delphi复制// 找出所有价格大于100的商品
TJsonHelper.Parse(JsonString)
.Path('products')
.AsArray
.Where(
function(Item: TJsonHelper): Boolean
begin
Result := Item.Path('price').AsDouble > 100;
end
)
.ForEach(ProcessProduct);
4. 性能优化技巧
4.1 缓存机制
频繁解析相同JSON字符串会影响性能,封装库提供了缓存功能:
delphi复制// 启用缓存(默认关闭)
TJsonHelper.EnableCache := True;
// 后续解析会自动缓存结果
var Helper1 := TJsonHelper.Parse(JsonString); // 解析并缓存
var Helper2 := TJsonHelper.Parse(JsonString); // 直接返回缓存实例
缓存实现关键点:
delphi复制class function TJsonHelper.Parse(const AJson: string): TJsonHelper;
var
CachedObj: TJSONValue;
begin
if FCacheEnabled and FCache.TryGetValue(AJson, CachedObj) then
Exit(TJsonHelper.Create(CachedObj.Clone as TJSONValue));
Result := Create(TJSONObject.ParseJSONValue(AJson));
if FCacheEnabled then
FCache.AddOrSetValue(AJson, Result.FRootValue.Clone);
end;
4.2 内存管理
原生System.JSON需要手动管理内存,封装库通过接口引用计数自动释放:
delphi复制type
TJsonHelper = class(TInterfacedObject, IJsonHelper)
private
FRootValue: TJSONValue;
FCurrentValue: TJSONValue;
public
destructor Destroy; override;
end;
// 使用接口自动管理生命周期
var
Helper: IJsonHelper;
begin
Helper := TJsonHelper.Parse(JsonString);
// 不需要手动Free,退出作用域自动释放
end;
4.3 流式处理
对于大JSON文件,建议使用TJSONReader进行流式处理:
delphi复制// 处理大JSON文件
var
Stream: TFileStream;
Reader: TJsonTextReader;
begin
Stream := TFileStream.Create('large.json', fmOpenRead);
try
Reader := TJsonTextReader.Create(Stream);
try
while Reader.Read do
begin
if (Reader.TokenType = TJsonToken.PropertyName) and
(Reader.Value.AsString = 'targetField') then
begin
Reader.Read;
ProcessValue(Reader.Value);
end;
end;
finally
Reader.Free;
end;
finally
Stream.Free;
end;
end;
5. 实战应用案例
5.1 配置文件读写
传统INI文件改用JSON配置:
delphi复制// 读取配置
AppConfig := TJsonHelper
.LoadFromFile('config.json')
.Path('settings');
Timeout := AppConfig.Field('timeout').AsInteger(30); // 默认值30
LogLevel := AppConfig.Field('log.level').AsString('info');
// 修改并保存配置
TJsonHelper
.LoadFromFile('config.json')
.Path('settings')
.Field('timeout').SetValue(60)
.Field('log.level').SetValue('debug')
.SaveToFile('config.json');
5.2 REST API交互
处理HTTP API返回的JSON数据:
delphi复制// 获取GitHub用户信息
var
HttpClient: THTTPClient;
Response: IHTTPResponse;
begin
HttpClient := THTTPClient.Create;
try
Response := HttpClient.Get('https://api.github.com/users/delphi');
if Response.StatusCode = 200 then
begin
ShowMessage(
TJsonHelper.Parse(Response.ContentAsString)
.Field('name').AsString + #13#10 +
TJsonHelper.Parse(Response.ContentAsString)
.Field('public_repos').AsString
);
end;
finally
HttpClient.Free;
end;
end;
5.3 数据库JSON字段处理
处理FireDAC中的JSON字段:
delphi复制// 查询并处理JSON字段
Query.SQL.Text := 'SELECT id, json_data FROM products';
Query.Open;
while not Query.Eof do
begin
var Product := TJsonHelper.Parse(Query.FieldByName('json_data').AsString);
ProcessProduct(
Query.FieldByName('id').AsInteger,
Product.Field('name').AsString,
Product.Field('price').AsDouble
);
Query.Next;
end;
6. 常见问题解决方案
6.1 日期时间处理
不同系统返回的日期格式可能不同,封装库做了兼容处理:
delphi复制// 支持多种日期格式
.AsDateTime; // 自动识别以下格式:
// "2023-01-01T12:00:00Z" (ISO8601)
// "2023-01-01 12:00:00"
// 1672531200 (Unix时间戳)
// 设置自定义日期格式
TJsonHelper.DateFormat := 'yyyy/mm/dd hh:nn:ss';
6.2 空值处理
安全地处理可能不存在的字段:
delphi复制// 传统方式
if JsonObj.TryGetValue('optionalField', Value) then
DoSomething(Value);
// 封装库方式
DoSomething(
TJsonHelper.Parse(JsonString)
.Field('optionalField')
.AsString('default value') // 提供默认值
);
6.3 性能调优
当处理大量JSON数据时,可以考虑以下优化:
- 禁用格式校验(仅对可信数据源)
delphi复制TJsonHelper.StrictMode := False; // 跳过完整JSON验证
- 预解析重用
delphi复制// 预解析并复用
var Helper := TJsonHelper.Parse(LargeJsonString);
for var I := 1 to 100 do
begin
Helper.Path('data.items[' + I.ToString + ']').ProcessItem;
end;
- 使用TJSONReader处理超大文件(见4.3节)
7. 扩展设计思路
7.1 自定义转换器
支持注册自定义类型转换器:
delphi复制// 注册枚举类型转换器
TJsonHelper.RegisterConverter<TMyEnum>(
function(Value: TJSONValue): TMyEnum
begin
Result := TMyEnum(GetEnumValue(TypeInfo(TMyEnum), Value.Value));
end,
function(Value: TMyEnum): TJSONValue
begin
Result := TJSONString.Create(GetEnumName(TypeInfo(TMyEnum), Ord(Value)));
end
);
// 使用自定义类型
var MyEnum := TJsonHelper.Parse(JsonString).Field('enumField').AsType<TMyEnum>;
7.2 动态代理模式
通过RTTI实现对象-JSON自动映射:
delphi复制type
TUser = class
private
FName: string;
FAge: Integer;
public
property Name: string read FName write FName;
property Age: Integer read FAge write FAge;
end;
// 自动转换
var
User: TUser;
begin
User := TJsonHelper
.Parse('{"name":"张三","age":30}')
.ToObject<TUser>;
end;
7.3 JSON Schema验证
集成JSON Schema验证支持:
delphi复制// 定义Schema
const UserSchema = '{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name"]
}';
// 验证JSON
if not TJsonHelper.Parse(JsonString).Validate(UserSchema) then
raise EJsonValidationError.Create('Invalid user data');
这个Delphi JSON封装库的设计充分考虑了实际开发中的各种需求场景,从简单的数据存取到复杂的业务逻辑处理都能优雅应对。它的链式调用接口让代码更简洁,智能类型转换减少了大量样板代码,而高级功能如条件查询、自定义转换等又为复杂场景提供了强大支持。