An introduction to JSON and using it within RadStudio Delphi or C++ Builder.

JSON (JavaScript Object Notation) is a popular format for the encapsulation of structured data. If you’re familiar with XML, you can consider JSON as doing the same job fundamentally, though the two are very different as we’ll see in a moment.

The name JSON may lead to some confusion, it is a language independent means of representing data and is not exclusive to JavaScript. The reason for the name is that JSON was introduced by JavaScript first, and was made popular by its use in JavaScript Ajax applications.

Some of the appealing properties of the JSON format include:

  • It is human readable.
    Using JSON, gone are the days when we’d pack binary data into packets with encoding and decoding on each end. JSON is transmitted as text, either in ASCII or one of the Unicode text formats.
  • It is light weight.
    This is true in that it’s light weight when compared to other human readable encapsulations such as XML or HTML. This is due to the fact that JSON carries far less “padding” in the form of tags. JSON is still a heavier than non human readable encapsulation methods, because it’s text, and often encoded in variable length unicode formats.
  • It’s supported everywhere.
    JSON is at first supported everywhere where JavaScript is (which means at least every HTML5 supporting browser), and has been adopted just about everywhere else from PHP to C# to C++ and Delphi.
  • It’s easy to parse.
    Probably the biggest contributor to it’s adoption outside of JavaScript is how simple JSON is to parse.

So lets take a look now a what JSON actually looks like…

What I’m about to explain is the layman understanding of JSON. For more accuracy and detail you should reference the JSON standards document RFC-7159.
Lets see a sample of some JSON:


{
  "book": {
  "title": "Zen",
  "subtitle": "and The art of motorcycle maintenance.",
  "author": "Robert M Pirsig",
  "isbn": "9780061908019"
  }
}

What we can see here is an example of several attributes of a book encapsulated in JSON. Each piece of data is represented as a name and value pair, separated by a colon.


"name" : "value"

e.g.


"title" : "Zen"

We’ll call this piece of data a pair. The name and value portions of a pair are enclosed in quotation marks. We can have as many pairs as we wish, separating each from the next using a comma.

Database application developers might like to think of a pair as a field or column.

You can see that our pairs are surrounded by braces, and at the top there is what might look like another pair…


"book": {

}

While this might look like a pair, it’s right-hand side does not have a value enclosed in quotes, but instead it has an opening brace, several pairs, and then a closing brace. This structure is called an object. (of JavaScript OBJECT Notation)
An object in this case is a collection of pairs. As you’ll notice from the sample, this object is named ‘book’ and contains several pairs containing descriptive attributes of a book.

Objects may also be nested, as we’ll see later. Right now, we can already see an example of nesting because our object “book” is already enclosed within braces, those outer braces can be considered an object also, though it is an object without a name. This object must be present to contain the other objects within.

Database application developers might like to think of an object as a record or row, where nesting forms a master-detail relationship between them.

What if we want to encapsulate more than one object? Well you can do this very simply by separating the objects with a comma. Here’s the same sample from above, but this time containing two books…


{
  "book-1": {
  "title": "Zen",
  "subtitle": "and The art of motorcycle maintenance.",
  "author": "Robert M Pirsig",
  "isbn": "9780061908019"
},

"book-2": {
  "title": "Coding in Delphi",
  "subtitle": "",
  "author": "Nick Hodges",
  "isbn": "978-1941266038"
  }

In this sample I have two books named “book-1” and “book-2”. They are separated by a comma and are two entirely different objects. I had to rename them because, they’d have both been named “book”. Though I could have given them both the same name, this would be a bad data structure because “book” is not the type of object being represented, but the name of the object. If I have two objects named “book” I have no way to tell them apart!

Thankfully, we can represent multiple objects (of the same type, or otherwise) using an array…

The multi-book sample above should have been more correctly written as…


{
  "books": [
  {
    "title": "Zen",
    "subtitle": "and The art of motorcycle maintenance.",
    "author": "Robert M Pirsig",
    "isbn": "9780061908019"
  },
  {
    "title": "Coding in Delphi",
    "subtitle": "",
    "author": "Nick Hodges",
    "isbn": "978-1941266038"
  }]
}

In this sample you can see that we’re able to represent multiple un-named objects, separated by commas, wrapped in squared brackets []. This is how we represent an array of data objects, and for good form they should be uniform objects (the same type) though that is not a requirement.

Thus far we’ve seen three JSON structures, Object, Pair, and Array. Both Delphi and C++ Builder have corresponding classes named ‘TJSONObject’, ‘TJSONPair’, ‘TJSONArray’ respectively. In this next sample, we’re going to use these three classes to construct the JSON sample that we just looked at.


{
  "books": [
  {
    "title": "Zen",
    "subtitle": "and The art of motorcycle maintenance.",
    "author": "Robert M Pirsig",
    "isbn": "9780061908019"
  },
  {
    "title": "Coding in Delphi",
    "subtitle": "",
    "author": "Nick Hodges",
    "isbn": "978-1941266038"
  }]
}

Lets write some code to build this structure.

In your choice of Delphi or C++ Builder, start a new application and drop a button and a memo onto your main form.
(VCL or FMX is fine, I’m using VCL)

Now uses / include the JSON unit
In Pascal:

uses
  System.JSON;

In C++


include <System.JSON.hpp>

Add the following code to the button OnClick event…

In Pascal:

procedure TForm1.Button1Click(Sender: TObject);
var
  o: TJSONObject;
  a: TJSONArray;
  book: TJSONObject;
begin
  // Create the outer JSON object which parents the others.
  o := TJSONObject.Create;
  try
    // Create the books object, which contains the array of books...
    a := TJSONArray.Create();

    // add the array to the object.
    o.AddPair('books',a);

    // Create the first book
    book := TJSONObject.Create;
    book.AddPair( TJSONPair.Create('title','Zen') );
    book.AddPair( TJSONPair.Create('subtitle','and The art of motorcycle maintenance.') );
    book.AddPair( TJSONPair.Create('author','Robert M Pirsig') );
    book.AddPair( TJSONPair.Create('isbn','9780061908019') );
    // Add the book to the array
    a.AddElement(book);

    // Create the second book
    book := TJSONObject.Create;
    book.AddPair( TJSONPair.Create('title','Coding in Delphi') );
    book.AddPair( TJSONPair.Create('subtitle','') );
    book.AddPair( TJSONPair.Create('author','Nick Hodges') );
    book.AddPair( TJSONPair.Create('isbn','978-1941266038') );
    // Add the book to the array
    a.AddElement(book);

  finally
    Memo1.Lines.Text := o.ToString;
    o.Free;
  end;
end;

or in C++


void __fastcall TForm1::Button1Click(TObject *Sender)
{
  // Create the outer JSON object which parents the others.
  TJSONObject *o = new TJSONObject();
  __try {
		// Create the books object, which contains the array of books...
		TJSONArray *a = new TJSONArray();

		// add the array to the object.
		o->AddPair("books",a);

		// Create the first book
		TJSONObject *book = new TJSONObject();
		book->AddPair( new TJSONPair("title","Zen") );
		book->AddPair( new TJSONPair("subtitle","and The art of motorcycle maintenance.") );
		book->AddPair( new TJSONPair("author","Robert M Pirsig") );
		book->AddPair( new TJSONPair("isbn","9780061908019") );
		// Add the book to the array
		a->AddElement(book);

		// Create the second book
		book = new TJSONObject();
		book->AddPair( new TJSONPair("title","Coding in Delphi") );
		book->AddPair( new TJSONPair("subtitle","") );
		book->AddPair( new TJSONPair("author","Nick Hodges") );
		book->AddPair( new TJSONPair("isbn","978-1941266038") );
		// Add the book to the array
		a->AddElement(book);
	}
  __finally
	{
	  Memo1->Lines->Text = o->ToString();
	  o->Free();
	}
}

Now start up the application and press the button. Your memo should be populated with the JSON snippet as expected.


{"books":[{"title":"Zen","subtitle":"and The art of motorcycle maintenance.","author":"Robert M Pirsig","isbn":"9780061908019"},{"title":"Coding in Delphi","subtitle":"","author":"Nick Hodges","isbn":"978-1941266038"}]}

Oh, and it’s all bunched up and ugly looking. If you want it to look pretty again you could paste it into an online tool such as JsonSH and have it made human readable again!

You may have noticed something unusual in the code. When we add the array of books to the outer JSON object, we add it as a pair. Why? Well because an array is a type of pair, with a name on the left and an array of data as the value on the right. As such, TJSONArray is derrived from TJSONPair. In fact, even an object is a pair, which is how you nest objects inside each other.

Now that we’ve looked at how to build JSON structures and produce JSON strings, lets look at doing the reverse. Lets take our JSON code and parse it back into a structure that we can read from code…

Drop a second button on your form, and add the following code to it’s OnClick event.

In Pascal:

procedure TForm1.Button2Click(Sender: TObject);
var
  o: TJSONObject;
  a: TJSONArray;
  book: TJSONObject;
  idx: integer;
  idy: integer;
begin
  o := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(Memo1.Lines.Text),0) as TJSONObject;
  try
    a := TJSONArray(o.Get('books').JsonValue);
    for idx := 0 to pred(a.size) do begin
      book := TJSONObject(a.Get(idx));
      for idy := 0 to pred(book.Count) do begin
        ShowMessage( book.Pairs[idy].JsonString.ToString + ':' + book.Pairs[idy].JsonValue.ToString );
      end;
    end;
  finally
    o.Free;
  end;
end;

In C++:


void __fastcall TForm1::Button2Click(TObject *Sender)
{
  TJSONObject *o = (TJSONObject*) TJSONObject::ParseJSONValue(TEncoding::ASCII->GetBytes(Memo1->Lines->Text),0);
  __try {
	TJSONArray *a = (TJSONArray*) o->Get("books")->JsonValue;
	for (int idx = 0; idx < a->Size(); idx++) {
	  TJSONObject *book = (TJSONObject*) a->Get(idx);
	  for (int idy = 0; idy < book->Count; idy++) {
		ShowMessage( book->Pairs[idy]->JsonString->ToString() + ':' +
					 book->Pairs[idy]->JsonValue->ToString() );
	  }
	}
  }
  __finally {
	o->Free();
  }
}

Now run the application and click both button1 and button2 in succession.
You’ll be presented with a series of message boxes containing the values of the two book entries.

You now have an example of building and consuming JSON data, go forth and notate your objects.

Thanks for reading!