#1 2015-10-14 13:19:39

Jorge_Ricardo
Member
Registered: 2015-10-14
Posts: 2

Problems serializing TObjectList

I have the following two classes:

type TListaEstados = class
  private
  flista:TObjectList<TEstado>;

  public
  constructor Create;

  published
  property estados:TObjectList<TEstado> read flista write flista;
end;


type
  TEstado = class
  private
    fcodigo: Integer;
    fnome: string;
    fsigla: string;

  published
    property codigo: Integer read fcodigo write fcodigo;
    property nome: string read fnome write fnome;
    property sigla: string read fsigla write fsigla;
end;

And I want to serialize a TListaEstados object. Ideally, I'd like to use the

    MyString := SynCrossPlatformJson.ObjectToJSON(MyObject);

function, but as it is, it returns me json like this:

    {"estados":"null"}

I already tried inheriting from TInterfacedCollection, but it gave me an Access Violation. I also tried to register the class like below:

    TJSONSerializer.RegisterClassForJSON(TListaEstados);

And the output is the same as above. So, where should I begin? How can I serialize this?

Thanks.

Offline

#2 2015-10-14 14:26:15

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,183
Website

Re: Problems serializing TObjectList

TObjectList<TEstado> is just not supported directly.

From the RTTI, there is no easy way of getting the TEstado type from its TObjectList<TEstado> class information..

Try to use simply a TSynAutoCreateFields, and an T*ObjArray.

Offline

#3 2015-10-15 18:33:30

Jorge_Ricardo
Member
Registered: 2015-10-14
Posts: 2

Re: Problems serializing TObjectList

Thanks for the answer, but I couldn't find what this T*ObjArray is.
But if I was to change TObjectList<TEstado> to a dynamic array of TEstado, could I serialize it then? How?

Thanks again.

Last edited by Jorge_Ricardo (2015-10-15 18:45:25)

Offline

#4 2015-10-16 12:20:38

cypriotcalm
Member
Registered: 2015-02-18
Posts: 122

Re: Problems serializing TObjectList

In order to use generics with the mORMot server you could do as followed:

uses
  System.Generics.Collections;
  System.SysUtils,
  System.JSON,
  REST.Json,
  mORMot,
  SynCommons;
  
type
  TEstadoObjectList = class(TObjectList<TEstado>);

  IEstadoService = interface(IInvokable)
  ['{A56EA833-873A-4AA5-9786-DA7DC1A7D014}']
    function GetEstados(out AList: TEstadoObjectList): boolean;
  end;

  TEstadoService = class(TInterfacedObject, IEstadoService)
  public
    function GetEstados(out AList: TEstadoObjectList): boolean;
  end; 

 TJSONSerializerEstado = class(TObject)
  public
    class function ReadJSON(const aValue: TObject; aFrom: PUTF8Char; var aValid: Boolean; aOptions: TJSONToObjectOptions): PUTF8Char;
    class procedure WriteJSON(const aSerializer: TJSONSerializer; aValue: TObject; aOptions: TTextWriterWriteObjectOptions);
  end;

implementation

  class function TJSONSerializerEstado.ReadJSON(const aValue: TObject; aFrom: PUTF8Char; var aValid: Boolean; aOptions: TJSONToObjectOptions): PUTF8Char;
  begin
    // not yet written...
  end;

  class procedure TJSONSerializerEstado.WriteJSON(const aSerializer: TJSONSerializer; aValue: TObject; aOptions: TTextWriterWriteObjectOptions);
  var
    I: Integer;
    EL: TEstadoObjectList;
    JSONObject: TJSOnObject;
  begin
    EL := TEstadoObjectList(aValue);
    JSONObject := nil;
    TRY
      JSONObject := TJson.ObjectToJsonObject(EL);
      aSerializer.AddString(JSONObject.Values['items'].ToJSON);
    FINALLY
      for I := (EL.Count-1) downto 0 do
      begin
        EL[i].Free;
        EL[i] := nil;
        EL.Delete(I);
      end;
      FreeAndNil(JSONObject);
    END;
  end;

function TEstadoService.GetEstados(out AList: TEstadoObjectList): boolean;
var
  Estado: TEstado;
  I: Integer;
begin
  AList.Capacity := 10;
  for I := 1 to AList.Capacity do
  begin
    Estado := TEstado.Create;
    Estado.ID := 123456;
    Estado.Name := 'abcdefg';
    AList.Add(Estado);
  end;
  Result := true;
end;


initialization
  TJSONSerializer.RegisterCustomSerializer(TEstadoObjectList, TJSONSerializerEstado.ReadJSON, TJSONSerializerEstado.WriteJSON);

  TJSONSerializer.RegisterClassForJSON([TEstadoObjectList]);

  TInterfaceFactory.RegisterInterfaces([TypeInfo(IEstadoService)]);

finalization

Then, you can call your service as http://localhost:8080/root/EstadoService/GetEstados and you will get something like:

{
  "result": [
    [
      {
        "iD": 123456,
        "name": "abcdefg"
      }

     ...

    ],
  ],
  "id": 1
}

I hope, it helps! :-)

Last edited by cypriotcalm (2015-10-16 12:43:59)

Offline

#5 2015-10-16 16:09:53

cypriotcalm
Member
Registered: 2015-02-18
Posts: 122

Re: Problems serializing TObjectList

Here is the reader method for the EstadoService

  IEstadoService = interface(IInvokable)
  ['{A56EA833-873A-4AA5-9786-DA7DC1A7D014}']    
    function GetEstados(out AList: TEstadoObjectList): boolean;
    procedure SaveEstados(const AList: TEstadoObjectList);
  end;

...

procedure TEstadoDataService.SaveEstados(const AList: TEstadoObjectList);
var
  I: Integer;
begin
  TRY
    if (AList.Count > 0) then
    begin
      Item1Name := AList[0].Name;
    end;
  FINALLY
    for I := (AList.Count-1) downto 0 do
    begin
      AList[i].Free;
      AList[i] := nil;
      AList.Delete(I);
    end;
  END;
end; 

...

  // THE READER 
  class function TJSONSerializerEstado.ReadJSON(const aValue: TObject; aFrom: PUTF8Char; var aValid: Boolean; aOptions: TJSONToObjectOptions): PUTF8Char;
  var
    I: Integer;
    ML: TEstadoObjectList;
    Estado: TEstado;
    JSONArray, JSONArrayRoot: TJSONArray;
    JSONObject: TJSONObject;
    lst: TEstadoObjectList;
    JSONString: string;
  begin
    aValid := false;
    Result := PUTF8Char('['+aFrom);
    if (aValue is TEstadoObjectList) then
    begin
      JSONArray := nil;
      JSONArrayRoot := nil;
      TRY
        JSONArrayRoot := TJSONObject.ParseJSONValue(Result) as TJSONArray;
        if Assigned(JSONArrayRoot) and (JSONArrayRoot.Count > 0) then
        begin
          JSONArray := JSONArrayRoot.Items[0] as TJSONArray;
          if Assigned(JSONArray) and (JSONArray.Count > 0) then
          begin
            ML := TEstadoObjectList(aValue);
            ML.Capacity := JSONArray.Count;
            for I := 0 to JSONArray.Count-1 do
            begin
              JSONString := JSONArray.Items[i].ToJSON;
              if (JSONString <> '') then
              begin
                Estado := TJson.JsonToObject<TEstado>(JSONString);
                if Assigned(Estado) then
                begin
                  ML.Add(Estado);
                end;
              end;
            end;
            aValid := true;
          end;
        end;
      FINALLY
        FreeAndNil(JSONArrayRoot);
      END;
    end;
  end;

Your POST request should look like this:

[[{
	"iD": 1,
	"name": "Estado 1"
},
{
	"iD": 2,
	"name": "Estado 2"
},
{
	"iD": 3,
	"name": "Estado 3"
}]]

Offline

Board footer

Powered by FluxBB