How to hide "ownsObjects" and "listHelper" TObjectList's properties from a Json using Delphi (Rest.JSON)?

2.4k views Asked by At

I'm using Delphi Tokyo and trying to convert an object into a json using TJson. ObjectToJsonString method present in Rest.Json. A simple object with simple properties such as String or Integer it's ok, but when a add a property TObjectList, the json is poluted with the properties "ownsObjects" and "listHelper", but the WebService doesn't accept these fields. How can I "hide" it from a Json?

6

There are 6 answers

0
Stefan Glienke On

You can put the JsonReflect attribute on fields and control how they are being serialized.

Here is some example code of how to write your own specialized "serialize this object list as array" attribute that takes care of that - just add the unit to the uses and add [SerializeObjectList] to your field.

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.

Unfortunately the opposite way does not work this way as there seems to be a bug inside of TJSONUnMarshal.PopulateFields that causes an AV when trying to populate the list from the json string.

1
John Ibarguen On
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;
1
Petr On
    // 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":{
      },
      ...
    },
    ...
  }
*)
0
Magne Rekdal On

I recommend that you create an array of objects instead of a list, if not generally, then for the purpose of serialization to JSON. This works much better with JSON, both reading and writing. Just make sure to handle the memory involved, i.e. free the objects in the list, something like this:

procedure TSomeContainerObject.BeforeDestruction;
var
  o: TSomeObjectInTheArray;
begin
  for o in fObjectArray do
    o.Free;
  inherited;
end;

I usually put this in my *.dpr file just to make sure:

begin
  {$IFDEF Debug}
  ReportMemoryLeaksOnShutdown := true;
  {$ENDIF}  
  // The rest of your startup code here
end.
0
Rodrigo Oliveira On

an improvement on nikhil swami's solution is:

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;
  • the ClearJsonObjectList function will replace the ObjectList as an object with a list/array, keeping the values.
1
acg.net On
uses
  System.Json;

Function GetNode(AJson, ANode: String): String;    
var
  JsonArray: TJSONArray;
  JsonValue: TJSONValue;

begin     
 JSonValue := TJSonObject.ParseJSONValue(AJson);

  if Assigned(JSonValue) and (JSonValue is TJSONObject) then
      JsonArray := TJSONObject(JSonValue).GetValue(ANode) as TJSONArray;

  Result := JsonArray.ToString
end;

Example of use:

StringJson := GetNode(DirtyJsonString, 'listHelper');