Delphi 12中如何正确将JSON反序列化为TJsonObject?
Delphi中TJson.JsonToObject反序列化TJSONObject字段为空的解决方法
问题背景
使用TJson.JsonToObject将JSON响应反序列化为自定义Delphi类时,除TErrorAdditionalInfo.FInfo(TJSONObject类型)字段始终为空外,其他字段均能正常解析。相关定义如下:
数据结构
ErrorAdditionalInfo
| 名称 | 类型 | 描述 |
|---|---|---|
| info | object | 附加信息 |
| type | string | 附加信息类型 |
ErrorDetail
| 名称 | 类型 | 描述 |
|---|---|---|
| additionalInfo | ErrorAdditionalInfo[] | 错误附加信息 |
| code | string | 错误代码 |
| details | ErrorDetail[] | 错误详情 |
| message | string | 错误消息 |
| target | string | 错误目标 |
自定义类结构
TErrorAdditionalInfo = class private FInfo: TJSONObject; FType: string; public destructor Destroy; override; property Info: TJSONObject read FInfo; property &Type: string read FType; end; TErrorDetail = class private FAdditionalInfo: TArray<TErrorAdditionalInfo>; FCode: string; FDetails: TArray<TErrorDetail>; FMessage: string; FTarget: string; public destructor Destroy; override; property AdditionalInfo: TArray<TErrorAdditionalInfo> read FAdditionalInfo; property Code: string read FCode; property Details: TArray<TErrorDetail> read FDetails; property Message: string read FMessage; property Target: string read FTarget; end;
待反序列化JSON示例
{ "additionalInfo": { "info": { "Key1": "TextValue", "Key2": 123, "Key3": [1, 2, 3] }, "type": "SomeType" }, "code": "some code", "details": [], "message": "Hello world", "target": "" }
问题原因
REST.Json库默认的序列化/反序列化逻辑主要针对自定义实体类和基本数据类型,不直接支持将JSON对象映射到TJSONObject类型的属性,因此无法自动填充FInfo字段。
解决方案
方法一:使用JSON拦截器(推荐)
通过自定义TJsonInterceptor拦截反序列化过程,手动处理info字段的解析,实现优雅的自动映射。
- 定义拦截器类,重写
ReadJson方法处理info字段:
TErrorAdditionalInfoInterceptor = class(TJsonInterceptor) public procedure ReadJson(const AObject: TObject; const AJson: TJSONValue); override; end; procedure TErrorAdditionalInfoInterceptor.ReadJson(const AObject: TObject; const AJson: TJSONValue); var LErrorInfo: TErrorAdditionalInfo; LJsonObj: TJSONObject; LInfoValue: TJSONValue; begin inherited; LErrorInfo := AObject as TErrorAdditionalInfo; LJsonObj := AJson as TJSONObject; LInfoValue := LJsonObj.GetValue('info'); if Assigned(LInfoValue) then LErrorInfo.FInfo := LInfoValue.Clone as TJSONObject; end;
- 在
TErrorAdditionalInfo的Info属性上添加JsonInterceptor注解:
TErrorAdditionalInfo = class private FInfo: TJSONObject; FType: string; public destructor Destroy; override; [JsonInterceptor(TErrorAdditionalInfoInterceptor)] property Info: TJSONObject read FInfo; property &Type: string read FType; end;
- 确保在析构函数中释放
FInfo,避免内存泄漏:
destructor TErrorAdditionalInfo.Destroy; begin FInfo.Free; inherited; end;
之后再调用TJson.JsonToObject<TErrorDetail>(AResponseString),Info字段即可正确填充为目标TJSONObject。
方法二:手动解析JSON
若场景简单,可先将JSON解析为TJSONObject,再手动为FInfo字段赋值:
var LRootJson: TJSONObject; LErr: TErrorDetail; LAdditionalInfoJson: TJSONObject; LInfoJson: TJSONObject; begin LRootJson := TJSONObject.ParseJSONValue(AResponseString) as TJSONObject; try // 先反序列化基础字段 LErr := TJson.JsonToObject<TErrorDetail>(LRootJson.ToString); try // 手动提取并赋值info字段 LAdditionalInfoJson := LRootJson.GetValue('additionalInfo') as TJSONObject; if Assigned(LAdditionalInfoJson) then begin LInfoJson := LAdditionalInfoJson.GetValue('info') as TJSONObject; if Assigned(LInfoJson) and (Length(LErr.AdditionalInfo) > 0) then LErr.AdditionalInfo[0].FInfo := LInfoJson.Clone as TJSONObject; end; // 后续使用LErr对象 finally LErr.Free; end; finally LRootJson.Free; end; end;
注意事项
- 注意JSON结构与类定义的一致性:你的JSON示例中
additionalInfo是单个对象,但类中AdditionalInfo是数组类型,这会导致该字段反序列化异常。需将JSON改为数组格式[{"info":..., "type":...}],或把类中的AdditionalInfo改为单个TErrorAdditionalInfo对象。
内容的提问来源于stack exchange,提问作者Mikhail Kostitsyn
相关产品推荐
相关产品推荐

