sgcOpenAPI 2026.9.0 is de grootste release die het product ooit heeft gehad. De vorige versie kon overweg met het soort specificatie dat de meeste tutorials laten zien, en ging bij al het andere stilletjes achteruit. Deze versie heeft de parser functie voor functie doorgenomen tegen de OpenAPI 3.0-, 3.1- en 3.2-specificaties en tegen echte gepubliceerde documenten, en het resultaat is 9 nieuwe functies, 26 opgeloste bugs en 5 bewuste breaking changes.
Kort samengevat: de gegenereerde client is nu correct voor specificaties die eerder code opleverden die niet compileerde, of erger nog, code die wel compileerde en de verkeerde URL aanriep.
De parser vertelt je nu wat er niet is gelukt
De oude parser had een manier om problemen te melden, namelijk een exception opwerpen, en een manier om al het andere af te handelen, namelijk stilletjes doorgaan. Een operatie die hij niet kon genereren, stond simpelweg niet in de uitvoer, en je kwam daar pas achter als je op zoek ging naar een methode die er niet was.
Elk document komt nu terug met een Warnings-lijst. Een ontbrekend openapi- of info-member, een member met het verkeerde JSON-type, een operatie die niet gegenereerd kon worden, een niet-opgeloste referentie naar een path item en een JSON Schema-keyword dat wel wordt gelezen maar nog niet wordt toegepast, worden daar allemaal in vastgelegd. De lijst wordt bij elke leesbeurt geleegd, zodat wat je krijgt bij het document hoort dat je zojuist hebt geparseerd.
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;
Stel OutputFileName in voordat je opslaat. Een Pascal-unit compileert alleen als de gedeclareerde naam overeenkomt met de basisnaam van het bestand, en de generator noemde de unit vroeger naar het invoerdocument, dus MyClient.pas genereren uit petstore.json leverde een unit op met de naam petstore die niet compileerde. De uitvoernaam is nu doorslaggevend.
Hij weet welke versie hij leest
OpenAPI 3.0 en 3.1 zijn het oneens over keywords die dezelfde naam delen, en de oude parser behandelde elk document als 3.0. Het duidelijkste geval is exclusiveMinimum, dat in 3.0 een booleaanse modifier op minimum is en in 3.1 een getal op zichzelf. Als je de ene als de andere leest, klopt de grenswaarde niet.
De versie wordt nu geparseerd tot een dialect, beschikbaar als Dialect, DialectMajor en DialectMinor, en elk keyword dat verschilt, wordt gelezen zoals de eigen versie dat voorschrijft.
oParser.ReadFromFile('api.yaml');
case oParser.Dialect of
oapiDialect30: ShowMessage('OpenAPI 3.0');
oapiDialect31: ShowMessage('OpenAPI 3.1');
oapiDialect32: ShowMessage('OpenAPI 3.2');
end;
Daarbovenop brengt 3.1 webhooks, jsonSchemaDialect en components.pathItems, de licentie-identifier, het mutualTLS-securityschema, een type dat als array is gedeclareerd zoals ["string","null"], en een schema dat als kale boolean is gedeclareerd. Ze worden allemaal ondersteund. De JSON Schema 2020-12-keywords waar de codegenerator nog niets mee doet, worden wel in het model gelezen en via Warnings gemeld, zodat het gat zichtbaar is in plaats van onzichtbaar.
Uit 3.2 ondersteunt de parser de query-operatie en de additionalOperations-map. Een pad dat een van beide declareert, genereert nu de bijbehorende methoden, die als POST worden verstuurd met een X-HTTP-Method-Override-header.
Parameters op padniveau
Dit is de fix die de meeste gebruikers zullen merken. De specificatie laat je een parameter een keer op het path item declareren in plaats van hem in elke onderliggende operatie te herhalen, en dat is de stijl die de specificatie aanbeveelt en die de meeste openbare documenten gebruiken. De oude parser las die parameters wel in en liet ze daarna vallen.
De gegenereerde methode nam helemaal geen argumenten aan, en het verzoek ging de deur uit met de placeholder nog in de URL, letterlijk /pets/{petId}. Het leek een werkende client tot de eerste aanroep met een 404 terugkwam.
Schema's die samenstellen zoals het hoort
Compositie was het zwakste deel van de oude parser, en elke variant was op een andere manier fout. allOf hield alleen het laatste van meerdere basisschema's over en gooide de members van de andere weg. oneOf voegde elke branch samen tot een enkele klasse, wat dubbele velden opleverde. anyOf werd helemaal niet afgehandeld en werd tot een string herleid. Een schema dat zowel properties als additionalProperties declareerde, raakte al zijn properties kwijt.
Alle vier genereren nu wat het document beschrijft. Een inline objectschema krijgt ook een eigen klasse in plaats van tot een string te vervallen, en items wordt als volledig schema gelezen, zodat een array van inline objecten, een array van enums en een geneste array elk het juiste type opleveren.
Enums die de waarde meedragen die de server verwacht
De gegenereerde enum-tabellen bevatten vroeger de opgeschoonde Pascal-identifier in plaats van de waarde op de lijn, dus allow-all ging als allowall de deur uit en json-file als jsonfile. Elk verzoek dat op zo'n enum was gebouwd, werd geweigerd.
De tabellen dragen nu de echte waarde, de declaratievolgorde van de specificatie blijft behouden, integer-enums krijgen ook een tabel, en er wordt een extra Unknown-member gegenereerd zodat een waarde die de server later toevoegt niet stilzwijgend als het eerste lid van de lijst wordt gedecodeerd.
Propertynamen krijgen dezelfde behandeling vanuit de andere richting. Een schemaproperty die is genoemd naar een gereserveerd woord van Delphi zoals property, class, string of function, of twee properties die alleen in hoofdlettergebruik verschillen zoals Name en name, leverde vroeger een unit op die niet compileerde. De property wordt nu hernoemd en de naam op de lijn blijft behouden met een JSONName-attribuut, zodat de serialisatie nog steeds overeenkomt met het document.
Responses, ook de responses die je maar een keer declareert
De default-response en de bereikresponses 2XX, 4XX en 5XX werden stilzwijgend genegeerd. Een API die zijn fouten alleen via default declareert, wat vaak voorkomt, leverde een client op zonder enige getypeerde fout. Ze worden nu wel gelezen. Als er meerdere geslaagde responses zijn gedeclareerd, wordt de laagste gebruikt, en application/json krijgt de voorkeur wanneer een operatie meerdere mediatypes aanbiedt.
Parameters op de lijn
De gegenereerde client ondersteunt nu cookieparameters en de volledige serialisatieregels voor OpenAPI-parameters: matrix, label, simple, form, spaceDelimited, pipeDelimited en deepObject, elk met explode en allowReserved. Met de nieuwe methoden AddArray en AddObject bouw je de gestructureerde waarden met de hand wanneer dat nodig is.
// query parameters built with the serialization style the document declares
oRequest.AddArray('tags', ['dog', 'cat'], True); // explode
oRequest.AddObject('filter', ['color', 'red', 'size', 'M']);
Externe referenties
Een specificatie die over meerdere bestanden is verdeeld, werkte nauwelijks. Een referentie met een JSON Pointer-fragment zoals ./common.yaml#/components/schemas/Error kon niet worden opgelost. Twee bestanden die naar elkaar verwezen, lieten de parser crashen. Een relatieve referentie in een subdocument werd opgelost ten opzichte van het hoofddocument in plaats van ten opzichte van het eigen bestand. Twee externe bestanden met dezelfde basisnaam overschreven elkaar, en konden een schema van het hoofddocument vervangen. En een keten van referenties werd precies een stap gevolgd.
Dat is allemaal opgelost, en een ding is bewust strenger gemaakt: een externe referentie kon voorheen elk bestand op de machine lezen, ../../../credentials.json inbegrepen, en de inhoud in de gegenereerde unit kopiëren. Externe referenties blijven nu binnen de map van het hoofddocument. Wanneer een indeling echt buiten die map moet reiken, wordt de beperking expliciet opgeheven.
uses
sgcOpenAPI_Bundle;
begin
// off by default: references may not leave the folder of the main document
sgcOpenAPIAllowRefsOutsideRoot := True;
end;
Bestanden die niet helemaal UTF-8 zijn
RFC 8259 zegt dat een JSON-document UTF-8 is, en heel wat gepubliceerde specificaties zijn dat niet. Een bestand met een byte order mark werd vroeger met een UTF-8-fout geweigerd zodra het een teken buiten ASCII bevatte, en Chinese of Japanse tekst werd stilzwijgend vervangen door vraagtekens.
Een document dat geen geldige UTF-8 is, wordt nu als Windows-1252 gelezen met een geregistreerde waarschuwing, in plaats van te mislukken. Een UTF-16-bestand met een byte order mark wordt correct gelezen. Het gegenereerde bestand wordt met een expliciete codering weggeschreven, en een teken dat de doelcodering niet kan weergeven wordt gemeld in plaats van stilletjes een vraagteken te worden.
Een commandoregel die je in een buildscript kunt zetten
De commandoregel stelt nu een exitcode in: 0 bij succes, en 1 tot en met 7 voor de verschillende fouten, zodat een buildstap kan zien of het genereren is gelukt. Foutmeldingen gaan altijd naar standard error, en de -l-switch is nu alleen nog voor voortgangslogging.
sgcOpenAPI -i petstore.json -o PetStoreClient.pas -c TPetStoreClient
if errorlevel 1 (
echo OpenAPI generation failed with exit code %errorlevel%
exit /b %errorlevel%
)
Daar gingen drie bugs in de commandoregel mee. De gedocumenteerde -output-switch schreef de unit naar een bestand met de naam utput in de huidige map, en omdat meldingen werden onderdrukt, leek de run toch geslaagd. Er gebeurde helemaal niets wanneer de tool draaide zonder gekoppelde console, wat precies het geval is bij een geplande taak of een buildagent, en een bestaande uitvoeromleiding werd weggegooid. En -h toonde een licentiefout in plaats van de gebruiksinformatie op een machine die niet was geactiveerd, een ongeldige waarde voor -m of -a werd stilzwijgend geaccepteerd, en een onbekende switch werd genegeerd.
Nieuw in deze release is -r (of -remote), die een YAML- of Swagger 2.0-document converteert via de publieke converter op converter.swagger.io. Die staat standaard uit, omdat je document daarmee naar een derde partij wordt gestuurd, dus het is iets wat je bewust aanzet.
De Swagger 2.0-conversie zelf was op twee manieren stuk die het noemen waard zijn. Elk getal werd een string, dus een numerieke default leverde een unit op die niet compileerde en het geconverteerde document was geen geldige OpenAPI 3.0. En een Swagger 2.0-discriminator, die daar een kale string is, brak de hele parse af met een invalid typecast.
Breaking changes
Vijf wijzigingen vragen een beslissing van jou in plaats van alleen een upgrade.
Gegenereerde clients verifiëren nu het servercertificaat. Dat deden ze eerder niet, wat betekent dat ze elk certificaat accepteerden, ook een certificaat dat door een man in the middle werd aangeboden. Om een zelfondertekend endpoint of een testendpoint te bereiken, zet je dat bewust uit.
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';
De request body is UTF-8. Zoals RFC 8259 vereist. Een klasse serialiseert een lege string nu als "field": "" in plaats van hem weg te laten. Null-waarden worden apart geregeld.
oClient.JSONIgnoreEmptyStrings := True; // previous output
oClient.JSONIgnoreNullValues := True; // default
Een response geeft een ResponseStream die jij hebt aangeleverd niet langer vrij. Zet OwnsResponseStream op True voor het oude gedrag. De client vrijgeven vanuit zijn eigen OnResponse-, OnError- of OnCancel-handler geeft nu een duidelijke fout in plaats van te blijven hangen.
De waarde van een commandoregelswitch moet worden geschreven als -name value of -name:value. De aangeplakte vorm zonder scheidingsteken, zoals -x"GET /pets", wordt niet meer geaccepteerd. Die vorm was ook de reden dat -x overeenkwam met andere switches die met x beginnen, zoals -xml.
Een parameter die als array is gedeclareerd, wordt als array gegenereerd. Vroeger werd die als string gegenereerd, dus de signatuur van de gegenereerde methode verandert voor die operaties.
Al het overige
De overige fixes zijn het soort dat je pas opmerkt wanneer ze toeslaan. Een member met een onverwacht JSON-type, bijvoorbeeld "properties": [], brak de parse af met een invalid typecast in plaats van te worden overgeslagen. Een schema van het type integer zonder default kreeg een default van 0, en een enum met een enkele waarde werd als constante behandeld, waardoor de parameter helemaal uit de gegenereerde methode verdween. Hetzelfde document twee keer lezen dupliceerde elk pad, elke tag, elke server en elk schema. Een specificatie-extensie zoals x-tagGroups die tussen de paths stond, werd gelezen alsof het een pad was. enum, required en tags werden geparseerd met een helper voor door komma's gescheiden tekst, dus een waarde met een komma of een JSON-escape werd opgesplitst of beschadigd. Een security requirement met meerdere schema's hield alleen het eerste over, waardoor de eis verloren ging dat er aan allemaal moet worden voldaan. info.contact en info.license werden helemaal nooit gelezen, door een test die nooit waar kon zijn. Een server-URL met meerdere variabelen verving de verkeerde waarde en kon een list index error opleveren. Het bundelen van een specificatie overschreef het invoerbestand zonder back-up en zonder melding, en verwijderde elke typografische apostrof uit het document. En een specificatie die is opgeslagen onder een pad met een spatie, zoals C:\My Specs\, kon zijn externe referenties niet oplossen.
Verkrijgbaarheid
sgcOpenAPI 2026.9.0 is nu beschikbaar, met volledige broncode en een jaar updates. Het ondersteunt Delphi 7 tot en met Delphi 13 Florence en de bijbehorende C++ Builder-versies.
Productpagina · Download de proefversie · Changelog
Vragen of feedback? Neem contact op, je krijgt antwoord van de mensen die de code hebben geschreven.
