First steps with REST and JSON

I must admit that I haven’t done much programming regarding the web. There was an attempt to write an RSS reader some time back and there is my virtual radio alarm clock (with which I’m currently listening to SWR3). But this is my first try to do something with REST and JSON. The server side is provided by the Delphi Dabbler SWAG archive the client is written with Delphi 10.1 Berlin.

At first I tried to make sense from the tutorial provided by Embarcadero but I found it rather confusing and it stops exactly when actually trying to access the data returned in a meaningful way.

So, I threw away the TRESTxxx components I had put on the form and went back to some actual programming rather than dropping components and setting properties without understanding what I am doing. Here is what I ended up with:

swagbrowser

The program itself consists of two parts: Requesting data from the server and interpreting its answers. Requests are sent via http using a TidHttp object which is dynamically created and destroyed for each request.

procedure Tf_DelphiDabblerSwag.doRequest(const _Command: string; out _Response: TJSONObject);
var
  HTTP: TIdHTTP;
  JSON: string;
begin
  HTTP := TIdHTTP.Create(nil);
  try
    JSON := HTTP.Get('http://swag.delphidabbler.com/api/v1/' + _Command);
//    m_Result.Lines.text := JSON;
  finally
    FreeAndNil(HTTP);
  end;
  _Response := TJSONObject.ParseJSONValue(JSON) as TJSONObject;
end;

It expects a Command which is simply sent to the server by appending it to the API url as described in the documentation.

It returns a TJSONObject which is then interpreted by the caller. (I don’t do much error handling here, it’s merely a learning experiment.)

The API is simple, it knows only 4 different commands:

  • categories
  • snippet
  • snippet-count
  • snippets

I implemented each of them as a method:

categories

procedure Tf_DelphiDabblerSwag.RequestCategories(_Categories: TObjectList<TIdTitleObj>);
var
  JObj: TJSONObject;
  JCategories: TJSONArray;
  i: Integer;
  JCategory: TJSONObject;
  Category: TIdTitleObj;
begin
  doRequest('categories', JObj);
  try
    m_Result.Lines.Add('status: ' + JObj.GetValue('status').Value);
    m_Result.Lines.Add('command: ' + JObj.GetValue('command').Value);
    JCategories := JObj.Get('categories').JsonValue as TJSONArray;
    for i := 0 to JCategories.Count - 1 do begin
      JCategory := JCategories.Items[i] as TJSONObject;
//        m_Result.Lines.Add(Format('Category: (ID: %s, Title: %s', [JCategory.GetValue('id').Value, JCategory.GetValue('title').Value]));
      Category := TIdTitleObj.Create(JCategory.GetValue('id').Value, JCategory.GetValue('title').Value);
      _Categories.Add(Category);
    end;
  finally
    FreeAndNil(JObj);
  end;
end;

The first thing the program does is getting a list of all categories. For that it sends the “category” command to the server and interprets its response.

{
  "status":"ok",
  "command":"categories",
  "categories":[
    {
      "id":"ANSI",
      "title":"ANSI Control & Ouput"
    },
    {
      "id":"ARCHIVES",
      "title":"Archive Handling"
    },

    ... more category elements ...

  ]
}

All categories are stored in a TObjectList and returned to the caller. It took me a while to figure out how to access the array of categories: You access a name value entry and type cast it to TJSONArray. After I understood that, everything else was easy.

snippets

Next is getting a list of all snippets of a category.

procedure Tf_DelphiDabblerSwag.RequestSnippets(const _CategoryId: string; _Snippets: TObjectList<TIdTitleObj>);
var
  JObj: TJSONObject;
  i: Integer;
  JSnippets: TJSONArray;
  JSnippet: TJSONObject;
  Snippet: TIdTitleObj;
begin
  _Snippets.Clear;
  doRequest('snippets/' + _CategoryId + '?fields=id,title', JObj);
  m_Result.Lines.Add('status: ' + JObj.GetValue('status').Value);
  m_Result.Lines.Add('command: ' + JObj.GetValue('command').Value);
  JSnippets := JObj.Get('snippets').JsonValue as TJSONArray;
  for i := 0 to JSnippets.Count - 1 do begin
    JSnippet := JSnippets.Items[i] as TJSONObject;
    Snippet := TIdTitleObj.Create(JSnippet.GetValue('id').Value, JSnippet.GetValue('title').Value);
//    m_Result.Lines.Add(Format('Snippet: (ID: %s, Title: %s', [JSnippet.GetValue('id').Value, JSnippet.GetValue('title').Value]));
    _Snippets.Add(Snippet);
  end;
end;

The “snippets” command needs a some parameters:

  • The id of the category
  • The fields we are interested in

They are appended to the command.

Then again, we simply parse the result and return it in a TObjectList.

{
  "status":"ok",
  "command":"snippets",
  "snippets":[
    {
      "id":491,
      "title":"Getting the Line number in a memo Field",
    },
    {
      "id":492,
      "title":"Capturing the Desktop to a form",
    },
    {
      "id":493,
      "title":"File Copying in DELPHI",
    },{
      "id":494,
      "title":"File Manager Drag\/Drop",
    }
  ]
}

snippet

The last part is getting a snippet’s text.

procedure Tf_DelphiDabblerSwag.RequestSnippetText(const _SnippetId: string; out _Text: string);
var
  JObj: TJSONObject;
  JSnippet: TJSONObject;
begin
  doRequest('snippet/' + _SnippetId + '?fields=source_code', JObj);
  try
    m_Result.Lines.Add('status: ' + JObj.GetValue('status').Value);
    m_Result.Lines.Add('command: ' + JObj.GetValue('command').Value);
    JSnippet := JObj.GetValue('snippet') as TJSONObject;
    m_Snippet.Lines.Text := JSnippet.GetValue('source_code').Value;
  finally
    FreeAndNil(JObj);
  end;
end;

The “snippet” command requires a snipped id and we also must specify the fields we want to get. In this case we only want the source_code.

{
  "status":"ok",
  "command":"snippet",
  "snippet":{
    "source_code":"\r\n{This way uses a File stream.}\r\nProcedure ..."
  }
}

The response is then parsed and returned as a string. Note that “snippet” is an object, so in order to get the source code we need to type cast the snippet value to a TJSONObject before we can access its source_code value.

That’s about it. The rest of the program is a few event handlers that call the methods described above and fill the result into the GUI. I won’t get into that, it’s boring.

The source code is available for download here and in case you simply want to browse the SWAG archive using that program, there is also a precompiled executable

As I already said in the title: These are my first steps. I’m pretty satisfied with the result, but I’m sure I made some mistakes and there are probably easier ways to implement this.