Remoting SDK + HTTPAPi :- Passing json object containing child class fields

Hi ,

I am using ROSDK + HTTPAPI to create a REST API based on existing server.
I have created a class inherited from TROComplexType as :

  stParent = class(TROComplexType)
  private
    fName: ROUTF8String;
    FMiddleName: ROUTF8String;
  published
    {$IFDEF RO_RTTI_Support} [ROSerializeAsUTF8String] {$ENDIF}
    property Name: ROUTF8String read fName write fName;
    {$IFDEF RO_RTTI_Support} [ROSerializeAsUTF8String] {$ENDIF}
    property MName: ROUTF8String read FLastName write FLastName;
  end;

based on above class, I have created two more classes as :

stChild1 = class(stParent)
    private
      LastName: string;
    published
      property LName: string read LastName write LastName;
  end; 

    stChild2 = class(stParent)
        private
          FSName: string;
        published
          property SName: string read FSName write FSName;
      end; 

Now I have created a service method as follows :

      [ROServiceMethod]
      [ROCustom('HttpApiPath','parent')]
      [ROCustom('HttpApiMethod','POST')]
      function GetDemo(obj: stParent): string;

according to my requirements, JSON data from client can contain any value. for example it could be like :

{
	"obj":{
		"Name":"Deepak",
		"MName":"Kumar",
		"LName": "Jain"
	}
}

or

{
	"obj":{
		"Name":"Deepak",
		"MName":"Kumar",
		"SName": "Jain"
	}
}

I do not want to create two service methods having different parameter type.
Could anyone please suggest that how this can be achieved having parameter type as parent in service method and json input having child class fields.

Hi,

you can include __type tag with actual class like

{"version":"1.1",
 "method":"MegaDemoService.EchoPerson",
 "params":{
     "aPerson":{
           "__type":"TPerson",  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
           "Age":33,
           "FirstName":"John",
           "LastName":"Smith","Sex":"sxMale"}
  }
}

Hi @EvgenyK

Sorry , but I could not get this , the example from MegaDemo does not have any child class inherited from TPerson.
The issue which I have mentioned is related with having child classes inherited from TPerson class and creating one service method to pass JSON for both parent and child class

In your case, you should add "__type":"stChild1" or "__type":"stChild2"


Create usual RO SDK client, set TROJsonMessage.IncludeTypeName to True and pass your childs to

function GetDemo(obj: stParent): string;

it will work as expected.

Hi @EvgenyK
Thank you for your quick response.
I tried implementing this , but not working for me. I have created a sample -

I am passing this sample JSON
{
“obj”: {
“__type”:“stChild”,
“FirstName”:“Deepak”,
“LastName”:“Jain”
}
}
This has 2 issues :

  1. RTTI was not able to create stChild class as a workaround I followed StackOverflow link (https://stackoverflow.com/questions/10613094/how-can-i-make-sure-rtti-is-available-for-a-class-without-instantiating-it)

  2. After solving first problem , I was able to progress but could not get child class fields value from stParent object. I want to access all the fields passed from JSON.

Please let me know If missing anything

Hi,
everything works as expected.
I’ve modified a bit your service method to be sure what object is used:

function TNewService.GetDemo(obj: stParent): string;
begin
  if obj is stChild then
    Result := stChild(obj).LastName
  else
    Result := obj.FirstName;
end;
>curl -verbose -X POST "http://localhost:8099/api/parent" -H  "accept: application/json" -H  "content-type: application/json" -d "{\"obj\": {\"__type\":\"stChild\",\"FirstName\":\"Deepak\",\"LastName\":\"Jain\"}}"
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8099 (#0)
> POST /api/parent HTTP/1.1
> Host: localhost:8099
> User-Agent: curl/7.56.0
> Referer: rbose
> accept: application/json
> content-type: application/json
> Content-Length: 68
>
* upload completely sent off: 68 out of 68 bytes
< HTTP/1.1 200 OK
< Connection: close
< Content-Type: application/json; charset=utf-8
< Content-Length: 6
< Date: Thu, 19 Dec 2019 09:27:56 GMT
< Accept-Encoding: gzip, identity
<
"Jain"* Closing connection 0

as you can see, LastName was returned

Thanks @EvgenyK This is working now.

Hi @EvgenyK
The above solution is working as expected but now I have added interger and boolean type properties in parent class as follows :

  stParent = class(TROComplexType)
    private
      FID: Integer;
      FFirstName: string;
      FBoolValue: Boolean;
    published
      property FirstName: string read FFirstName write FFirstName;
      property ID: Integer read FID write FID;
      property BoolValue: Boolean read FBoolValue write FBoolValue;
  end;

Request body json is as :

{
	"obj":{
		"__type":"stChild",
		"LastName":"Jain"
	}
}

I have debug code and found that exception is generated inside TROJSONSerializer.ReadInteger procedure as lItem is nil

procedure TROJSONSerializer.ReadInteger(const aName: string;
  anOrdType: TOrdType; var Ref; ArrayElementId: integer);
var
  lItem: TROJSONValue;
begin
  lItem := IntReadObject(aName);
  if lItem = nil then raise EROJSONSerializerException.Create(err_FieldNotFound);
  case anOrdType of
    otSByte,
    otUByte : byte(Ref) := StrToInt(lItem.AsString);
    otSWord,
    otUWord : word(Ref) := StrToInt(lItem.AsString);
    otSLong,
    otULong : integer(Ref) := StrToInt(lItem.AsString);
  end;
end;

Same thing happening for boolean values.
This only works for string type properties.
I have update code on git repository with this sample

Could you please suggest me what am I missing here ?

Hi,

it works as expected.
Can you specify what version of Remoting SDK you are using, pls?
it can be found in ...\RemObjects SDK for Delphi\Source\ROVersion.inc.

this change that allows to read missing fields in HTTPApi requests, was made a year ago …

HI ,

RO_VERSION = '9.4.107.1363.

Thanks

Can you retest this issue with the RO v9.6+ ?