Become a member!

Supporto JSON in Delphi: Guida Completa con Esempi (2025)

JSON (JavaScript Object Notation) è lo standard de-facto per lo scambio di dati nelle applicazioni moderne. Che tu stia costruendo REST API, leggendo file di configurazione o comunicando con web services, comprendere come lavorare con JSON in Delphi è essenziale.

Questa guida completa copre tutto ciò che devi sapere sul supporto JSON in Delphi, con esempi completi e compilabili che puoi usare nei tuoi progetti.

Tutti gli esempi di codice in questo articolo sono stati testati e verificati con Delphi 13 Florence.
📝
Questo articolo si concentra sul parser JSON in stile DOM (TJSONObject, TJSONArray). Per il parsing in stile streaming/SAX, consulta le unit System.JSON.Readers e System.JSON.Writers.

Compatibilità tra le Versioni di Delphi

Il supporto JSON si è evoluto significativamente tra le varie versioni di Delphi:

Versione Unit Caratteristiche Principali
Delphi 2009 DBXJSON Supporto JSON iniziale con classi base
Delphi XE6 System.JSON Unit rinominata, API migliorata
Delphi 10.1 Berlin System.JSON API fluent TJSONObjectBuilder, miglioramenti a TryGetValue<T>
Delphi 10.3 Rio System.JSON Metodo Format(), EJSONParseException con dettagli, miglioramenti delle prestazioni
Delphi 11-12 System.JSON Ulteriori ottimizzazioni e perfezionamenti
Delphi 13 Florence System.JSON Ultimi miglioramenti e supporto continuo
💡
Tutti gli esempi in questo articolo sono compatibili con Delphi XE7 e successivi salvo diversa indicazione. Le funzionalità specifiche per versione sono chiaramente contrassegnate.

Cos’è JSON?

JSON è un formato leggero di scambio dati basato su testo. È facile da leggere e scrivere per gli umani, e facile da analizzare e generare per le macchine. Un documento JSON può contenere:

  • Oggetti: Coppie chiave-valore racchiuse tra parentesi graffe {}
  • Array: Liste ordinate di valori racchiuse tra parentesi quadre []
  • Valori: Stringhe, numeri, booleani (true/false), null, oggetti o array

Esempio di struttura JSON:

{
  "name": "Daniele Teti",
  "age": 45,
  "active": true,
  "skills": ["Delphi", "Python", "SQL"],
  "address": {
    "city": "Rome",
    "country": "Italy"
  }
}

Panoramica delle Classi JSON di Delphi

Delphi fornisce supporto JSON integrato tramite l’unit System.JSON. Le classi principali sono:

Classe Descrizione
TJSONValue Classe base per tutti i tipi di valori JSON
TJSONObject Rappresenta un oggetto JSON (coppie chiave-valore)
TJSONArray Rappresenta un array JSON (lista ordinata)
TJSONString Rappresenta un valore stringa JSON
TJSONNumber Rappresenta un valore numerico JSON
TJSONBool Rappresenta un valore booleano JSON
TJSONNull Rappresenta un valore null JSON
TJSONPair Rappresenta una coppia chiave-valore in un oggetto

Creazione di Oggetti JSON

Iniziamo dalle basi: creare oggetti JSON e aggiungere proprietà.

Creazione Base di Oggetti JSON

L’operazione più fondamentale è creare un TJSONObject e aggiungervi coppie chiave-valore. Delphi fornisce pratici overload di AddPair che accettano direttamente stringhe, interi, booleani e double - non è necessario avvolgere i valori primitivi in classi specifiche per JSON. L’esempio seguente dimostra come costruire un semplice oggetto JSON contenente informazioni personali con vari tipi di dati:

program JSONCreateBasic;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

var
  LJSONObject: TJSONObject;
begin
  LJSONObject := TJSONObject.Create;
  try
    // Aggiungi proprietà stringa
    LJSONObject.AddPair('firstName', 'Daniele');
    LJSONObject.AddPair('lastName', 'Teti');

    // Aggiungi proprietà numerica (sono disponibili overload per Integer, Int64, Double)
    LJSONObject.AddPair('age', 45);

    // Aggiungi proprietà booleana
    LJSONObject.AddPair('active', True);

    // Aggiungi proprietà null (nessun overload - deve usare TJSONNull)
    LJSONObject.AddPair('middleName', TJSONNull.Create);

    // Stampa il JSON
    // Nota: Format() disponibile da Delphi 10.3 Rio
    {$IF CompilerVersion >= 33.0} // Delphi 10.3 Rio
    WriteLn(LJSONObject.Format());
    {$ELSE}
    WriteLn(LJSONObject.ToString);
    {$ENDIF}
  finally
    LJSONObject.Free;
  end;

  ReadLn;
end.

Output:

{
    "firstName": "Daniele",
    "lastName": "Teti",
    "age": 45,
    "active": true,
    "middleName": null
}

Creazione di Array JSON

Gli array JSON sono collezioni ordinate che possono contenere qualsiasi combinazione di valori - stringhe, numeri, booleani o anche altri array e oggetti. Quando aggiungi un TJSONArray a un TJSONObject usando AddPair, l’oggetto genitore assume la proprietà dell’array, quindi devi liberare solo l’oggetto root. Questo esempio mostra come creare sia un array omogeneo di stringhe che un array a tipo misto:

program JSONCreateArray;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

var
  LJSONObject: TJSONObject;
  LContacts: TJSONArray;
  LSkills: TJSONArray;
begin
  LJSONObject := TJSONObject.Create;
  try
    LJSONObject.AddPair('name', 'Daniele Teti');

    // Crea array di stringhe
    LSkills := TJSONArray.Create;
    LJSONObject.AddPair('skills', LSkills);
    LSkills.Add('Delphi');
    LSkills.Add('Python');
    LSkills.Add('SQL');

    // Crea array con tipi misti
    LContacts := TJSONArray.Create;
    LJSONObject.AddPair('contacts', LContacts);
    LContacts.Add('daniele@example.com');  // stringa
    LContacts.Add(123456);                 // numero
    LContacts.Add(True);                   // booleano

    {$IF CompilerVersion >= 33.0}
    WriteLn(LJSONObject.Format());
    {$ELSE}
    WriteLn(LJSONObject.ToString);
    {$ENDIF}
  finally
    LJSONObject.Free; // Libera anche LContacts e LSkills
  end;

  ReadLn;
end.

Output:

{
    "name": "Daniele Teti",
    "skills": [
        "Delphi",
        "Python",
        "SQL"
    ],
    "contacts": [
        "daniele@example.com",
        123456,
        true
    ]
}

Creazione di un Array di Oggetti

Uno dei pattern più comuni nel JSON del mondo reale è un array contenente più oggetti - pensa a una lista di utenti, prodotti o qualsiasi collezione di record. Ogni oggetto nell’array può avere il proprio insieme di proprietà. Quando costruisci questa struttura, crei ogni oggetto separatamente e lo aggiungi all’array usando il metodo Add. L’array possiede quindi tutti gli oggetti che aggiungi:

program JSONArrayOfObjects;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

var
  LRoot: TJSONObject;
  LUsers: TJSONArray;
  LUser: TJSONObject;
begin
  LRoot := TJSONObject.Create;
  try
    LUsers := TJSONArray.Create;
    LRoot.AddPair('users', LUsers);

    // Primo utente
    LUser := TJSONObject.Create;
    LUsers.Add(LUser);
    LUser.AddPair('id', 1);
    LUser.AddPair('name', 'Alice');
    LUser.AddPair('email', 'alice@example.com');

    // Secondo utente
    LUser := TJSONObject.Create;
    LUsers.Add(LUser);
    LUser.AddPair('id', 2);
    LUser.AddPair('name', 'Bob');
    LUser.AddPair('email', 'bob@example.com');

    // Terzo utente
    LUser := TJSONObject.Create;
    LUsers.Add(LUser);
    LUser.AddPair('id', 3);
    LUser.AddPair('name', 'Charlie');
    LUser.AddPair('email', 'charlie@example.com');

    {$IF CompilerVersion >= 33.0}
    WriteLn(LRoot.Format());
    {$ELSE}
    WriteLn(LRoot.ToString);
    {$ENDIF}
  finally
    LRoot.Free;
  end;

  ReadLn;
end.

Output:

{
    "users": [
        {
            "id": 1,
            "name": "Alice",
            "email": "alice@example.com"
        },
        {
            "id": 2,
            "name": "Bob",
            "email": "bob@example.com"
        },
        {
            "id": 3,
            "name": "Charlie",
            "email": "charlie@example.com"
        }
    ]
}

Oggetti JSON Annidati

I dati complessi richiedono spesso un’organizzazione gerarchica - una persona ha un indirizzo, un indirizzo ha città e paese, e così via. In Delphi, crei strutture annidate aggiungendo istanze TJSONObject come valori all’interno di altri oggetti. Proprio come con gli array, l’oggetto genitore assume la proprietà dei suoi figli, semplificando la gestione della memoria. Questo esempio crea una persona con oggetti annidati per indirizzo e azienda:

program JSONNested;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

var
  LJSONObject: TJSONObject;
  LAddress: TJSONObject;
  LCompany: TJSONObject;
begin
  LJSONObject := TJSONObject.Create;
  try
    LJSONObject.AddPair('name', 'Daniele Teti');

    // Crea oggetto indirizzo annidato
    LAddress := TJSONObject.Create;
    LJSONObject.AddPair('address', LAddress);
    LAddress.AddPair('street', 'Via Roma 123');
    LAddress.AddPair('city', 'Rome');
    LAddress.AddPair('country', 'Italy');
    LAddress.AddPair('zipCode', '00100');

    // Crea un altro oggetto annidato
    LCompany := TJSONObject.Create;
    LJSONObject.AddPair('company', LCompany);
    LCompany.AddPair('name', 'bit Time Professionals');
    LCompany.AddPair('website', 'https://www.bittime.it');

    {$IF CompilerVersion >= 33.0}
    WriteLn(LJSONObject.Format());
    {$ELSE}
    WriteLn(LJSONObject.ToString);
    {$ENDIF}
  finally
    LJSONObject.Free;
  end;

  ReadLn;
end.

Output:

{
    "name": "Daniele Teti",
    "address": {
        "street": "Via Roma 123",
        "city": "Rome",
        "country": "Italy",
        "zipCode": "00100"
    },
    "company": {
        "name": "bit Time Professionals",
        "website": "https://www.bittime.it"
    }
}

Uso di TJSONObjectBuilder (Delphi 10.1 Berlin+)

Se preferisci una sintassi più dichiarativa e concatenabile per costruire JSON, Delphi 10.1 Berlin ha introdotto TJSONObjectBuilder. Questa API fluent ti permette di costruire strutture JSON complesse in una singola espressione usando il concatenamento di metodi con BeginObject, BeginArray, Add e chiamate EndObject/EndArray. Il builder scrive su un TJsonTextWriter, che scrive su un TStringBuilder. Sebbene più verboso nella configurazione, questo approccio produce codice più pulito e leggibile per strutture complesse:

program JSONBuilderExample;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Classes,
  System.JSON.Types,
  System.JSON.Writers,
  System.JSON.Builders;

var
  LBuilder: TJSONObjectBuilder;
  LWriter: TJsonTextWriter;
  LStringWriter: TStringWriter;
  LStringBuilder: TStringBuilder;
begin
  LStringBuilder := TStringBuilder.Create;
  try
    LStringWriter := TStringWriter.Create(LStringBuilder);
    try
      LWriter := TJsonTextWriter.Create(LStringWriter);
      try
        LWriter.Formatting := TJsonFormatting.Indented;
        LBuilder := TJSONObjectBuilder.Create(LWriter);
        try
          // Costruisci JSON usando l'API fluent
          LBuilder
            .BeginObject
              .Add('firstName', 'Daniele')
              .Add('lastName', 'Teti')
              .Add('age', 45)
              .Add('active', True)
              .BeginObject('address')
                .Add('city', 'Rome')
                .Add('country', 'Italy')
              .EndObject
              .BeginArray('skills')
                .Add('Delphi')
                .Add('Python')
                .Add('SQL')
              .EndArray
            .EndObject;

          WriteLn(LStringBuilder.ToString);
        finally
          LBuilder.Free;
        end;
      finally
        LWriter.Free;
      end;
    finally
      LStringWriter.Free;
    end;
  finally
    LStringBuilder.Free;
  end;

  ReadLn;
end.

Output:

{
    "firstName": "Daniele",
    "lastName": "Teti",
    "age": 45,
    "active": true,
    "address": {
        "city": "Rome",
        "country": "Italy"
    },
    "skills": [
        "Delphi",
        "Python",
        "SQL"
    ]
}

Parsing di Stringhe JSON

Quando ricevi dati JSON da un web service, file o qualsiasi altra sorgente, devi analizzarli in oggetti Delphi con cui puoi lavorare. Il metodo di classe TJSONObject.ParseJSONValue gestisce questa conversione. Restituisce un TJSONValue (la classe base), quindi dovrai controllare se è del tipo atteso - tipicamente TJSONObject o TJSONArray. Se il JSON è malformato, il metodo restituisce nil, quindi controlla sempre questo prima di procedere:

program JSONParsing;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

const
  JSON_STRING =
    '{"name":"Daniele","age":45,"skills":["Delphi","Python"]}';

var
  LJSONValue: TJSONValue;
  LJSONObject: TJSONObject;
begin
  // ParseJSONValue restituisce TJSONValue, converti al tipo appropriato
  LJSONValue := TJSONObject.ParseJSONValue(JSON_STRING);

  if LJSONValue = nil then
  begin
    WriteLn('ERRORE: JSON non valido!');
    ReadLn;
    Exit;
  end;

  try
    // Controlla se è un oggetto (potrebbe essere un array a livello root)
    if not (LJSONValue is TJSONObject) then
    begin
      WriteLn('ERRORE: Atteso oggetto JSON a livello root');
      Exit;
    end;

    LJSONObject := TJSONObject(LJSONValue); // Cast diretto - sicuro dopo il controllo "is"
    WriteLn('Analisi riuscita!');
    WriteLn('Numero di coppie: ', LJSONObject.Count);

    {$IF CompilerVersion >= 33.0}
    WriteLn(LJSONObject.Format());
    {$ELSE}
    WriteLn(LJSONObject.ToString);
    {$ENDIF}
  finally
    LJSONValue.Free;
  end;

  ReadLn;
end.
⚠️
Controlla sempre se ParseJSONValue restituisce nil, che indica JSON non valido.

Gestione degli Errori di Parsing (Delphi 10.3+)

Quando il parsing fallisce, sapere perché è fallito aiuta enormemente il debugging. A partire da Delphi 10.3 Rio, puoi passare True come secondo parametro a ParseJSONValue per fargli sollevare un’eccezione EJSONParseException invece di restituire nil. Questa eccezione include il messaggio di errore, il percorso dove il parsing è fallito e l’offset del carattere - informazioni preziosissime quando si ha a che fare con JSON complesso o fornito esternamente:

program JSONParseErrors;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

const
  INVALID_JSON = '{"name": "Test", "value": }'; // Non valido!

var
  LJSONValue: TJSONValue;
begin
  {$IF CompilerVersion >= 33.0} // Delphi 10.3 Rio
  try
    // Usa l'opzione RaiseExc per ottenere l'eccezione con i dettagli
    LJSONValue := TJSONObject.ParseJSONValue(INVALID_JSON, True);
    try
      WriteLn('Analizzato: ', LJSONValue.ToString);
    finally
      LJSONValue.Free;
    end;
  except
    on E: EJSONParseException do
    begin
      WriteLn('Errore di parsing!');
      WriteLn('  Messaggio: ', E.Message);
      WriteLn('  Percorso: ', E.Path);
      WriteLn('  Offset: ', E.Offset);
    end;
  end;
  {$ELSE}
  // Pre-10.3: controlla solo nil
  LJSONValue := TJSONObject.ParseJSONValue(INVALID_JSON);
  if LJSONValue = nil then
    WriteLn('JSON non valido - nessun dettaglio disponibile')
  else
    LJSONValue.Free;
  {$ENDIF}

  ReadLn;
end.

Lettura di Valori JSON

Delphi fornisce diversi modi per leggere valori da oggetti JSON, ognuno con diversi compromessi tra comodità e sicurezza. Capire quando usare ciascun approccio ti aiuterà a scrivere codice più robusto che gestisce i dati mancanti o inaspettati con eleganza.

Metodo 1: GetValue con Tipo Generico (Delphi XE7+)

Il modo più semplice per leggere un valore è usare il metodo generico GetValue<T>. Specifichi il tipo atteso come parametro di tipo e Delphi gestisce automaticamente la conversione. Tuttavia, questo metodo solleva un’eccezione se la chiave non esiste, quindi usalo solo quando sei certo che la chiave è presente:

program JSONReadGetValue;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

const
  JSON_DATA = '{"name":"Daniele","age":45,"active":true}';

var
  LJSONObject: TJSONObject;
begin
  LJSONObject := TJSONObject.ParseJSONValue(JSON_DATA) as TJSONObject;
  try
    // GetValue<T> - solleva eccezione se la chiave non è trovata
    WriteLn('Nome: ', LJSONObject.GetValue<string>('name'));
    WriteLn('Età: ', LJSONObject.GetValue<Integer>('age'));
    WriteLn('Attivo: ', LJSONObject.GetValue<Boolean>('active'));
  finally
    LJSONObject.Free;
  end;

  ReadLn;
end.

Metodo 2: TryGetValue - Lettura Sicura (Consigliato)

Per il codice di produzione, TryGetValue<T> è l’approccio consigliato. Restituisce False se la chiave manca o il valore non può essere convertito al tipo richiesto, permettendoti di gestire i dati mancanti senza gestione delle eccezioni. Questo è particolarmente utile quando si analizza JSON da fonti esterne dove non puoi garantire che tutti i campi siano presenti:

program JSONReadTryGetValue;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

const
  JSON_DATA = '{"name":"Daniele","age":45}';

var
  LJSONObject: TJSONObject;
  LName: string;
  LAge: Integer;
  LMiddleName: string;
begin
  LJSONObject := TJSONObject.ParseJSONValue(JSON_DATA) as TJSONObject;
  try
    // TryGetValue restituisce False se la chiave non è trovata (nessuna eccezione)
    if LJSONObject.TryGetValue<string>('name', LName) then
      WriteLn('Nome: ', LName)
    else
      WriteLn('Nome non trovato');

    if LJSONObject.TryGetValue<Integer>('age', LAge) then
      WriteLn('Età: ', LAge)
    else
      WriteLn('Età non trovata');

    // Questa chiave non esiste - nessuna eccezione sollevata
    if LJSONObject.TryGetValue<string>('middleName', LMiddleName) then
      WriteLn('Secondo Nome: ', LMiddleName)
    else
      WriteLn('Secondo Nome: (non specificato)');
  finally
    LJSONObject.Free;
  end;

  ReadLn;
end.

Metodo 3: FindValue - Restituisce nil se Non Trovato

Quando hai bisogno di accedere all’oggetto TJSONValue grezzo piuttosto che a un valore convertito, usa FindValue. Questo metodo restituisce nil se la chiave non esiste, non solleva mai un’eccezione e ti dà pieno accesso alle proprietà e metodi del valore JSON. È utile quando devi controllare il tipo effettivo di un valore o quando lavori con strutture annidate complesse:

program JSONReadFindValue;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

const
  JSON_DATA = '{"name":"Daniele","age":45}';

var
  LJSONObject: TJSONObject;
  LValue: TJSONValue;
begin
  LJSONObject := TJSONObject.ParseJSONValue(JSON_DATA) as TJSONObject;
  try
    // FindValue restituisce nil se non trovato (non solleva mai eccezioni)
    LValue := LJSONObject.FindValue('name');
    if LValue <> nil then
      WriteLn('Nome: ', LValue.Value);

    LValue := LJSONObject.FindValue('nonexistent');
    if LValue = nil then
      WriteLn('Chiave "nonexistent" non trovata');
  finally
    LJSONObject.Free;
  end;

  ReadLn;
end.

Metodo 4: Notazione a Percorso per Valori Annidati

Una delle caratteristiche più comode di Delphi è la notazione a percorso - puoi accedere a valori profondamente annidati usando percorsi separati da punti come 'person.address.city' invece di navigare attraverso più oggetti intermedi. Questo funziona con TryGetValue, GetValue e FindValue, rendendo molto più facile estrarre valori specifici da strutture JSON complesse senza scrivere codice di navigazione verboso:

program JSONReadPath;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

const
  JSON_DATA = '{' +
    '"person": {' +
    '  "name": "Daniele",' +
    '  "address": {' +
    '    "city": "Rome",' +
    '    "country": "Italy"' +
    '  }' +
    '}' +
  '}';

var
  LJSONObject: TJSONObject;
  LValue: string;
begin
  LJSONObject := TJSONObject.ParseJSONValue(JSON_DATA) as TJSONObject;
  try
    // Usa la notazione a punto per accedere ai valori annidati
    if LJSONObject.TryGetValue<string>('person.name', LValue) then
      WriteLn('Nome Persona: ', LValue);

    if LJSONObject.TryGetValue<string>('person.address.city', LValue) then
      WriteLn('Città: ', LValue);

    if LJSONObject.TryGetValue<string>('person.address.country', LValue) then
      WriteLn('Paese: ', LValue);
  finally
    LJSONObject.Free;
  end;

  ReadLn;
end.

Lettura di Array - Approcci Classico e Moderno

Quando il tuo JSON contiene array, dovrai iterare sui loro elementi per elaborare ogni valore. Delphi supporta sia i cicli tradizionali basati su indice usando Items[I] che la più moderna sintassi for-in che funziona con qualsiasi TJSONArray. L’approccio for-in è più pulito quando non hai bisogno dell’indice, mentre il ciclo classico ti dà accesso alla posizione. Per array numerici, converti ogni elemento in TJSONNumber per accedere a metodi come AsInt o AsDouble:

program JSONReadArrays;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

const
  JSON_DATA = '{"skills":["Delphi","Python","SQL"],"scores":[95,87,92]}';

var
  LJSONObject: TJSONObject;
  LSkills: TJSONArray;
  LScores: TJSONArray;
  LItem: TJSONValue;
  I: Integer;
begin
  LJSONObject := TJSONObject.ParseJSONValue(JSON_DATA) as TJSONObject;
  try
    // Leggi array di stringhe - ciclo for classico
    if LJSONObject.TryGetValue<TJSONArray>('skills', LSkills) then
    begin
      WriteLn('Competenze (ciclo classico):');
      for I := 0 to LSkills.Count - 1 do
        WriteLn('  ', I + 1, '. ', LSkills.Items[I].Value);
    end;

    WriteLn;

    // Leggi array di stringhe - ciclo for-in moderno (Delphi XE+)
    if LJSONObject.TryGetValue<TJSONArray>('skills', LSkills) then
    begin
      WriteLn('Competenze (ciclo for-in):');
      for LItem in LSkills do
        WriteLn('  - ', LItem.Value);
    end;

    WriteLn;

    // Leggi array numerico
    if LJSONObject.TryGetValue<TJSONArray>('scores', LScores) then
    begin
      WriteLn('Punteggi:');
      for LItem in LScores do
        WriteLn('  Punteggio: ', (LItem as TJSONNumber).AsInt);
    end;
  finally
    LJSONObject.Free;
  end;

  ReadLn;
end.

Iterazione sulle Coppie di un Oggetto JSON

A volte devi elaborare tutte le proprietà in un oggetto JSON senza conoscere i nomi delle chiavi in anticipo - per esempio, quando costruisci un visualizzatore JSON generico o quando la struttura è dinamica. Il ciclo for-in funziona su TJSONObject proprio come sugli array, producendo istanze TJSONPair. Ogni coppia ti dà accesso sia alla chiave (tramite JsonString.Value) che al valore (tramite JsonValue), oltre a ispezionare il tipo runtime usando ClassName:

program JSONIteratePairs;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

const
  JSON_DATA = '{"name":"Daniele","age":45,"city":"Rome","active":true}';

var
  LJSONObject: TJSONObject;
  LPair: TJSONPair;
begin
  LJSONObject := TJSONObject.ParseJSONValue(JSON_DATA) as TJSONObject;
  try
    WriteLn('Tutte le coppie nell''oggetto:');
    WriteLn;

    // Itera su tutte le coppie usando for-in
    for LPair in LJSONObject do
    begin
      WriteLn('Chiave: ', LPair.JsonString.Value);
      WriteLn('Valore: ', LPair.JsonValue.ToString);
      WriteLn('Tipo: ', LPair.JsonValue.ClassName);
      WriteLn;
    end;
  finally
    LJSONObject.Free;
  end;

  ReadLn;
end.

Modifica di Oggetti JSON

Gli oggetti JSON in Delphi sono completamente mutabili - puoi aggiungere, rimuovere e aggiornare proprietà dopo la creazione. Capire le regole di gestione della memoria è cruciale: quando chiami RemovePair, la proprietà di quella coppia viene trasferita a te, quindi devi liberarla. Il metodo Free in Delphi è sicuro da chiamare su nil, quindi il pattern RemovePair('key').Free funziona anche se la chiave non esiste.

Aggiunta e Rimozione di Coppie

Questo esempio dimostra il ciclo di vita completo della modifica di un oggetto JSON: creazione di proprietà iniziali, rimozione di una e aggiornamento di un’altra. Nota che per aggiornare un valore, devi prima rimuovere la vecchia coppia (liberandola) e poi aggiungerne una nuova con la stessa chiave:

program JSONModify;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

var
  LJSONObject: TJSONObject;
  LRemovedPair: TJSONPair;
begin
  LJSONObject := TJSONObject.Create;
  try
    // Aggiungi coppie iniziali
    LJSONObject.AddPair('name', 'Daniele');
    LJSONObject.AddPair('city', 'Rome');
    LJSONObject.AddPair('temp', 'da rimuovere');

    WriteLn('Iniziale:');
    WriteLn(LJSONObject.ToString);
    WriteLn;

    // Rimuovi una coppia - RemovePair restituisce la coppia rimossa (ne hai la proprietà!)
    LRemovedPair := LJSONObject.RemovePair('temp');
    LRemovedPair.Free; // Sicuro anche se nil - Free controlla Self <> nil

    WriteLn('Dopo la rimozione di "temp":');
    WriteLn(LJSONObject.ToString);
    WriteLn;

    // Per aggiornare un valore: rimuovi poi aggiungi
    LRemovedPair := LJSONObject.RemovePair('city');
    LRemovedPair.Free;
    LJSONObject.AddPair('city', 'Milan');

    WriteLn('Dopo l''aggiornamento di "city":');
    WriteLn(LJSONObject.ToString);
  finally
    LJSONObject.Free;
  end;

  ReadLn;
end.

Output:

Iniziale:
{"name":"Daniele","city":"Rome","temp":"da rimuovere"}

Dopo la rimozione di "temp":
{"name":"Daniele","city":"Rome"}

Dopo l'aggiornamento di "city":
{"name":"Daniele","city":"Milan"}

Clonazione di Oggetti JSON

Quando hai bisogno di creare una versione modificata di un oggetto JSON senza influenzare l’originale, usa il metodo Clone. Questo crea una copia profonda - un albero di oggetti completamente indipendente dove le modifiche al clone non influenzano l’originale e viceversa. Questo è essenziale quando ricevi dati JSON che devi trasformare prima di inviare altrove preservando l’originale:

program JSONClone;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON;

var
  LOriginal: TJSONObject;
  LClone: TJSONObject;
  LPair: TJSONPair;
begin
  LOriginal := TJSONObject.Create;
  try
    LOriginal.AddPair('name', 'Daniele');
    LOriginal.AddPair('city', 'Rome');

    // Clone crea una copia indipendente
    LClone := LOriginal.Clone as TJSONObject;
    try
      // Modifica il clone - l'originale non è influenzato
      LPair := LClone.RemovePair('city');
      LPair.Free;
      LClone.AddPair('city', 'Milan');

      WriteLn('Originale: ', LOriginal.ToString);
      WriteLn('Clone: ', LClone.ToString);
    finally
      LClone.Free;
    end;
  finally
    LOriginal.Free;
  end;

  ReadLn;
end.

Output:

Originale: {"name":"Daniele","city":"Rome"}
Clone: {"name":"Daniele","city":"Milan"}

Lavorare con File JSON

Persistere JSON su disco è un requisito comune per file di configurazione, caching ed esportazione di dati. L’unit System.IOUtils di Delphi fornisce la classe TFile con semplici metodi per leggere e scrivere file di testo, che si abbina perfettamente con la rappresentazione in stringa di JSON.

Salvare JSON su File

Per salvare un oggetto JSON su file, convertilo in una stringa usando Format() (per output leggibile) o ToString() (per output compatto), quindi scrivi quella stringa su disco. Usare TPath.GetDocumentsPath assicura che il tuo file vada in una posizione scrivibile che funziona su diverse configurazioni di Windows:

program JSONSaveToFile;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.IOUtils,
  System.JSON;

var
  LJSONObject: TJSONObject;
  LDatabase: TJSONObject;
  LFileName: string;
begin
  LFileName := TPath.Combine(TPath.GetDocumentsPath, 'config.json');

  LJSONObject := TJSONObject.Create;
  try
    LJSONObject.AddPair('appName', 'MyApplication');
    LJSONObject.AddPair('version', '1.0.0');
    LJSONObject.AddPair('debug', False);

    LDatabase := TJSONObject.Create;
    LJSONObject.AddPair('database', LDatabase);
    LDatabase.AddPair('host', 'localhost');
    LDatabase.AddPair('port', 5432);

    // Salva su file
    {$IF CompilerVersion >= 33.0}
    TFile.WriteAllText(LFileName, LJSONObject.Format());
    {$ELSE}
    TFile.WriteAllText(LFileName, LJSONObject.ToString);
    {$ENDIF}

    WriteLn('Salvato in: ', LFileName);
  finally
    LJSONObject.Free;
  end;

  ReadLn;
end.

Caricamento di JSON da File

Leggere JSON da un file è altrettanto semplice: leggi il contenuto del file in una stringa, quindi analizzalo con ParseJSONValue. Controlla sempre prima se il file esiste per evitare eccezioni e verifica che il parsing sia riuscito prima di accedere ai dati. La notazione a percorso funziona altrettanto bene su JSON analizzato quanto su oggetti costruiti manualmente:

program JSONLoadFromFile;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.IOUtils,
  System.JSON;

var
  LJSONObject: TJSONObject;
  LJSONValue: TJSONValue;
  LContent: string;
  LFileName: string;
  LAppName: string;
  LPort: Integer;
begin
  LFileName := TPath.Combine(TPath.GetDocumentsPath, 'config.json');

  if not TFile.Exists(LFileName) then
  begin
    WriteLn('File non trovato: ', LFileName);
    ReadLn;
    Exit;
  end;

  LContent := TFile.ReadAllText(LFileName);
  LJSONValue := TJSONObject.ParseJSONValue(LContent);

  if LJSONValue = nil then
  begin
    WriteLn('JSON non valido nel file!');
    ReadLn;
    Exit;
  end;

  try
    LJSONObject := LJSONValue as TJSONObject;

    if LJSONObject.TryGetValue<string>('appName', LAppName) then
      WriteLn('Nome App: ', LAppName);

    if LJSONObject.TryGetValue<Integer>('database.port', LPort) then
      WriteLn('Porta Database: ', LPort);
  finally
    LJSONValue.Free;
  end;

  ReadLn;
end.

Esempio Pratico: Client REST API

Ora mettiamo tutto insieme in uno scenario del mondo reale: chiamare una REST API ed elaborare la risposta JSON. Questo esempio si connette a JSONPlaceholder (una API di test gratuita), recupera una lista di utenti e analizza ognuno in un record Delphi. Nota come usiamo TryGetValue ovunque per gestire con eleganza i campi potenzialmente mancanti - un must quando si ha a che fare con API esterne che potrebbero cambiare.

📝
THTTPClient richiede Delphi XE8 o successivo.
program JSONRestApiClient;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.JSON,
  System.Net.HttpClient;  // Richiede Delphi XE8+

type
  TUser = record
    ID: Integer;
    Name: string;
    Email: string;
    Username: string;
  end;

function ParseUser(AJSONObject: TJSONObject): TUser;
begin
  // Uso di TryGetValue per sicurezza
  if not AJSONObject.TryGetValue<Integer>('id', Result.ID) then
    Result.ID := 0;
  if not AJSONObject.TryGetValue<string>('name', Result.Name) then
    Result.Name := '';
  if not AJSONObject.TryGetValue<string>('email', Result.Email) then
    Result.Email := '';
  if not AJSONObject.TryGetValue<string>('username', Result.Username) then
    Result.Username := '';
end;

var
  LClient: THTTPClient;
  LResponse: IHTTPResponse;
  LJSONValue: TJSONValue;
  LJSONArray: TJSONArray;
  LUserJSON: TJSONObject;
  LUser: TUser;
  I: Integer;
begin
  WriteLn('Recupero utenti dall''API JSONPlaceholder...');
  WriteLn;

  LClient := THTTPClient.Create;
  try
    LResponse := LClient.Get('https://jsonplaceholder.typicode.com/users');

    if LResponse.StatusCode <> 200 then
    begin
      WriteLn('Errore HTTP: ', LResponse.StatusCode);
      ReadLn;
      Exit;
    end;

    // Analizza la risposta array JSON
    LJSONValue := TJSONObject.ParseJSONValue(LResponse.ContentAsString);
    if LJSONValue = nil then
    begin
      WriteLn('Risposta JSON non valida');
      ReadLn;
      Exit;
    end;

    try
      if not (LJSONValue is TJSONArray) then
      begin
        WriteLn('Atteso array JSON');
        Exit;
      end;

      LJSONArray := LJSONValue as TJSONArray;
      WriteLn('Trovati ', LJSONArray.Count, ' utenti:');
      WriteLn(StringOfChar('-', 50));

      for I := 0 to LJSONArray.Count - 1 do
      begin
        LUserJSON := LJSONArray.Items[I] as TJSONObject;
        LUser := ParseUser(LUserJSON);

        WriteLn('ID: ', LUser.ID);
        WriteLn('Nome: ', LUser.Name);
        WriteLn('Email: ', LUser.Email);
        WriteLn('Username: ', LUser.Username);
        WriteLn(StringOfChar('-', 50));
      end;

    finally
      LJSONValue.Free;
    end;

  finally
    LClient.Free;
  end;

  ReadLn;
end.

Esempio Pratico: Gestore File di Configurazione

Questo esempio finale mostra una classe completa e riutilizzabile per gestire la configurazione dell’applicazione. Il TConfigManager incapsula tutta la complessità di caricamento, salvataggio e accesso alle impostazioni fornendo una API pulita e type-safe. Dimostra il caricamento lazy (legge il file solo quando necessario), valori predefiniti per chiavi mancanti e creazione automatica del file. Puoi usare questo pattern come punto di partenza per i tuoi sistemi di configurazione:

program JSONConfigManager;
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.IOUtils,
  System.JSON;

type
  TConfigManager = class
  private
    FFileName: string;
    FJSONObject: TJSONObject;
    FModified: Boolean;
    procedure EnsureLoaded;
  public
    constructor Create(const AFileName: string);
    destructor Destroy; override;

    procedure Load;
    procedure Save;

    function GetString(const AKey: string; const ADefault: string = ''): string;
    function GetInteger(const AKey: string; const ADefault: Integer = 0): Integer;
    function GetBoolean(const AKey: string; const ADefault: Boolean = False): Boolean;

    procedure SetValue(const AKey: string; const AValue: string); overload;
    procedure SetValue(const AKey: string; const AValue: Integer); overload;
    procedure SetValue(const AKey: string; const AValue: Boolean); overload;

    property FileName: string read FFileName;
    property Modified: Boolean read FModified;
  end;

constructor TConfigManager.Create(const AFileName: string);
begin
  inherited Create;
  FFileName := AFileName;
  FJSONObject := nil;
  FModified := False;
end;

destructor TConfigManager.Destroy;
begin
  FJSONObject.Free;
  inherited;
end;

procedure TConfigManager.EnsureLoaded;
begin
  if FJSONObject = nil then
    Load;
end;

procedure TConfigManager.Load;
var
  LContent: string;
  LJSONValue: TJSONValue;
begin
  FreeAndNil(FJSONObject);
  FModified := False;

  if TFile.Exists(FFileName) then
  begin
    LContent := TFile.ReadAllText(FFileName);
    LJSONValue := TJSONObject.ParseJSONValue(LContent);
    if (LJSONValue <> nil) and (LJSONValue is TJSONObject) then
      FJSONObject := TJSONObject(LJSONValue)
    else if LJSONValue <> nil then
      LJSONValue.Free;
  end;

  if FJSONObject = nil then
    FJSONObject := TJSONObject.Create;
end;

procedure TConfigManager.Save;
begin
  EnsureLoaded;
  {$IF CompilerVersion >= 33.0}
  TFile.WriteAllText(FFileName, FJSONObject.Format());
  {$ELSE}
  TFile.WriteAllText(FFileName, FJSONObject.ToString);
  {$ENDIF}
  FModified := False;
end;

function TConfigManager.GetString(const AKey, ADefault: string): string;
begin
  EnsureLoaded;
  if not FJSONObject.TryGetValue<string>(AKey, Result) then
    Result := ADefault;
end;

function TConfigManager.GetInteger(const AKey: string; const ADefault: Integer): Integer;
begin
  EnsureLoaded;
  if not FJSONObject.TryGetValue<Integer>(AKey, Result) then
    Result := ADefault;
end;

function TConfigManager.GetBoolean(const AKey: string; const ADefault: Boolean): Boolean;
begin
  EnsureLoaded;
  if not FJSONObject.TryGetValue<Boolean>(AKey, Result) then
    Result := ADefault;
end;

procedure TConfigManager.SetValue(const AKey: string; const AValue: string);
begin
  EnsureLoaded;
  FJSONObject.RemovePair(AKey).Free;
  FJSONObject.AddPair(AKey, AValue);
  FModified := True;
end;

procedure TConfigManager.SetValue(const AKey: string; const AValue: Integer);
begin
  EnsureLoaded;
  FJSONObject.RemovePair(AKey).Free;
  FJSONObject.AddPair(AKey, AValue);
  FModified := True;
end;

procedure TConfigManager.SetValue(const AKey: string; const AValue: Boolean);
begin
  EnsureLoaded;
  FJSONObject.RemovePair(AKey).Free;
  FJSONObject.AddPair(AKey, AValue);
  FModified := True;
end;

// Dimostrazione di utilizzo
var
  Config: TConfigManager;
  LConfigFile: string;
begin
  LConfigFile := TPath.Combine(TPath.GetDocumentsPath, 'appsettings.json');
  WriteLn('File di configurazione: ', LConfigFile);
  WriteLn;

  Config := TConfigManager.Create(LConfigFile);
  try
    // Imposta alcuni valori (le chiavi sono piatte - non oggetti annidati)
    Config.SetValue('databaseHost', 'localhost');
    Config.SetValue('databasePort', 5432);
    Config.SetValue('databaseName', 'myapp');
    Config.SetValue('loggingEnabled', True);
    Config.SetValue('loggingMaxFiles', 10);
    Config.Save;

    WriteLn('Configurazione salvata!');
    WriteLn;

    // Rileggi i valori
    WriteLn('Host Database: ', Config.GetString('databaseHost'));
    WriteLn('Porta Database: ', Config.GetInteger('databasePort'));
    WriteLn('Logging Abilitato: ', Config.GetBoolean('loggingEnabled'));

    // Leggi con valore predefinito
    WriteLn('Timeout (predefinito 30): ', Config.GetInteger('timeout', 30));
  finally
    Config.Free;
  end;

  ReadLn;
end.

Output:

File di configurazione: C:\Users\yourname\Documents\appsettings.json

Configurazione salvata!

Host Database: localhost
Porta Database: 5432
Logging Abilitato: TRUE
Timeout (predefinito 30): 30

Librerie JSON di Terze Parti

Sebbene il parser JSON integrato di Delphi sia eccellente per la maggior parte dei casi d’uso, alcuni scenari possono beneficiare di librerie di terze parti:

Libreria Migliore Per URL
JsonDataObjects Alte prestazioni, usata da DelphiMVCFramework GitHub
Grijjy Foundation Ricca di funzionalità, include supporto BSON GitHub
mORMot2 Livello enterprise, molto veloce GitHub

Quando Usare Librerie di Terze Parti

  • File JSON grandi (>10MB): Considera parser streaming o JsonDataObjects
  • Parsing ad alta frequenza: JsonDataObjects o mORMot2 offrono prestazioni migliori
  • Necessità supporto BSON: Grijjy Foundation
  • Serializzazione oggetti: Serializzatori di DelphiMVCFramework o mORMot2

Per la maggior parte delle applicazioni, il System.JSON integrato è sufficiente e ha il vantaggio di non avere dipendenze esterne.

Costruire REST API con JSON

Se stai costruendo REST API in Delphi, DelphiMVCFramework fornisce eccellente supporto JSON con serializzazione automatica:

[MVCPath('/api/customers')]
TCustomersController = class(TMVCController)
public
  [MVCPath]
  [MVCHTTPMethod([httpGET])]
  procedure GetCustomers;

  [MVCPath('/($id)')]
  [MVCHTTPMethod([httpGET])]
  procedure GetCustomer(id: Integer);
end;

procedure TCustomersController.GetCustomers;
var
  LCustomers: TObjectList<TCustomer>;
begin
  LCustomers := TCustomerService.GetAll;
  Render(LCustomers); // Serializzazione JSON automatica
end;

Vedi gli esempi di DelphiMVCFramework per esempi completi.

Domande Frequenti (FAQ)

Come faccio a fare il parsing di una stringa JSON in Delphi?

Usa TJSONObject.ParseJSONValue() dall’unit System.JSON:

uses System.JSON;

var
  LJSONObject: TJSONObject;
  LValue: TJSONValue;
begin
  LValue := TJSONObject.ParseJSONValue('{"name":"John"}');
  if (LValue <> nil) and (LValue is TJSONObject) then
  begin
    LJSONObject := TJSONObject(LValue);
    try
      WriteLn(LJSONObject.GetValue<string>('name')); // Output: John
    finally
      LJSONObject.Free;
    end;
  end;
end;

Come gestisco i valori null in JSON?

Usa TryGetValue per gestire in sicurezza valori mancanti o null:

var
  LValue: string;
begin
  if LJSONObject.TryGetValue<string>('optionalField', LValue) then
    WriteLn('Valore: ', LValue)
  else
    WriteLn('Campo mancante o null');
end;

Come itero su un array JSON?

Usa la sintassi moderna del ciclo for-in:

var
  LArray: TJSONArray;
  LItem: TJSONValue;
begin
  if LJSONObject.TryGetValue<TJSONArray>('items', LArray) then
  begin
    for LItem in LArray do
      WriteLn(LItem.Value);
  end;
end;

Qual è la differenza tra Format() e ToString()?

  • Format(): Restituisce JSON indentato e leggibile (Solo Delphi 10.3+)
  • ToString(): Restituisce JSON compatto senza spazi (meglio per il trasferimento di rete, funziona in tutte le versioni)

Come modifico un oggetto JSON esistente?

Usa RemovePair poi AddPair. RemovePair restituisce la coppia rimossa (o nil se non trovata) - ne hai la proprietà e devi liberarla:

begin
  // Remove restituisce la coppia - devi liberarla!
  // Free è sicuro da chiamare su nil (controlla internamente Self <> nil)
  LJSONObject.RemovePair('name').Free;
  // Aggiungi nuovo valore
  LJSONObject.AddPair('name', 'Nuovo Valore');
end;

Quale versione di Delphi ha introdotto il supporto JSON?

  • Delphi 2009: Supporto JSON iniziale nell’unit DBXJSON
  • Delphi XE6: Rinominata in System.JSON con miglioramenti API
  • Delphi 10.1 Berlin: API fluent TJSONObjectBuilder
  • Delphi 10.3 Rio: Aggiunto metodo Format(), EJSONParseException con informazioni dettagliate sull’errore

Qual è la differenza tra GetValue, FindValue e TryGetValue?

Metodo Restituisce Se Chiave Non Trovata
GetValue<T>('key') Valore di tipo T Solleva eccezione
FindValue('key') TJSONValue o nil Restituisce nil
TryGetValue<T>('key', outVar) Boolean Restituisce False

Raccomandazione: Usa TryGetValue per il codice di produzione in quanto è l’approccio più sicuro.

Come creo una copia profonda di un oggetto JSON?

Usa il metodo Clone:

var
  LOriginal, LCopy: TJSONObject;
begin
  LOriginal := TJSONObject.ParseJSONValue('{"name":"test"}') as TJSONObject;
  try
    LCopy := LOriginal.Clone as TJSONObject;
    try
      // LCopy è indipendente - le modifiche non influenzano LOriginal
    finally
      LCopy.Free;
    end;
  finally
    LOriginal.Free;
  end;
end;

Come controllo se un valore JSON è null?

var
  LValue: TJSONValue;
begin
  LValue := LJSONObject.FindValue('myField');
  if LValue = nil then
    WriteLn('Il campo non esiste')
  else if LValue is TJSONNull then
    WriteLn('Il campo esiste ma è null')
  else
    WriteLn('Il campo ha un valore: ', LValue.Value);
end;

Posso usare la notazione a percorso per accedere agli elementi di un array?

Sì, usa la notazione tra parentesi quadre con l’indice:

var
  LFirstSkill: string;
begin
  // Accedi al primo elemento dell'array skills
  if LJSONObject.TryGetValue<string>('skills[0]', LFirstSkill) then
    WriteLn('Prima competenza: ', LFirstSkill);
end;

Come converto un oggetto Delphi in JSON?

Per casi semplici, costruisci il JSON manualmente con TJSONObject. Per la serializzazione automatica di oggetti e record, usa i serializzatori di DelphiMVCFramework o librerie di terze parti come mORMot2. Questi possono convertire qualsiasi oggetto Delphi in JSON con una singola chiamata di metodo.

System.JSON è thread-safe?

No, TJSONObject e le classi correlate non sono thread-safe. Se più thread devono accedere allo stesso oggetto JSON, devi implementare la tua sincronizzazione (sezioni critiche, lock, ecc.). Per l’accesso in sola lettura dopo il parsing iniziale, puoi condividere in sicurezza l’oggetto tra i thread finché non avvengono modifiche.

Come serializzo un TDateTime in JSON?

TJSONObject non ha un overload integrato di AddPair per TDateTime. Convertilo prima in una stringa usando un formato standard come ISO 8601:

LJSONObject.AddPair('createdAt', FormatDateTime('yyyy-mm-dd"T"hh:nn:ss', Now));

Qual è la dimensione massima di JSON che Delphi può analizzare?

Non c’è un limite rigido, ma System.JSON carica l’intero documento in memoria. Per file molto grandi (>100MB), considera parser streaming come TJsonTextReader da System.JSON.Readers, o librerie di terze parti ottimizzate per documenti grandi.

Qual è la differenza tra System.JSON e DBXJSON?

Sono la stessa libreria - solo rinominata. DBXJSON era il nome originale dell’unit in Delphi 2009-XE5. A partire da Delphi XE6, è stata rinominata in System.JSON per seguire le nuove convenzioni di denominazione. L’API è essenzialmente la stessa, quindi migrare il vecchio codice è semplice.

Come faccio a formattare in modo leggibile (pretty print) JSON in Delphi?

Usa il metodo Format() (Delphi 10.3+) che restituisce JSON indentato e leggibile:

WriteLn(LJSONObject.Format());  // Formattato con indentazione
WriteLn(LJSONObject.ToString);  // Compatto, singola riga

Per le versioni più vecchie di Delphi, usa librerie di terze parti o implementa una formattazione personalizzata.

Come gestisco i caratteri speciali e Unicode in JSON?

System.JSON gestisce automaticamente Unicode ed effettua l’escape dei caratteri speciali quando genera JSON. Durante il parsing, le sequenze di escape come \n, \t e \uXXXX vengono correttamente convertite. Non è necessaria gestione manuale:

LJSONObject.AddPair('message', 'Riga 1'#13#10'Riga 2');  // Newline con escape automatico
LJSONObject.AddPair('emoji', '🚀');  // Unicode funziona direttamente

Come unisco due oggetti JSON?

Non c’è una funzione di merge integrata. Itera su un oggetto e aggiungi le sue coppie all’altro:

for LPair in LSource do
  LTarget.AddPair(LPair.JsonString.Value, LPair.JsonValue.Clone as TJSONValue);

Nota: Devi clonare i valori poiché possono appartenere solo a un oggetto genitore.

Come valido JSON prima del parsing?

ParseJSONValue restituisce nil per JSON non valido, che serve come validazione base. Per la validazione dello schema (controllo struttura, campi obbligatori, tipi), avrai bisogno di librerie di terze parti poiché Delphi non include supporto integrato per JSON Schema.

Come accedo ad array profondamente annidati?

Combina la notazione a percorso con l’indicizzazione dell’array:

// Accedi a: {"data": {"users": [{"name": "Alice"}, {"name": "Bob"}]}}
if LJSONObject.TryGetValue<string>('data.users[1].name', LValue) then
  WriteLn(LValue);  // Output: Bob

Posso usare JSON con i dataset FireDAC?

Sì, ma non c’è integrazione diretta. Puoi iterare manualmente su un dataset e costruire JSON, o usare librerie di serializzazione. Sia DelphiMVCFramework che mORMot2 forniscono serializzazione dataset-to-JSON pronta all’uso.

Come gestisco JSON con chiavi duplicate?

JSON tecnicamente permette chiavi duplicate, anche se è sconsigliato. TJSONObject memorizza tutte le coppie, ma GetValue/TryGetValue restituiscono solo la prima corrispondenza. Per accedere a tutti i valori con la stessa chiave, itera usando il ciclo for-in.

Delphi fornisce un supporto JSON robusto e integrato tramite l’unit System.JSON. Punti chiave:

  1. Usa TJSONObject e TJSONArray per creare e analizzare JSON
  2. Controlla sempre nil quando analizzi stringhe JSON
  3. Usa TryGetValue per la lettura sicura dei valori con campi opzionali
  4. Usa la notazione a percorso ('parent.child') per valori annidati
  5. Ricorda la gestione della memoria: gli oggetti genitori possiedono i loro figli; RemovePair restituisce la proprietà a te
  6. Considera librerie di terze parti solo per esigenze specifiche di prestazioni
  7. Usa Format() per output leggibile (Delphi 10.3+), ToString() per output compatto
  8. Usa cicli for-in per iterazione più pulita su array e coppie di oggetti

Per costruire moderne REST API in Delphi, dai un’occhiata a DelphiMVCFramework - include serializzazione JSON avanzata ed è usato in produzione da aziende in tutto il mondo.

Comments

comments powered by Disqus