3

I'm having trouble deserializing an object containing a interface field from json using SuperObject (serialization works fine) on DXE2. Consider the following:

  ITest = interface(IInterface)
    ['{9E5623FF-1BC9-4FFA-919D-80C45EE24F38}']
    function GetField3() : string;
    procedure SetField3(Value: string);
    property FField3: string read GetField3 write SetField3;
  end;

  TTest = class(TInterfacedObject, ITest)
  private
    FField3: string;
    function GetField3() : string;
    procedure SetField3(Value: string);
  public
    property Field3: string read GetField3 write SetField3;
    constructor Create(Field3: string);
  end;

  TMyClass = class(TObject)
  public
    FField1: string;
    FField2: string;
    FTest:  ITest;
    constructor Create(Field1: string; Field2: string; Test: ITest);
  end;

  // TTest-stuff omitted for brevity.

constructor TMyClass.Create(Field1, Field2: string; Test: ITest);
begin
  FField1 := Field1;
  FField2 := Field2;
  FTest := Test;
end;

var
  MyClass:        TMyClass;
  MyClass2:       TMyClass;
  JSONObj:        ISuperObject;
  SuperContext:   TSuperRttiContext;
begin
  MyClass := TMyClass.Create('Test1', 'Test2', TTest.Create('Test3'));
  SuperContext := TSuperRttiContext.Create();
  JSONObj := SuperContext.AsJson<TMyClass>(MyClass);
  WriteLn(JSONObj.AsString);
  MyClass2 := SuperContext.AsType<TMyClass>(JSONObj);
  MyClass2.Free();
  ReadLn;
end.

When execution gets to TSuperRttiContext.FromJson.FromClass checking the FTest-field, the doo-doo hits the propeller in the ceiling (or table mounted, if you prefer that). At this point, Result := FromJson(f.FieldType.Handle, GetFieldDefault(f, obj.AsObject[GetFieldName(f)]), v); is called, which leads us into the interesting part of the SuperObject.pas code. I'll duplicated it here for brevity.

  procedure FromInterface;
  const soguid: TGuid = '{4B86A9E3-E094-4E5A-954A-69048B7B6327}';
  var
    o: ISuperObject;
  begin
    if CompareMem(@(GetTypeData(TypeInfo).Guid), @soguid, SizeOf(TGUID)) then
    begin
      if obj <> nil then
        TValue.Make(@obj, TypeInfo, Value) else
        begin
          o := TSuperObject.Create(stNull);
          TValue.Make(@o, TypeInfo, Value);
        end;
      Result := True;
    end else
      Result := False;
  end;

The value assigned to soguid is that of ISuperObject, so clearly the two won't match (I'm testing for ITest, remember?). And so I'm a little lost of what to make of this. Is it illegal to deserialize any object composed of one or more interface fields?

This seems like such a common use case, that I find it hard to believe. I can appreciate the fact that knowing what implementation of a given interface to choose may be non-trivial. Yet, I see from the comment in the preamble, that interfaced objects are supposed to be supported - http://code.google.com/p/superobject/source/browse/trunk/superobject.pas#47.

Sure would be great if anyone have solved this out there. Thanks! :)

Jan Doggen
  • 8,799
  • 13
  • 70
  • 144
conciliator
  • 6,078
  • 6
  • 41
  • 66
  • Did you (also) ask on the superobject forum at progdigy.com? – Jan Doggen Sep 13 '12 at 06:19
  • No, not yet. Will do as soon as my newly created account is approved by the administrators. – conciliator Sep 13 '12 at 06:24
  • I don't think you quote means what you tell. Look at next line #48 - "+ added a new data type: the method" - that is a support. And your quote only meant that SuperObject itself became interfaced. Otherwise what means line 46 to you " + renamed class" ? SO renames classes while reading JSON file ? Was you able to save that object to json file ? what did it written for that interface property ? – Arioch 'The Sep 13 '12 at 10:31
  • Frankly, how can it read that type without class factory ? Imagine it is you now, who try to do it. You are given GUID, and TStringList of properties key/value pairs. You should create an object and fill it with those properties. But to create an object you need a class. Interface is not class. HOW would you create that object then ? – Arioch 'The Sep 13 '12 at 10:31
  • @Arioch'The: yes, I agree in your interpretation regarding the quote. (I must admit that the edit was done in a hurry, and so my initial interpretation was faulty.) I was able to serialize the object to json, which led me to expect a symmetric behaviour. The returned json contained null for the FTest value. – conciliator Sep 13 '12 at 11:33
  • @Arioch'The: re your second comment, as I wrote in the post, I do appreciate the difficulties in deserializing to an interfaced object. I think SuperObject would benefit from a more detailed exception thrown, like "Deserializing interfaces not supported" instead of the rather generic "Marshaling error". Or even better, ability to pass in the desired implementation for use in conjunction with deserializing interfaced objects. – conciliator Sep 13 '12 at 11:38
  • *The returned json contained null for the FTest value* Am i right that in json file there was nothing for the property. You told "serialization works fine" but you only meant that no exception thrown, not that all the values really were saved ? – Arioch 'The Sep 13 '12 at 12:59
  • It works fine in the sense of not throwing an exception. The resulting json is "{"FField2":"Test2","FField1":"Test1","FTest":null}". – conciliator Sep 13 '12 at 13:01
  • The author is SO member ([Henri Gourvest](http://stackoverflow.com/users/259076/henri-gourvest)). Don't look furhter, ask him directly. – menjaraz Sep 14 '12 at 09:43

0 Answers0