我正在使用 Delphi Tokyo 并尝试使用 TJson 将对象转换为 json。 Rest.Json 中存在 ObjectToJsonString 方法。 具有简单属性(例如 String 或 Integer)的简单对象没问题,但是当添加属性 TObjectList 时,json 会被属性“ownsObjects”和“listHelper”污染,但 WebService 不接受这些字段。我怎样才能从 Json 中“隐藏”它?
您可以将
JsonReflect
属性放在字段上并控制它们的序列化方式。
这里是一些示例代码,说明如何编写您自己的专用“将此对象列表序列化为数组”属性来处理该问题 - 只需将单位添加到使用中并将
[SerializeObjectList]
添加到您的字段即可。
unit CollectionsReflect;
interface
uses
Generics.Collections,
REST.JsonReflect;
type
SerializeObjectListAttribute = class(JsonReflectAttribute)
constructor Create;
end;
implementation
uses
Rtti;
type
TListOfObjectInterceptor = class(TJSONInterceptor)
function ObjectsConverter(Data: TObject; Field: string): TListOfObjects; override;
end;
{ TListOfObjectInterceptor }
function TListOfObjectInterceptor.ObjectsConverter(Data: TObject;
Field: string): TListOfObjects;
var
ctx: TRttiContext;
list: TList<TObject>;
begin
list := TList<TObject>(ctx.GetType(Data.ClassInfo).GetField(Field).GetValue(Data).AsObject);
Result := TListOfObjects(list.List);
SetLength(Result, list.Count);
end;
{ SerializeObjectListAttribute }
constructor SerializeObjectListAttribute.Create;
begin
inherited Create(ctObjects, rtObjects, TListOfObjectInterceptor);
end;
end.
不幸的是,相反的方法不起作用,因为
TJSONUnMarshal.PopulateFields
内部似乎存在一个错误,在尝试从 json 字符串填充列表时会导致 AV。
// How to hide “ownsObjects” and “listHelper” TObjectList's properties from a Json
type
TSameClass = class(...)
....
public
...
function GetAsJson: string;
...
end;
...
// метод любого класса Txxxxx для получения его json, у которого, для всех его
// переменных с типом TObjectList, будут очищены “ownsObjects” и “listHelper” свойства
function TSameClass.GetAsJson: string;
procedure ClearJsonObjectList(AJson: TJSONObject);
var je: TJSONObject.TEnumerator;
begin
// проходим по всему дереву json и удаляем ненужные нам пары
je := AJson.GetEnumerator();
while je.MoveNext() do
if je.Current.JsonValue is TJSONObject then
// рекурсивный вызов
ClearJsonObjectList(je.Current.JsonValue as TJSONObject)
else
// если есть этот Pair, то есть и другой
if Assigned(AJson.RemovePair('listHelper')) then
AJson.RemovePair('ownsObjects');
end;
var j: TJSONObject;
begin
// получаем json класса, в котором могут быть или не быть TObjectList с ненужными нам парами
j := TJson.ObjectToJsonObject(Self);
// в этой процедуре очищаем полученный json от этих пар
ClearJsonObjectList(j);
// возвращаем результат в виде строки json
Result := j.ToString;
end;
(*
// example
// json before ClearJsonObjectList --------------->
{
"content":{
"checkClose":{
"payments":{
"ownsObjects":true, <<-- must be removed
"listHelper":[ <<-- must be removed
]
},
...
},
"positions":{
"ownsObjects":true, <<-- must be removed
"listHelper":[ <<-- must be removed
]
},
...
},
...
}
// json after ClearJsonObjectList --------------->
{
"content":{
"checkClose":{
"payments":{
},
...
},
"positions":{
},
...
},
...
}
*)
我建议您创建一个对象数组而不是列表,如果不是一般情况下,则为了序列化为 JSON。这对于 JSON 来说效果更好,无论是读取还是写入。只需确保处理所涉及的内存,即释放列表中的对象,如下所示:
procedure TSomeContainerObject.BeforeDestruction;
var
o: TSomeObjectInTheArray;
begin
for o in fObjectArray do
o.Free;
inherited;
end;
我通常将其放入我的 *.dpr 文件中只是为了确保:
begin
{$IFDEF Debug}
ReportMemoryLeaksOnShutdown := true;
{$ENDIF}
// The rest of your startup code here
end.
对 nikhil swami 解决方案的改进是:
procedure ClearJsonObjectList(AJson: TJSONObject);
function GetAsObjectList(AJson: TJSONObject): TJSONPair;
var
ownsObjects, listHelper: TJSONPair;
begin
Result := nil;
if AJson.Count = 2 then
begin
ownsObjects := AJson.Get('ownsObjects');
listHelper := AJson.Get('listHelper');
if Assigned(ownsObjects) and Assigned(listHelper) then
if (ownsObjects.JsonValue is TJSONBool) and (listHelper.JsonValue is TJSONArray) then
begin
AJson.RemovePair(ownsObjects.JsonString.Value);
AJson.RemovePair(listHelper.JsonString.Value);
Result := listHelper;
end;
end;
end;
var
index: integer;
itemName: string;
itemValue: TJSONObject;
list: TJSONPair;
begin
for index := Pred(AJson.Count) downto 0 do
if AJson.Pairs[index].JsonValue is TJSONObject then
begin
itemValue := TJSONObject(AJson.Pairs[index].JsonValue);
list := GetAsObjectList(itemValue);
if Assigned(list) then
begin
itemName := AJson.Pairs[index].JsonString.Value;
AJson.RemovePair(itemName);
AJson.AddPair(itemName, list.JsonValue);
end
else
ClearJsonObjectList(itemValue);
end;
end;
function TSrvMethContab.GetConecctions: TJSONObject;
var
lContext : TDbContext ;
lLista : TList<TConexions>;
jResult : TJSONObject;
begin
lContext := TDbContext.Create(strConexion);
try
lLista := lContext.Select<TConexions>();
jResult := TJson.ObjectToJsonObject(lLista);
jResult.RemovePair('listHelper');
Result := jResult;
finally
lContext.Free;
end;
end;