#1 2023-07-24 20:46:48

AntoineGS
Member
Registered: 2020-04-01
Posts: 7

ObjectToJSON / TTextWriter

In mORMot1 I was using similar code to ObjectToJSON to serialize objects with runtime filtering on what fields should end up in the JSON.
I was doing it by using TTextWriter.OnWriteObject (the rest of the code was identical to the ObjectToJSON procedure), and returning True on any field/object that should not be serialized.
Is there a way of achieving this using mORMot2? I have failed to find a way to do so sad .

Offline

#2 2023-07-24 22:23:40

tbo
Member
Registered: 2015-04-20
Posts: 353

Re: ObjectToJSON / TTextWriter

AntoineGS wrote:

Is there a way of achieving this using mORMot2?

I am not sure if I understand your requirement correctly. You can do it with a custom serializer:

type
  TTestObject = class(TSynPersistent)
  private
    FVorname: RawUtf8;
    FNachname: RawUtf8;
    class procedure TTestObjectWriter(pmWriter: TJsonWriter; pmData: Pointer; pmOptions: TTextWriterWriteObjectOptions);
  published
    property Vorname: RawUtf8
      read FVorname write FVorname;
    property Nachname: RawUtf8
      read FNachname write FNachname;
  end;

class procedure TTestObject.TTestObjectWriter(pmWriter: TJsonWriter; pmData: Pointer; pmOptions: TTextWriterWriteObjectOptions);
begin
  with TTestObject(pmData) do
    pmWriter.AddJsonEscape(['Vorname', Vorname, 'Nachname', Nachname]);
end;

initialization
  TRttiJson.RegisterCustomSerializer(TypeInfo(TTestObject), Nil, TTestObject.TTestObjectWriter);

If you're looking for something else, I have a class TCompPropStorage in the source code for this article in the unit u_Utilities.pas.

With best regards
Thomas

Offline

#3 2023-07-24 22:42:45

AntoineGS
Member
Registered: 2020-04-01
Posts: 7

Re: ObjectToJSON / TTextWriter

Thank for recommendation!
Though I think it would require a lot of custom serializers.

Here is more information with examples: I had this following function which I called for any object class:

function TWIERestExecute.ObjectToJSON(Value: TObject; Options: TTextWriterWriteObjectOptions = [woDontStoreDefault]): RawUTF8;
var
  temp: TTextWriterStackBuffer;
  aTextWriter: SynCommons.TTextWriter;
begin
  if Value = nil then
    result := NULL_STR_VAR
  else
  begin
    aTextWriter := DefaultTextWriterSerializer.CreateOwnedStream(temp);
    try
      aTextWriter.OnWriteObject := OnWriteObject;
      aTextWriter.CustomOptions := aTextWriter.CustomOptions + [twoForceJSONStandard];
      aTextWriter.WriteObject(Value, Options);
      aTextWriter.SetText(result);
    finally
      aTextWriter.Free;
    end;
  end;
end;

And the OnWriteObject implement was as such:

function TWIERestExecute.OnWriteObject(Sender: SynCommons.TTextWriter; Value: TObject; PropInfo: pointer; Options: TTextWriterWriteObjectOptions): boolean;
begin
  if ((FBasePropInfosToInclude.IndexOf(PropInfo) <> -1) or (FPropInfosToInclude.Count = 0) or (FPropInfosToInclude.IndexOf(PropInfo) <> -1)) and
     (FBasePropInfosToExclude.IndexOf(PropInfo) = -1) then
    result := False
  else
    result := True; {Will skip this property}
end;

The above class fields are just TLists, which I used as such:

  FBasePropInfosToExclude := TList.Create;
  FBasePropInfosToExclude.Add(GetPropInfo(TypeInfo(TWebOutputRemotable), 'ErrorCode'));
  FBasePropInfosToExclude.Add(GetPropInfo(TypeInfo(TWebOutputRemotable), 'ErrorMessage'));

The client code could then define what properties it wanted to serialize, without needing a custom serializer for each class I use.
If I understand the example you sent I would need to define a custom serializer for each class that is getting serialized right?

Edit: The TLists are actually getting populated using strings received through a rest API, in the format classname.propertyname, and so it varies from call to call.
My example does not reflect that.

Last edited by AntoineGS (2023-07-24 22:44:32)

Offline

#4 2023-07-25 01:12:40

tbo
Member
Registered: 2015-04-20
Posts: 353

Re: ObjectToJSON / TTextWriter

AntoineGS wrote:

Though I think it would require a lot of custom serializers.

You can serialize the object yourself. For non-nested objects like this:

var
  FPropNamesToInclude: TRawUtf8DynArray;
  FPropNamesToExclude: TRawUtf8DynArray;

class procedure TTestObject.TTestObjectWriter(pmWriter: TJsonWriter; pmInstance: TObject; pmOptions: TTextWriterWriteObjectOptions);
var
  value: TRttiVarData;
begin
  pmWriter.Add('{');
  for var run: TRttiCustomProp in Rtti.ByClass[pmInstance.ClassType].Props.List do
  begin
    if (FindPropName(FPropNamesToInclude, run.Name) >= 0)
      and (FindPropName(FPropNamesToExclude, run.Name) < 0) then
    begin
      pmWriter.AddFieldName(run.Name);
      run.GetValue(pmInstance, value);
      pmWriter.AddVariant(Variant(value));
      pmWriter.AddComma;
    end;
  end;
  pmWriter.CancelLastComma;
  pmWriter.Add('}');
end;

Then you only need to register objects.

With best regards
Thomas

Offline

Board footer

Powered by FluxBB