2

I am trying to serialize an TObject to JSON using the mORMot framework. Unfortunately, the result is always null.

The class I am trying to serialize is:

type ApmTime = class(TObject)
private
  function currentTime() : String;
published
  property Current_time: String read currentTime;
public
  constructor Create;
end;

constructor ApmTime.Create;
begin
  inherited;
end;

function ApmTime.currentTime() : String;
begin
  result :=  TimeToStr(Now);
end;

And the corresponding mORMot method is defined in SynCommons:

currentTime := ApmTime.Create;
Write(ObjectToJSON(currentTime, [woFullExpand]));

This always returns null. After having single-stepped in TTextWriter.WriteObject (located in unit SynCommons), the following piece of code seems to be where the resulting json is set to null:

if not(woFullExpand in Options) or
       not(Value.InheritsFrom(TList)
       {$ifndef LVCL} or Value.InheritsFrom(TCollection){$endif}) then
      Value := nil;
  if Value=nil then begin
    AddShort('null');
    exit;

I am expecting something along the line:

{
  "Current_time" : "15:04"
}
BigONotation
  • 3,788
  • 4
  • 33
  • 57

2 Answers2

1

Try add a write to the published property.

property Current_time: String read currentTime write SetCurrentTime. 

A readonly property is not serialized. Also ApmTime should be based on TPersistent

type 
  ApmTime = class(TPersistent)
Jan Lauridsen
  • 456
  • 4
  • 8
1

Ran into this yesterday and worked out what's going on, so for the benefit of future people stumbling over this problem as well:

If you only add SynCommons.pas to your uses clause, then the default DefaultTextWriterJSONClass is set to TTextWriter which only supports serializing particular class types as you've seen, and doesn't support arbitrary classes/objects. See the following lines in SynCommons.pas where this default is set:

var
  DefaultTextWriterJSONClass: TTextWriterClass = TTextWriter;

Now, in order to support serializing arbitrary objects to JSON, this global variable needs to be changed from the default TTextWriter to TJSONSerializer.

This class is defined in mORMot.pas, and in fact, if you add mORMot.pas to your uses clause, its initialization will override the above default and set TJSONSerializer as the new default for you.

This behaviour is in fact documented in SynCommons.pas if you read carefully enough, e.g. see the comments for "SetDEfaultJSONClass()" class method:

// you can use this method to override the default JSON serialization class
// - if only SynCommons.pas is used, it will be TTextWriter
// - but mORMot.pas initialization will call it to use the TJSONSerializer
// instead, which is able to serialize any class as JSON
class procedure SetDefaultJSONClass(aClass: TTextWriterClass);

So in short: To fix your issue, just add mORMot.pas to your uses clause in addition to SynCommons.pas which you should already have.

W.Prins
  • 1,108
  • 1
  • 13
  • 20