# 10. JSON and RESTful Fundamentals
The Language of mORMot
Before exploring the Client-Server architecture, we need to understand the two key standards that mORMot builds upon: JSON for data interchange and REST for API design.
mORMot uses JSON (JavaScript Object Notation) as its primary data format:
| Advantage | Description |
|---|---|
| Human-readable | Easy to debug and inspect |
| Compact | Smaller than XML for most use cases |
| Fast parsing | In-place parsing without memory allocation |
| Native UTF-8 | Matches SQLite3 and web standards |
| Universal | Supported by all languages/platforms |
| AJAX-ready | Native to JavaScript/browser apps |
{
"string": "Hello UTF-8 World",
"number": 42,
"float": 3.14159,
"boolean": true,
"null": null,
"array": [1, 2, 3],
"object": {"nested": "value"}
}
mORMot follows the JSON standard with some extensions:
Int64 support (no JavaScript 53-bit limit)TDateTime, TTimeLog, Currency serialization// Extended syntax (valid in mORMot)
{name: "John", age: 30}
// Standard JSON (always valid)
{"name": "John", "age": 30}
uses
mormot.core.json;
var
i: Integer;
d: Double;
s: RawUtf8;
json: RawUtf8;
begin
// To JSON
json := FormatJson('{name:?,age:?,score:?}', [], ['John', 30, 95.5]);
// Result: {"name":"John","age":30,"score":95.5}
// From JSON
JsonDecode(pointer(json), ['name', 'age', 'score'], @Values);
end;
Records are automatically serialized via RTTI (Delphi 2010+):
type
TPerson = record
Name: RawUtf8;
Age: Integer;
Email: RawUtf8;
end;
var
Person: TPerson;
json: RawUtf8;
begin
Person.Name := 'John';
Person.Age := 30;
Person.Email := 'john@example.com';
// Record to JSON
json := RecordSaveJson(Person, TypeInfo(TPerson));
// Result: {"Name":"John","Age":30,"Email":"john@example.com"}
// JSON to Record
RecordLoadJson(Person, pointer(json), TypeInfo(TPerson));
end;
type
TPersonArray = array of TPerson;
var
People: TPersonArray;
json: RawUtf8;
begin
SetLength(People, 2);
People[0].Name := 'John';
People[1].Name := 'Jane';
// Array to JSON
json := DynArraySaveJson(People, TypeInfo(TPersonArray));
// Result: [{"Name":"John",...},{"Name":"Jane",...}]
// JSON to Array
DynArrayLoadJson(People, pointer(json), TypeInfo(TPersonArray));
end;
For flexible JSON handling:
var
doc: Variant;
begin
// Create from JSON
doc := _JsonFast('{"name":"John","tags":["admin","user"]}');
// Access via late-binding
WriteLn(doc.name); // 'John'
WriteLn(doc.tags._Count); // 2
// Modify
doc.email := 'john@example.com';
doc.tags._Add('moderator');
// Back to JSON
WriteLn(doc); // Full JSON string
end;
Classes are serialized via published properties:
type
TMyClass = class(TSynPersistent)
private
fName: RawUtf8;
fValue: Integer;
published
property Name: RawUtf8 read fName write fName;
property Value: Integer read fValue write fValue;
end;
var
Obj: TMyClass;
json: RawUtf8;
begin
Obj := TMyClass.Create;
Obj.Name := 'Test';
Obj.Value := 42;
json := ObjectToJson(Obj);
// Result: {"Name":"Test","Value":42}
JsonToObject(Obj, pointer(json), Valid);
end;
REST (Representational State Transfer) defines how resources are addressed and manipulated over HTTP:
| HTTP Method | CRUD | ORM Method | Description |
|---|---|---|---|
GET |
Read | Retrieve() |
Get resource(s) |
POST |
Create | Add() |
Create new resource |
PUT |
Update | Update() |
Update existing resource |
DELETE |
Delete | Delete() |
Remove resource |
http://server:port/root/TableName/ID
│ │ │
│ │ └── Record ID (optional)
│ └── TOrm class name
└── Model.Root
Examples:
GET /api/Customer → List all customers
GET /api/Customer/123 → Get customer #123
POST /api/Customer → Create new customer (body = JSON)
PUT /api/Customer/123 → Update customer #123
DELETE /api/Customer/123 → Delete customer #123
mORMot is REST-oriented but supports both paradigms:
| Feature | REST Style | RPC Style |
|---|---|---|
| ORM operations | URI + HTTP verbs | URI + HTTP verbs |
| Method services | GET/POST /root/Method |
POST /root/Method |
| Interface services | /root/Interface.Method |
JSON-RPC body |
var
Customer: TOrmCustomer;
json: RawUtf8;
begin
Customer := TOrmCustomer.Create;
Customer.Name := 'ACME Corp';
Customer.Email := 'contact@acme.com';
// Single record
json := Customer.GetJsonValues(True, True, ooSelect);
// Result: {"ID":0,"Name":"ACME Corp","Email":"contact@acme.com"}
// Selected fields only
json := Customer.GetJsonValues(True, True, ooSelect, 'Name,Email');
end;
var
Customer: TOrmCustomer;
begin
Customer := TOrmCustomer.Create;
Customer.FillFrom('{"Name":"ACME Corp","Email":"contact@acme.com"}');
end;
var
json: RawUtf8;
begin
// Direct JSON from query
json := Server.Orm.RetrieveListJson(TOrmCustomer,
'Country = ?', ['USA'], 'Name,Email');
// Result: [{"Name":"John","Email":"john@..."},...]
end;
type
TMyServer = class(TRestServerDB)
published
procedure Sum(Ctxt: TRestServerUriContext);
end;
procedure TMyServer.Sum(Ctxt: TRestServerUriContext);
var
A, B: Integer;
begin
A := Ctxt.InputInt['a'];
B := Ctxt.InputInt['b'];
Ctxt.Returns(['result', A + B]);
end;
// Client call: GET /api/Sum?a=10&b=20
// Response: {"result":30}
type
ICalculator = interface(IInvokable)
['{...}']
function Add(A, B: Integer): Integer;
end;
// Server registration
Server.ServiceDefine(TCalculator, [ICalculator], sicShared);
// Client call (automatic JSON marshalling)
var
Calc: ICalculator;
begin
Client.Services.Resolve(ICalculator, Calc);
Result := Calc.Add(10, 20); // JSON: {"result":30}
end;
| Format | Use Case | Trade-off |
|---|---|---|
| JSON | Interoperability, debugging | Larger, slower parsing |
| Binary | Internal, large data | Smaller, faster |
| SynLZ+JSON | Compression over network | Best of both |
mORMot clients automatically negotiate compression:
// Server enables compression (default)
HttpServer.Compress := [hcSynLZ, hcDeflate];
// SynLZ is 20x faster than Deflate for compression
// Delphi clients use SynLZ automatically
// AJAX clients fall back to Deflate
// Binary record save (faster than JSON)
Binary := RecordSave(Person, TypeInfo(TPerson));
RecordLoad(Person, pointer(Binary), TypeInfo(TPerson));
// Binary with compression
Binary := RecordSaveBase64(Person, TypeInfo(TPerson), True);
// SLOW: Multiple conversions
s := Utf8ToString(json);
json2 := StringToUtf8(s);
// FAST: Stay in UTF-8
ProcessRawUtf8(json);
// SLOW: Variant access
value := doc.field;
// FAST: Direct typed access
value := _Safe(doc)^.I['field']; // Integer
value := _Safe(doc)^.U['field']; // RawUtf8
// SLOW: Create/Free per iteration
for i := 1 to 1000 do
begin
Obj := TMyClass.Create;
try
JsonToObject(Obj, pointer(json[i]), Valid);
Process(Obj);
finally
Obj.Free;
end;
end;
// FAST: Reuse single instance
Obj := TMyClass.Create;
try
for i := 1 to 1000 do
begin
Obj.ClearProperties; // Reset fields
JsonToObject(Obj, pointer(json[i]), Valid);
Process(Obj);
end;
finally
Obj.Free;
end;
| mORMot 1 | mORMot 2 |
|---|---|
JSONToObject() |
JsonToObject() |
ObjectToJSON() |
ObjectToJson() |
RecordSaveJSON() |
RecordSaveJson() |
RecordLoadJSON() |
RecordLoadJson() |
DynArraySaveJSON() |
DynArraySaveJson() |
JSONDecode() |
JsonDecode() |
| Feature | mORMot 2 Unit |
|---|---|
| JSON parsing | mormot.core.json |
| TDocVariant | mormot.core.variants |
| Record serialization | mormot.core.rtti |
| HTTP client/server | mormot.net.client / mormot.net.server |
Next Chapter: Client-Server Architecture
| Previous | Index | Next |
|---|---|---|
| Chapter 9: External NoSQL Database Access | Index | Chapter 11: Client-Server Architecture |