Delphi含短字符串的记录JSON序列化时字段丢失问题求解
Delphi XE 10.2.2中TJsonSerializer忽略短字符串(string[N])字段的解决方法
问题根源
你推测的没错,问题确实出在RTTI上。Delphi的System.Rtti单元对静态短字符串(string[N])的支持有限,TJsonSerializer在遍历RTTI字段时会默认跳过这类类型——因为它无法直接识别并处理静态长度的字符串结构,而AnsiString和String(UnicodeString)属于动态字符串类型,RTTI对其有完善支持,所以能正常序列化。
解决方法
方法1:批量替换短字符串类型(最直接)
如果项目允许修改现有字段定义,把所有string[N]替换为AnsiString或String是最快的解决方案:
// 原记录定义 type TMyRecord = record ShortStrField: string[50]; end; // 修改后 type TMyRecord = record ShortStrField: AnsiString; // 或直接用 String(UnicodeString) end;
这种方式无需额外编写序列化逻辑,直接复用TJsonSerializer的原生功能即可。
方法2:自定义RTTI扩展处理短字符串
如果无法修改字段类型,可以通过扩展RTTI和手动处理短字符串的方式实现序列化:
- 定义一个标记用的自定义属性(可选,也可直接通过类型判断):
uses System.Rtti; type TShortStringAttr = class(TCustomAttribute);
- 给短字符串字段添加标记:
type TMyRecord = record [TShortStringAttr] ShortStrField: string[50]; NormalField: String; end;
- 编写自定义序列化函数,手动读取短字符串内容:
uses System.JSON.Serializers, System.JSON, System.TypInfo; function SerializeWithShortStrings(const ARecord: TValue): TJSONObject; var RttiCtx: TRttiContext; RttiType: TRttiType; RttiField: TRttiField; ShortStrVal: string; begin Result := TJSONObject.Create; RttiCtx := TRttiContext.Create; try RttiType := RttiCtx.GetType(ARecord.TypeInfo); for RttiField in RttiType.GetFields do begin // 判断字段是否为短字符串类型 if (RttiField.FieldType.TypeKind = tkUString) and (RttiField.FieldType is TRttiOrdinalType) then begin // 手动读取短字符串内容 ShortStrVal := StrPas(PAnsiChar(ARecord.GetReferenceToRawData + RttiField.Offset)); Result.AddPair(RttiField.Name, TJSONString.Create(ShortStrVal)); end else begin // 其他类型用默认序列化逻辑 Result.AddPair(RttiField.Name, TJsonSerializer.Default.SerializeValue(RttiField.GetValue(ARecord))); end; end; finally RttiCtx.Free; end; end;
使用时直接调用该函数代替默认序列化即可。
方法3:继承TJsonSerializer重写字段处理逻辑
如果想完全集成到TJsonSerializer的工作流中,可以继承它并重写字段处理方法:
type TCustomJsonSerializer = class(TJsonSerializer) protected procedure SerializeField(AObject: TObject; AField: TRttiField; AJsonPair: TJSONPair); override; procedure SerializeValue(const AValue: TValue; out AJsonValue: TJSONValue); override; end; procedure TCustomJsonSerializer.SerializeField(AObject: TObject; AField: TRttiField; AJsonPair: TJSONPair); var FieldVal: TValue; ShortStrVal: string; begin FieldVal := AField.GetValue(AObject); if (AField.FieldType.TypeKind = tkUString) and (AField.FieldType is TRttiOrdinalType) then begin ShortStrVal := StrPas(PAnsiChar(FieldVal.GetReferenceToRawData)); AJsonPair.JsonValue := TJSONString.Create(ShortStrVal); end else begin inherited; end; end; procedure TCustomJsonSerializer.SerializeValue(const AValue: TValue; out AJsonValue: TJSONValue); var ShortStrVal: string; begin if (AValue.TypeInfo.Kind = tkUString) and (AValue.TypeInfo is TRttiOrdinalType) then begin ShortStrVal := StrPas(PAnsiChar(AValue.GetReferenceToRawData)); AJsonValue := TJSONString.Create(ShortStrVal); end else begin inherited; end; end;
之后使用TCustomJsonSerializer替代默认的TJsonSerializer,即可自动处理短字符串字段。
内容的提问来源于stack exchange,提问作者Asa IL
相关产品推荐
相关产品推荐

