Complex result types from HttpAPI Methods

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

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 :frowning:

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

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,

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

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

What does that “1” mean? Any other valid inputs, like 2, 3?

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)