I must be overlooking something in my web service...
Data structures:
TResIDNameRec = record
id : Integer;
name: String;
end;
TResIDNameArr = array of TResIDNameRec;
TResBase = class(TJSONStructure) // just a class(TObject)
private
public
&type : String;
success : Integer;
errormessage: String;
threadid : Integer;
constructor Create; overload; // empty
constructor Create(ADescendantClassName: String); overload; // sets &type
end;
TResClass = class of TResBase;
TResGetList = class(TResBase)
private
public
activities,
projects,
customers: TResIDNameArr;
constructor Create; overload; // inherited Create(Self.ClassName);
end;
and then a
type
TWebAct = (
ttlogin,
...
ttgetlist,
...
ttgetversion
);
const
cWebActStructures: Array[TWebAct] of
record
RequestClass : TReqClass;
ResponseClass: TResClass;
end
= (
{ ttlogin } (RequestClass: TReqLogin; ResponseClass: TResLogin;),
...
{ ttgetlist } (RequestClass: TReqGetList; ResponseClass: TResGetList;),
...
{ ttgetversion} (RequestClass: TReqGetVersion; ResponseClass: TResGetVersion;)
);
I have a private field on my WebModule:
FResponse: TResBase; // Response object
In the WebmoduleBeforeDispatch, FWebAct is a ttgetlist, and I do:
lResponseClass := cWebActStructures[FWebAct].ResponseClass;
FResponse := lResponseClass.Create(lResponseClass.ClassName);
// FResponse.ClassName is TResGetList, OK
In the OnAction handler for the TWebActionItem that handles the 'ttgetlist', here is the essential part:
var
lListArr : TResIDNameArr;
begin
lListArr := (FResponse as TResGetList).activities;
with AClientDataSet do
begin
Open;
SetLength(lListArr,RecordCount); // Pre-allocate (currently 152)
l := 0;
First;
while not EOF do
begin
if SomeConditionMet then
begin
lListArr[l].id := FieldByName(lIDVeld).AsInteger;
lListArr[l].name := FieldByName(SName).AsString;
Inc(l);
end;
Next;
end;
Close;
SetLength(lListArr,l+1); // Adjust (l is now 108)
end;
FResponse.success := 1;
lListArr shows correct data at this moment, object inspector shows:
((3, 'Activiteit 3'), (4, 'Activiteit 3.1'), (5, 'Activiteit 3.2'), (10, 'Activiteit 3.3'), (8, 'Activiteit 5'),...
In WebmoduleAfterDispatch:
var lJSO: ISuperObject;
begin
lJSO := FResponse.ToJson;
LJSO.AsString does have the correct fields (&type, success, ... activities, ... ), and e.g. success=1, but activities is empty:
{
"errormessage":"",
"success":1,
"projects":[],
"threadid":1556,
"type":"ttgetlistresult",
"customers":[],
"activities":[]
}
What am I overlooking? Is maybe the lListArr := (FResponse as TResGetList).activities
in the OnAction handler 'not good enough'?
BTW Feel free to update the question title, this was the best I could come up with.
When you assign
lListArr
, the local variable receives a reference to the same array that theactivities
field references. However, when you callSetLength(lListArr, ...)
, that makeslListArr
refer to a new array, completely separate from the one it referenced before.If you want the field to refer to the same array, you need to assign it back to the field:
You can make that assignment at any time after the length has been set. Changes the the contents of the array will be visible through either variable. Changing the length is not the same as changing the contents, though. Changing the length re-allocates the entire array and gives a new, unique array.