sgcOpenAPI 2026.9.0 è la release più importante che il prodotto abbia mai avuto. La versione precedente gestiva il tipo di specifica che mostrano quasi tutti i tutorial, e si degradava in silenzio su tutto il resto. Questa volta il parser è stato ripreso funzionalità per funzionalità confrontandolo con le specifiche OpenAPI 3.0, 3.1 e 3.2 e con documenti reali pubblicati, e il risultato è 9 nuove funzionalità, 26 bug corretti e 5 modifiche incompatibili volute.
In breve: il client generato ora è corretto anche per le specifiche che prima producevano codice che non compilava o, peggio, codice che compilava e chiamava l'URL sbagliato.
Il parser ora ti dice che cosa non è riuscito a fare
Il vecchio parser aveva un solo modo di segnalare i problemi, cioè sollevare un'eccezione, e un solo modo di gestire tutto il resto, cioè proseguire in silenzio. Un'operazione che non riusciva a generare semplicemente non compariva nell'output, e te ne accorgevi quando andavi a cercare un metodo che non c'era.
Ogni documento ora restituisce un elenco Warnings. Un membro openapi o info mancante, un membro con il tipo JSON sbagliato, un'operazione che non è stato possibile generare, un riferimento a path item non risolto e una parola chiave JSON Schema che viene letta ma non ancora applicata vengono tutti registrati lì. L'elenco viene azzerato a ogni lettura, quindi quello che ottieni appartiene al documento che hai appena analizzato.
uses
sgcOpenAPI_Classes, sgcOpenAPI_Parser_Client_Pascal;
var
oParser: TsgcOpenAPI_Parser_Client_Pascal;
i: Integer;
begin
oParser := TsgcOpenAPI_Parser_Client_Pascal.Create;
Try
oParser.OpenAPIClassName := 'TPetStoreClient';
oParser.OpenAPINamespace := 'PetStore';
oParser.OutputFileName := 'PetStoreClient.pas';
oParser.ReadFromFile('petstore.json');
for i := 0 to oParser.Warnings.Count - 1 do
Memo1.Lines.Add('warning: ' + oParser.Warnings[i]);
oParser.SaveToFile('PetStoreClient.pas');
Finally
oParser.Free;
End;
end;
Imposta OutputFileName prima di salvare. Una unit Pascal compila solo quando il nome dichiarato coincide con il nome base del suo file, e il generatore dava alla unit il nome del documento di ingresso, quindi generare MyClient.pas da petstore.json produceva una unit chiamata petstore che non compilava. Ora prevale il nome dell'output.
Sa quale versione sta leggendo
OpenAPI 3.0 e 3.1 non concordano su parole chiave che condividono il nome, e il vecchio parser trattava ogni documento come 3.0. Il caso più evidente è exclusiveMinimum, che in 3.0 è un modificatore booleano di minimum e in 3.1 è un numero a sé stante. Leggere l'uno come l'altro sbaglia il limite.
La versione ora viene interpretata come dialetto, esposto tramite Dialect, DialectMajor e DialectMinor, e ogni parola chiave che cambia viene letta come richiede la sua versione.
oParser.ReadFromFile('api.yaml');
case oParser.Dialect of
oapiDialect30: ShowMessage('OpenAPI 3.0');
oapiDialect31: ShowMessage('OpenAPI 3.1');
oapiDialect32: ShowMessage('OpenAPI 3.2');
end;
In più, 3.1 introduce webhooks, jsonSchemaDialect e components.pathItems, l'identifier della licenza, lo schema di sicurezza mutualTLS, un tipo dichiarato come array come ["string","null"] e uno schema dichiarato come semplice booleano. Sono tutti supportati. Le parole chiave di JSON Schema 2020-12 su cui il generatore di codice non agisce ancora vengono lette nel modello e segnalate tramite Warnings, così la lacuna è visibile invece che invisibile.
Di 3.2 il parser supporta l'operazione query e la mappa additionalOperations. Un path che dichiara l'una o l'altra ora genera i metodi corrispondenti, inviati come POST con un header X-HTTP-Method-Override.
Parametri a livello di path
Questa è la correzione che la maggior parte degli utenti sentirà di più. La specifica consente di dichiarare un parametro una sola volta sul path item invece di ripeterlo in ogni operazione sottostante, ed è lo stile che la specifica raccomanda e che usa la maggior parte dei documenti pubblici. Il vecchio parser leggeva quei parametri e poi li buttava via.
Il metodo generato non prendeva alcun argomento, e la richiesta partiva con il segnaposto ancora nell'URL, letteralmente /pets/{petId}. Sembrava un client funzionante, finché la prima chiamata non tornava con un 404.
Schemi che si compongono
La composizione era la parte più debole del vecchio parser, e ogni suo ramo era sbagliato in modo diverso. allOf teneva solo l'ultimo di più schemi base e buttava via i membri degli altri. oneOf fondeva tutti i rami in un'unica classe, producendo campi duplicati. anyOf non era gestito affatto e si risolveva in una stringa. Uno schema che dichiarava sia properties sia additionalProperties perdeva tutte le sue proprietà.
Tutti e quattro ora generano quello che il documento descrive. Anche uno schema oggetto inline ottiene una classe propria invece di degradare a stringa, e items viene letto come uno schema completo, quindi un array di oggetti inline, un array di enum e un array annidato generano ciascuno il tipo giusto.
Enum che portano il valore che il server si aspetta
Le tabelle di enum generate contenevano l'identificatore Pascal ripulito invece del valore effettivo, quindi allow-all partiva come allowall e json-file come jsonfile. Ogni richiesta costruita a partire da uno di quegli enum veniva rifiutata.
Le tabelle ora portano il valore reale, l'ordine di dichiarazione della specifica viene preservato, anche gli enum interi ottengono una tabella e viene generato un membro Unknown aggiuntivo, così un valore che il server aggiunge in seguito non viene decodificato in silenzio come il primo membro dell'elenco.
I nomi delle proprietà ricevono lo stesso trattamento dal lato opposto. Una proprietà di schema che porta il nome di una parola riservata Delphi come property, class, string o function, oppure due proprietà che differiscono solo per maiuscole e minuscole come Name e name, producevano una unit che non compilava. La proprietà ora viene rinominata e il nome effettivo viene preservato con un attributo JSONName, quindi la serializzazione continua a corrispondere al documento.
Risposte, comprese quelle che dichiari una volta sola
La risposta default e le risposte per intervalli 2XX, 4XX e 5XX venivano scartate in silenzio. Una API che dichiara i suoi errori solo tramite default, cosa frequente, generava un client senza alcun errore tipizzato. Ora vengono lette. Quando sono dichiarate più risposte di successo viene usata la più bassa, e application/json è preferito quando un'operazione offre più media type.
I parametri sulla rete
Il client generato ora supporta i parametri cookie e l'insieme completo delle regole di serializzazione dei parametri OpenAPI: matrix, label, simple, form, spaceDelimited, pipeDelimited e deepObject, ognuna con explode e allowReserved. I nuovi metodi AddArray e AddObject costruiscono a mano i valori strutturati quando ti serve.
// query parameters built with the serialization style the document declares
oRequest.AddArray('tags', ['dog', 'cat'], True); // explode
oRequest.AddObject('filter', ['color', 'red', 'size', 'M']);
Riferimenti esterni
Una specifica divisa su più file funzionava a malapena. Un riferimento che porta un frammento JSON Pointer come ./common.yaml#/components/schemas/Error non poteva essere risolto. Due file che si riferivano a vicenda mandavano in crash il parser. Un riferimento relativo dentro un sotto-documento veniva risolto rispetto al documento radice invece che rispetto al proprio file. Due file esterni con lo stesso nome base si sovrascrivevano a vicenda, e potevano sostituire uno schema appartenente al documento principale. E una catena di riferimenti veniva seguita per un solo passaggio.
Tutto questo è stato corretto, e una cosa è stata deliberatamente resa più stretta: un riferimento esterno prima poteva leggere qualsiasi file sulla macchina, ../../../credentials.json compreso, e copiarne il contenuto nella unit generata. I riferimenti esterni ora sono confinati alla directory del documento principale. Quando una struttura ha davvero bisogno di uscire da lì, il confinamento si toglie in modo esplicito.
uses
sgcOpenAPI_Bundle;
begin
// off by default: references may not leave the folder of the main document
sgcOpenAPIAllowRefsOutsideRoot := True;
end;
File che non sono proprio UTF-8
RFC 8259 dice che un documento JSON è UTF-8, e parecchie specifiche pubblicate non lo sono. Un file con un byte order mark veniva rifiutato con un errore UTF-8 nel momento in cui conteneva un carattere fuori da ASCII, e il testo cinese o giapponese veniva sostituito in silenzio da punti interrogativi.
Un documento che non è UTF-8 valido ora viene letto come Windows-1252 registrando un avviso, invece di fallire. Un file UTF-16 con byte order mark viene letto correttamente. Il file generato viene scritto con una codifica esplicita, e un carattere che la codifica di destinazione non può rappresentare viene segnalato invece di diventare in sordina un punto interrogativo.
Una riga di comando che puoi mettere in uno script di build
La riga di comando ora imposta un codice di uscita: 0 in caso di successo e da 1 a 7 per i diversi fallimenti, così un passo di build può capire se la generazione ha funzionato. I messaggi di errore vanno sempre sullo standard error, e lo switch -l ora serve solo per il log di avanzamento.
sgcOpenAPI -i petstore.json -o PetStoreClient.pas -c TPetStoreClient
if errorlevel 1 (
echo OpenAPI generation failed with exit code %errorlevel%
exit /b %errorlevel%
)
Insieme se ne sono andati tre bug della riga di comando. Lo switch documentato -output scriveva la unit in un file chiamato utput nella directory corrente e, dato che i messaggi erano soppressi, l'esecuzione sembrava comunque riuscita. Non succedeva assolutamente nulla quando lo strumento girava senza una console collegata, che è esattamente il caso di un'operazione pianificata o di un build agent, e un'eventuale redirezione dell'output già impostata veniva scartata. E -h stampava un errore di licenza invece del testo di utilizzo su una macchina non attivata, un valore non valido per -m o -a veniva accettato in silenzio e uno switch sconosciuto veniva ignorato.
Novità di questa release, -r (oppure -remote) converte un documento YAML o Swagger 2.0 tramite il convertitore pubblico su converter.swagger.io. È disattivato per impostazione predefinita, perché invia il tuo documento a terze parti, quindi è qualcosa che attivi consapevolmente.
La conversione di Swagger 2.0 in sé era rotta in due modi che vale la pena nominare. Ogni numero diventava una stringa, quindi un default numerico produceva una unit che non compilava e il documento convertito non era OpenAPI 3.0 valido. E un discriminator di Swagger 2.0, che lì è una semplice stringa, interrompeva l'intera analisi con un typecast non valido.
Modifiche incompatibili
Cinque modifiche richiedono una decisione da parte tua, non solo un aggiornamento.
I client generati ora verificano il certificato del server. Prima non lo facevano, il che significa che accettavano qualsiasi certificato, compreso quello presentato da un attaccante man in the middle. Per raggiungere un endpoint self-signed o di test, disattiva la verifica in modo deliberato.
oClient.TLSOptions.VerifyCertificate := False; // test endpoints only
// certificates are trusted through the OpenSSL default paths, so a machine
// with no certificate store configured needs an explicit root
oClient.TLSOptions.RootCertFile := 'cacert.pem';
Il corpo della richiesta è UTF-8. Come richiede RFC 8259. Una classe ora serializza una stringa vuota come "field": "" invece di ometterla. I valori null si controllano separatamente.
oClient.JSONIgnoreEmptyStrings := True; // previous output
oClient.JSONIgnoreNullValues := True; // default
Una risposta non libera più un ResponseStream fornito da te. Imposta OwnsResponseStream a True per il comportamento precedente. Liberare il client dall'interno del suo stesso gestore OnResponse, OnError o OnCancel ora solleva un errore chiaro invece di restare bloccato.
Il valore di uno switch della riga di comando va scritto come -name value oppure -name:value. La forma accodata senza separatore, come -x"GET /pets", non è più accettata. È anche quella forma ad aver fatto sì che -x corrispondesse ad altri switch che iniziano per x, come -xml.
Un parametro dichiarato come array viene generato come array. Prima veniva generato come stringa, quindi per quelle operazioni cambia la firma del metodo generato.
Tutto il resto
Le correzioni rimanenti sono di quelle che noti solo quando mordono. Un membro con un tipo JSON inatteso, per esempio "properties": [], interrompeva l'analisi con un typecast non valido invece di essere saltato. Uno schema di tipo integer senza default riceveva un default pari a 0, e un enum con un solo valore veniva trattato come una costante, il che rimuoveva del tutto il parametro dal metodo generato. Leggere due volte lo stesso documento duplicava ogni path, tag, server e schema. Un'estensione della specifica come x-tagGroups collocata tra i path veniva letta come se fosse un path. enum, required e tags venivano analizzati con un helper di testo separato da virgole, quindi un valore contenente una virgola o un escape JSON veniva spezzato o corrotto. Un requisito di sicurezza che elencava più schemi ne teneva solo il primo, perdendo la condizione che fossero soddisfatti tutti. info.contact e info.license non venivano mai letti, a causa di un test che non poteva mai essere vero. Un URL di server con più variabili sostituiva il valore sbagliato e poteva sollevare un errore di indice di lista. Il bundling di una specifica sovrascriveva il file di ingresso senza backup e senza messaggi, e cancellava dal documento ogni apostrofo tipografico. E una specifica salvata in un percorso contenente uno spazio, come C:\My Specs\, non riusciva a risolvere i suoi riferimenti esterni.
Come ottenerlo
sgcOpenAPI 2026.9.0 è disponibile da subito, con il codice sorgente completo e un anno di aggiornamenti. Supporta da Delphi 7 a Delphi 13 Florence e le versioni corrispondenti di C++ Builder.
Pagina del prodotto · Scarica la versione di prova · Changelog
Domande o commenti? Contattaci, riceverai una risposta dalle persone che hanno scritto il codice.
