Hi,
Is it possible to have complex result types for REST API methods that can be serialized to JSON?
I have tried with the following:
[ROServiceMethod]
[ROCustom('HttpApiPath','patients/')]
[ROCustom('HttpApiMethod','GET')]
function GetPatientListAPI: TROArray<Patient>;
where Patient is declared as
PersonName = class(TROComplexType)
published
property GivenName : string read fGiveName write fGivenName;
property FamilyName : string read fFamilyName write fFamilyName;
// etc
end;
Patient = class(TROComplexType)
published
property ID : string read fID write fID;
property NHSNumber : string read fNHSNumber write fNHSNumber;
property Name : PersonName read fName write fName;
// etc
end;
The result I’d like to see is :
[
{ ID:"1", "NHSNumber":"111111111", "Name":{"GivenName":"John","FamilyName":"Doe"},"Address":{...}} ,
{ ID:"2", "NHSNumber":"222222222", "Name":{"GivenName":"Jane","FamilyName":"Doe"},"Address":{...}}
]
But the patients/
path doesn’t appear in the swagger definition at localhost:8099/api
??
Thanks,
Stuart
EvgenyK
(Evgeny Karpov)
April 10, 2019, 9:17am
2
Hi,
I don’t see any problems with it:
what RO/DA version you are using?
Hi,
My apologies, I missed out an important code line from the above
My api method has a single query parameter:
function GetPatientListAPI(
[ROCustom('HttpApiQueryParameter','')] ID: String
) : TROArray<Patient>;
Without the parameter. the method shows up in the swagger api. With the query parameter declared, it isn’t shown.
I’m on v9.555.111.1399 & Delphi 10.3.1.
Thanks
EvgenyK
(Evgeny Karpov)
April 10, 2019, 9:35am
4
pass 1
as second parameter for HttpApiQueryParameter
:
function GetPatientListAPI(
[ROCustom('HttpApiQueryParameter','1')] ID: String
) : TROArray<Patient>;
Thanks - I got that working, but my method returns an access violation now:
Access violation at address 656D614E in module 'AquilaServer.exe'. Read of address 656D614E
This is at line 911 of uRORTTIServerSupport.pas
.
FreeOrDisposeOf(__lObjectDisposer);
The method is implemented as follows:
function TAPIService.GetPatientByID(const id: string): TROArray<Patient>;
var
aPatient : Patient;
begin
result := TROArray<Patient>.Create;
aPatient := Patient.Create;
aPatient.PATIENT_ID := id;
aPatient.Name.GivenName := 'Stuart';
aPatient.Name.FamilyName := 'Clennett';
aPatient.GENDER := 'MAL';
result.add(aPatient);
end;
(This is just for testing, data from the database eventually of course)
The classes are declared as:
PersonName = class(TROComplexType)
private
fFamilyName: string;
fGivenName: string;
published
property GivenName : string read fGivenName write fGivenName;
property FamilyName : string read fFamilyName write fFamilyName;
end;
PostalAddress = class(TROComplexType)
private
fPOSTCODE_2: string;
fPOSTCODE_1: string;
published
property POSTCODE_1 : string read fPOSTCODE_1 write fPOSTCODE_1;
property POSTCODE_2 : string read fPOSTCODE_2 write fPOSTCODE_2;
end;
Patient = class(TROComplexType)
private
fNHS_NUMBER: string;
fHOSPITAL_NUMBER: string;
fGENDER: string;
fPATIENT_ID: string;
fDATE_OF_BIRTH: string;
fName: PersonName;
fAddress: PostalAddress;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
published
property PATIENT_ID : string read fPATIENT_ID write fPATIENT_ID;
property NHS_NUMBER : string read fNHS_NUMBER write fNHS_NUMBER;
property HOSPITAL_NUMBER : string read fHOSPITAL_NUMBER write fHOSPITAL_NUMBER;
property Name : PersonName read fName;
property Address : PostalAddress read fAddress;
property DATE_OF_BIRTH : string read fDATE_OF_BIRTH write fDATE_OF_BIRTH;
property GENDER : string read fGENDER write fGENDER;
end;
procedure Patient.AfterConstruction;
begin
inherited;
fName := PersonName.Create;
fAddress := PostalAddress.Create;
end;
procedure Patient.BeforeDestruction;
begin
inherited;
fName.Free;
fAddress.Free;
end;
Thanks for your continued help,
EvgenyK
(Evgeny Karpov)
April 10, 2019, 10:03am
6
destroy objects with FreeAndNil
or assign nil
after you destroyed it manually.
procedure Patient.BeforeDestruction;
begin
inherited;
FreeAndNil(fName);
FreeAndNil(fAddress);
end;
Hi Evgeny,
Ok, I’m getting data back now from the method, but it’s missing the Name
and Address
properties.
E.g.
result := Patient.Create;
result.PATIENT_ID := id;
result.Name.GivenName := 'Stuart';
result.Name.FamilyName := 'Clennett';
result.Address.Postcode_1 := 'AB1';
result.Address.Postcode_2 := '3SW'
result.GENDER := 'MAL';
is producing:
{
DATE_OF_BIRTH: "",
GENDER: "MAL",
HOSPITAL_NUMBER: "",
NHS_NUMBER: "",
PATIENT_ID: "abc123",
VULNERABLE_PERSON: false
}
Is there some attribute I need to apply to the Name and Address properties in the Patient class to enable it to stream properly ?
Thanks
EvgenyK
(Evgeny Karpov)
April 10, 2019, 3:05pm
8
specify them as r/w properties like
property Name : PersonName read fName write fName;
property Address : PostalAddress read fAddress write fAddress;
[
{
"Address": {
"POSTCODE_1": "",
"POSTCODE_2": ""
},
"DATE_OF_BIRTH": "",
"GENDER": "MAL",
"HOSPITAL_NUMBER": "",
"Name": {
"FamilyName": "Clennett",
"GivenName": "Stuart"
},
"NHS_NUMBER": "",
"PATIENT_ID": "100"
}
]
1 Like
Great - working like a charm !!!
Thanks very much
wuping
(wuping)
May 10, 2019, 10:21pm
10
What does that “1” mean? Any other valid inputs, like 2, 3?
EvgenyK
(Evgeny Karpov)
May 13, 2019, 8:27am
11
wuping:
What does that “1” mean?
it means True
. others values will be ignored:
else if p.Attributes.Values[rodl_HttpApiQueryParameter] = '1' then
fQueryParameters.Add(p)
else if p.Attributes.Values[rodl_HttpApiHeaderParameter] = '1' then
fHeaderParameters.Add(p)