TSQLTableJSON will expect some JSON content as input, will parse it in rows and columns, associate it with one or more optional TSQLRecord class types, then will let you access the data via its Get* methods.

You can use this TSQLTableJSON class as in the following example:

procedure WriteBabiesStartingWith(const Letters: RawUTF8; Sex: TSex);
var aList: TSQLTableJSON;
    Row: integer;
begin
  aList := Client.MultiFieldValues(TSQLBaby,'ID,BirthDate',
    'Name LIKE ? AND Sex = ?',[Letters+'%',ord(Sex)]);
  if aList=nil then
    raise Exception.Create('Impossible to retrieve data from Server');
  try
    for Row := 1 to aList.RowCount do
      writeln('ID=',aList.GetAsInteger(Row,0),' BirthDate=',aList.Get(Row,1));
  finally
    aList.Free;
  end;
end;

For a record with a huge number of fields, specifying the needed fields could save some bandwidth. In the above sample code, the ID column has a field index of 0 (so is retrieved via aList.GetAsInteger(Row,0)) and the BirthDate column has a field index of 1 (so is retrieved as a PUTF8Char via aList.Get(Row,1)). All data rows are processed via a loop using the RowCount property count - first data row is indexed as 1, since the row 0 will contain the column names.

The TSQLTable class has some methods dedicated to direct cursor handling, as such:

procedure WriteBabiesStartingWith(const Letters: RawUTF8; Sex: TSex);
var aList: TSQLTableJSON;
begin
  aList := Client.MultiFieldValues(TSQLBaby,'ID,BirthDate',
    'Name LIKE ? AND Sex = ?',[Letters+'%',ord(Sex)]);
  try
    while aList.Step do
      writeln('ID=',aList.Field(0),' BirthDate=',aList.Field(1));
  finally
    aList.Free;
  end;
end;

By using the TSQLTable.Step method, you do not need to check that aList<>nil, since it will return false if aList is not assigned. And you do not need to access the RowCount property, nor specify the current row number.

We may have used not the field index, but the field name, within the loop:

      writeln('ID=',aList.Field('ID'),' BirthDate=',aList.Field('BirthDate'));

You can also access the field values using late-binding and a local variant, which gives some perfectly readable code:

procedure WriteBabiesStartingWith(const Letters: RawUTF8; Sex: TSex);
var baby: variant;
begin
  with Client.MultiFieldValues(TSQLBaby,'ID,BirthDate'
    'Name LIKE ? AND Sex = ?',[Letters+'%',ord(Sex)]) do
  try
    while Step(false,@baby) do
      writeln('ID=',baby.ID,' BirthDate=',baby.BirthDate);
  finally
    Free;
  end;
end;

In the above code, late-binding will search for the "ID" and "BirthDate" fields at runtime. But the ability to write baby.ID and baby.BirthDate is very readable. Using a with ... do statement makes the code shorter, but should be avoided if it leads into confusion, e.g. in case of more complex process within the loop.

See also the following methods of TSQLRest: OneFieldValue, OneFieldValues, MultiFieldValue, MultiFieldValues which are able to retrieve either a TSQLTableJSON, or a dynamic array of integer or RawUTF8. And also List and ListFmt methods of TSQLRestClient, if you want to make a JOIN against multiple tables at once.

Feedback is welcome in our forum, as usual.