TTMSFNCObjectPersistence.LoadObjectFromString

Hi,

I have the following json generated using the .JSON class helper from TMSFNCTypes unit.

{"$type":"TRespuesta","ID":1,"MensajesError":[{"$type":"TMensajeError","Codigo":1,"Texto":"ERROR1"},{"$type":"TMensajeError","Codigo":2,"Texto":"ERROR2"}],"Tributos":[{"$type":"TDUCATributo","BaseImponible":0,"Codigo":"DAI","FormaPago":"","ID":0,"NomFormaPago":"","NomTributo":"","Porcentaje":0,"PorcentajeA":0,"PorcentajeN":0,"Secuencia":0,"Tipo":"","Valor":0}],"ValorI":2,"ValorS":"OMAR"}

The code using VCL.TTMSFNCObjectPersistence works ok.
Respuesta := TRespuesta.Create;
TTMSFNCObjectPersistence.LoadObjectFromString(Respuesta, sTemp);
when using WebLib.TTMSFNCObjectPersistence on WebCore I get a Exception "Name expected".

Any hints?

Thanks in advance,

Omar Zelaya

Hi, this more compact json string also fails with same error using WebLib.

{"$type":"TRespuesta","ID":1,"MensajesError":[{"$type":"TMensajeError","Codigo":1,"Texto":"ERROR1"},{"$type":"TMensajeError","Codigo":2,"Texto":"ERROR2"}],"Tributos":[{"$type":"TDUCATributo","BaseImponible":0,"Codigo":"DAI","FormaPago":"","ID":0,"NomFormaPago":"","NomTributo":"","Porcentaje":0,"PorcentajeA":0,"PorcentajeN":0,"Secuencia":0,"Tipo":"","Valor":0}],"ValorI":2,"ValorS":"OMAR"}

TDUCATributo = class(TPersistent)
private
FID: int64;
FSecuencia: integer;
FPorcentaje: double;
FTipo: string;
FCodigo: string;
FNomCodTributo: string;
FFormaPago: string;
FNomFormaPago: string;
FValor: double;
FBaseImponible: double;
FPorcentajeA: double;
FPorcentajeN: double;

  function GetXML : string;

{$IFNDEF PAS2JS}
function GetDB : TDBTRIBUTO;
{$ENDIF}
public
function ToJson : TJSONObject;
property XML : string read GetXML;
{$IFNDEF PAS2JS}
property DB : TDBTRIBUTO read GetDB;

  class procedure FromDB(const ALista : ArrayOfTDUCATributo;
                         const ADBLista : TList<TDBTRIBUTO>);

{$ENDIF}
published
property Porcentaje: double read FPorcentaje write FPorcentaje;
property Secuencia: integer read FSecuencia write FSecuencia;
property BaseImponible: double read FBaseImponible write FBaseImponible;
property Codigo: string read FCodigo write FCodigo;
property FormaPago: string read FFormaPago write FFormaPago;
property ID: int64 read FID write FID;
property NomFormaPago: string read FNomFormaPago write FNomFormaPago;
property NomTributo: string read FNomCodTributo write FNomCodTributo;
property PorcentajeA: double read FPorcentajeA write FPorcentajeA;
property PorcentajeN: double read FPorcentajeN write FPorcentajeN;
property Tipo: string read FTipo write FTipo;
property Valor: double read FValor write FValor;
end;

TMensajeError = class(TPersistent)
private
FCodigo: integer;
FTexto: string;
public
published
property Codigo: integer read FCodigo write FCodigo;
property Texto: string read FTexto write FTexto;
end;
ArrayOfTTMensajeError = TList;

TRespuesta = class(TPersistent)
private
FID: int64;
FValorI: int64;
FValorS: string;
FMensajesError: ArrayOfTTMensajeError;
FTributos: ArrayOfTDUCATributo;
public
procedure AgregaMensajeError(const ACodigo : integer;
const ATexto : string);
constructor Create;
destructor Destroy; override;
published
property ID: int64 read FID write FID;
property Tributos: ArrayOfTDUCATributo read FTributos write FTributos;
property MensajesError: ArrayOfTTMensajeError read FMensajesError write
FMensajesError;
property ValorI: int64 read FValorI write FValorI;
property ValorS: string read FValorS write FValorS;
end;

Thanks in advace,

Omar Zelaya

If you just declare
ArrayOfTTMensajeError = TList;
I can't see how RTTI could resolve items in this list to TMensajeError.
While it is not shown in your code here, I assume you do the same with ArrayOfTDUCATributo.

Hi,

I have attached a small demo project that shows that same code works on VCL but not in WebCore.

Thanks in advance,

Omar Zelaya
Test.zip (149.6 KB)

For generic types you need to register the class and implement interfaces to deal with adding items.
The JSON persistence class is not capable of detecting which class it needs to create.

Hi,

The blog post mention that the inferfaces are needed when there is no $type in the JSON. On the test the $type is present on the json.

Thank in advance,

Omar Zelaya

Unfortunately the class TMensajeError constructor is not called during the recreation process when reloading the object, therefore, the sub list Items: ArrayOfTItemMensajeError is not created. It actually calls TObject.Create instead. So, you could indeed use the $type recreation process, but then you'll need to change the code, so the Items array is not created in the constructor, but in the getter of the property:

{ TMensajeError }

constructor TMensajeError.Create;
begin
end;

destructor TMensajeError.Destroy;
var
  Item : TItemMensajeError;
begin
  for Item in FItems do
    Item.Free;
  FITems.Free;
  inherited;
end;

function TMensajeError.GetItems: ArrayOfTItemMensajeError;
begin
  if not Assigned(FItems) then
    FItems := ArrayOfTItemMensajeError.Create;

  Result := FItems;
end;

Alternatively, you could implement the same technique as the blog post explains, by implementing the required interfaces on the object to properly create the message and call the correct constructor