You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi 11中TJsonMarshal自定义转换器注册及序列化问题

Delphi 11 TJsonMarshal自定义转换器处理TObjectList继承属性排除问题

问题背景

在Delphi 11中使用TJsonMarshal序列化包含TObjectList<T>的DTO时,会默认序列化TObjectList<T>的继承属性(如FListHelper、FOwnsObjects),导致JSON冗余。尝试注册自定义转换器排除这些属性时,遇到两个核心问题:

  1. 转换器注册后始终不触发,官方文档无有效示例;
  2. 转换器生效后,发现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 21:18:11