Delphi 11中TJsonMarshal自定义转换器注册及序列化问题
Delphi 11 TJsonMarshal自定义转换器处理TObjectList继承属性排除问题
问题背景
在Delphi 11中使用TJsonMarshal序列化包含TObjectList<T>的DTO时,会默认序列化TObjectList<T>的继承属性(如FListHelper、FOwnsObjects),导致JSON冗余。尝试注册自定义转换器排除这些属性时,遇到两个核心问题:
- 转换器注册后始终不触发,官方文档无有效示例;
- 转换器生效后,发现
FIELD_ANY常量无法匹配所有字段,必须指定精确字段名;且转换器仅支持精确类型匹配,不识别继承关系,衍生自TObjectList<T>的自定义列表类仍会序列化冗余属性。
解决方案
1. 让自定义转换器触发的正确方式
要触发转换器,必须使用TTypeObjectsConverter针对精确的泛型列表类型注册,而非父类或泛型模板。同时确保Convert方法正确覆盖并实现自定义序列化逻辑。
示例代码:
type TObjectListConverter<T: class> = class(TTypeObjectsConverter) public procedure Convert(Sender: TObject; const Input: TObject; var Output: TJSONValue); override; end; procedure TObjectListConverter<T>.Convert(Sender: TObject; const Input: TObject; var Output: TJSONValue); var LMarshal: TJsonMarshal; LList: TObjectList<T>; LArray: TJSONArray; LItem: T; begin LMarshal := Sender as TJsonMarshal; LList := Input as TObjectList<T>; LArray := TJSONArray.Create; try // 仅序列化列表元素,跳过TObjectList的继承属性 for LItem in LList do LArray.Add(LMarshal.Marshal(LItem) as TJSONValue); Output := LArray; except LArray.Free; raise; end; end; // 注册与使用 var LMarshal: TJsonMarshal; begin LMarshal := TJsonMarshal.Create(TJSONConverter.Create); try // 注册具体泛型类型的转换器 LMarshal.RegisterConverter(TObjectList<TMyItem>, TObjectListConverter<TMyItem>); // 序列化DTO var LJson := LMarshal.Marshal(TMySampleDTO.Create); try Writeln(LJson.ToString); finally LJson.Free; end; finally LMarshal.Free; end; end;
2. 处理FIELD_ANY不生效的问题
Embarcadero定义的FIELD_ANY常量(值为'*')实际无法匹配所有字段,若需保留默认序列化逻辑仅排除特定属性,可在Convert方法中先调用默认序列化,再手动移除冗余字段:
procedure TObjectListConverter<T>.Convert(Sender: TObject; const Input: TObject; var Output: TJSONValue); var LObj: TJSONObject; begin // 先执行默认序列化 LObj := inherited Convert(Sender, Input) as TJSONObject; try // 移除不需要的继承属性 LObj.RemovePair('FListHelper'); LObj.RemovePair('FOwnsObjects'); Output := LObj; except LObj.Free; raise; end; end;
3. 解决继承类型匹配问题
由于TJsonMarshal的转换器不支持继承检测,需为所有TObjectList<T>的子类逐个注册转换器。可通过遍历类后代实现批量注册:
procedure RegisterAllObjectListDescendantConverters(AMarshal: TJsonMarshal); var LClass: TClass; begin for LClass in TObjectList<T>.ClassDescendants do begin if not AMarshal.HasConverter(LClass) then AMarshal.RegisterConverter(LClass, TObjectListConverter<T>); end; end;
内容的提问来源于stack exchange,提问作者Matt Baech
相关产品推荐
相关产品推荐

