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.
EvgenyK
(Evgeny Karpov)
December 18, 2019, 12:33pm
2
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
EvgenyK
(Evgeny Karpov)
December 18, 2019, 12:53pm
4
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 :
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 )
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
EvgenyK
(Evgeny Karpov)
December 19, 2019, 9:31am
6
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 ?
EvgenyK
(Evgeny Karpov)
December 23, 2019, 8:52am
9
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
EvgenyK
(Evgeny Karpov)
December 23, 2019, 9:13am
11
Can you retest this issue with the RO v9.6+ ?