sgcOpenAPI 2026.9.0 es la versión más grande que ha tenido el producto. La versión anterior manejaba el tipo de especificación que muestran la mayoría de los tutoriales, y se degradaba en silencio con todo lo demás. Esta ha repasado el parser característica por característica frente a las especificaciones OpenAPI 3.0, 3.1 y 3.2 y frente a documentos reales publicados, y el resultado son 9 nuevas características, 26 errores corregidos y 5 cambios incompatibles deliberados.
En resumen: el cliente generado ahora es correcto para especificaciones que antes producían código que no compilaba o, peor aún, código que compilaba y llamaba a la URL equivocada.
El parser ahora te dice qué no ha podido hacer
El parser antiguo tenía una sola forma de informar de un problema, que era lanzar una excepción, y una sola forma de gestionar todo lo demás, que era continuar en silencio. Una operación que no podía generar simplemente no aparecía en la salida, y te enterabas cuando ibas a buscar un método que no estaba.
Ahora todos los documentos devuelven una lista Warnings. Un miembro openapi o info ausente, un miembro con el tipo JSON incorrecto, una operación que no se ha podido generar, una referencia a un path item sin resolver y una palabra clave de JSON Schema que se lee pero todavía no se aplica quedan todos registrados ahí. La lista se vacía en cada lectura, así que lo que obtienes pertenece al documento que acabas de analizar.
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;
Establece OutputFileName antes de guardar. Una unit de Pascal solo compila cuando su nombre declarado coincide con el nombre base de su archivo, y el generador antes nombraba la unit según el documento de entrada, de modo que generar MyClient.pas a partir de petstore.json producía una unit llamada petstore que no compilaba. Ahora manda el nombre de salida.
Sabe qué versión está leyendo
OpenAPI 3.0 y 3.1 no coinciden en palabras clave que comparten nombre, y el parser antiguo trataba todos los documentos como 3.0. El caso más claro es exclusiveMinimum, que en 3.0 es un modificador booleano de minimum y en 3.1 es un número por derecho propio. Leer uno como el otro deja mal el límite.
Ahora la versión se analiza como un dialecto, expuesto en Dialect, DialectMajor y DialectMinor, y cada palabra clave que difiere se lee como exige su propia versión.
oParser.ReadFromFile('api.yaml');
case oParser.Dialect of
oapiDialect30: ShowMessage('OpenAPI 3.0');
oapiDialect31: ShowMessage('OpenAPI 3.1');
oapiDialect32: ShowMessage('OpenAPI 3.2');
end;
Además, 3.1 incorpora webhooks, jsonSchemaDialect y components.pathItems, el identifier de la licencia, el esquema de seguridad mutualTLS, un tipo declarado como array, por ejemplo ["string","null"], y un esquema declarado como un booleano simple. Todos ellos son compatibles. Las palabras clave de JSON Schema 2020-12 sobre las que el generador de código todavía no actúa se leen en el modelo y se informan mediante Warnings, de modo que la carencia queda visible en lugar de invisible.
De 3.2, el parser admite la operación query y el mapa additionalOperations. Un path que declare cualquiera de los dos genera ahora los métodos correspondientes, enviados como un POST con una cabecera X-HTTP-Method-Override.
Parámetros a nivel de path
Esta es la corrección que más usuarios van a notar. La especificación permite declarar un parámetro una sola vez en el path item en lugar de repetirlo en cada operación que cuelga de él, y ese es el estilo que recomienda la especificación y el que usan la mayoría de los documentos públicos. El parser antiguo leía esos parámetros y después los descartaba.
El método generado no recibía ningún argumento, y la petición salía con el marcador de posición todavía en la URL, literalmente /pets/{petId}. Parecía un cliente que funcionaba hasta que la primera llamada devolvía un 404.
Esquemas que componen
La composición era la parte más débil del parser antiguo, y cada una de sus ramas estaba mal de una forma distinta. allOf conservaba solo el último de varios esquemas base y tiraba los miembros de los demás. oneOf fusionaba todas las ramas en una única clase, lo que producía campos duplicados. anyOf no se gestionaba en absoluto y se resolvía como un string. Un esquema que declaraba a la vez properties y additionalProperties perdía todas sus propiedades.
Los cuatro generan ahora lo que describe el documento. Un esquema de objeto en línea también recibe su propia clase en lugar de degradarse a un string, y items se lee como un esquema completo, así que un array de objetos en línea, un array de enums y un array anidado generan cada uno el tipo correcto.
Enums que llevan el valor que espera el servidor
Las tablas de enums generadas contenían el identificador Pascal saneado en lugar del valor que viaja por la red, así que allow-all salía como allowall y json-file como jsonfile. Toda petición construida a partir de uno de esos enums era rechazada.
Las tablas llevan ahora el valor real, se conserva el orden de declaración de la especificación, los enums de enteros también tienen tabla, y se genera un miembro Unknown adicional para que un valor que el servidor añada más adelante no se decodifique en silencio como el primer miembro de la lista.
Los nombres de las propiedades reciben el mismo trato desde la otra dirección. Una propiedad de esquema con el nombre de una palabra reservada de Delphi como property, class, string o function, o dos propiedades que solo se diferencian por mayúsculas y minúsculas como Name y name, producían antes una unit que no compilaba. Ahora la propiedad se renombra y el nombre original se conserva con un atributo JSONName, así que la serialización sigue coincidiendo con el documento.
Respuestas, incluidas las que declaras una sola vez
La respuesta default y las respuestas por rango 2XX, 4XX y 5XX se descartaban en silencio. Una API que declara sus errores solo mediante default, algo habitual, generaba un cliente sin ningún error tipado. Ahora se leen. Cuando se declaran varias respuestas correctas se usa la más baja, y se prefiere application/json cuando una operación ofrece varios tipos de medio.
Parámetros en la petición
El cliente generado admite ahora parámetros de cookie y todas las reglas de serialización de parámetros de OpenAPI: matrix, label, simple, form, spaceDelimited, pipeDelimited y deepObject, cada una con explode y allowReserved. Los nuevos métodos AddArray y AddObject construyen a mano los valores estructurados cuando lo necesitas.
// query parameters built with the serialization style the document declares
oRequest.AddArray('tags', ['dog', 'cat'], True); // explode
oRequest.AddObject('filter', ['color', 'red', 'size', 'M']);
Referencias externas
Una especificación repartida en varios archivos apenas funcionaba. Una referencia con un fragmento JSON Pointer como ./common.yaml#/components/schemas/Error no se podía resolver. Dos archivos que se referenciaban entre sí hacían fallar al parser. Una referencia relativa dentro de un subdocumento se resolvía contra el documento raíz en lugar de contra su propio archivo. Dos archivos externos con el mismo nombre base se sobrescribían entre sí, y podían reemplazar un esquema que pertenecía al documento principal. Y una cadena de referencias se seguía exactamente un paso.
Todo eso está corregido, y una cosa se ha restringido a propósito: antes una referencia externa podía leer cualquier archivo de la máquina, ../../../credentials.json incluido, y copiar el contenido en la unit generada. Ahora las referencias externas están confinadas al directorio del documento principal. Cuando una estructura de archivos necesita de verdad salir de ahí, el confinamiento se levanta de forma explícita.
uses
sgcOpenAPI_Bundle;
begin
// off by default: references may not leave the folder of the main document
sgcOpenAPIAllowRefsOutsideRoot := True;
end;
Archivos que no son del todo UTF-8
RFC 8259 dice que un documento JSON es UTF-8, y muchas especificaciones publicadas no lo son. Un archivo con marca de orden de bytes se rechazaba con un error de UTF-8 en cuanto contenía cualquier carácter fuera de ASCII, y el texto en chino o japonés se sustituía en silencio por signos de interrogación.
Un documento que no es UTF-8 válido se lee ahora como Windows-1252 dejando registrada una advertencia, en lugar de fallar. Un archivo UTF-16 con marca de orden de bytes se lee correctamente. El archivo generado se escribe con una codificación explícita, y un carácter que la codificación de destino no puede representar se informa en lugar de convertirse en silencio en un signo de interrogación.
Una línea de comandos que puedes poner en un script de compilación
La línea de comandos establece ahora un código de salida: 0 si todo va bien, y de 1 a 7 para los distintos fallos, de modo que un paso de compilación puede saber si la generación ha funcionado. Los mensajes de error van siempre a la salida de error estándar, y el modificador -l sirve ahora solo para registrar el progreso.
sgcOpenAPI -i petstore.json -o PetStoreClient.pas -c TPetStoreClient
if errorlevel 1 (
echo OpenAPI generation failed with exit code %errorlevel%
exit /b %errorlevel%
)
Con ella se han ido tres errores de la línea de comandos. El modificador documentado -output escribía la unit en un archivo llamado utput en el directorio actual, y como los mensajes estaban suprimidos la ejecución seguía pareciendo correcta. No ocurría absolutamente nada cuando la herramienta se ejecutaba sin consola asociada, que es justo el caso en una tarea programada o en un agente de compilación, y una redirección de salida existente se descartaba. Y -h imprimía un error de licencia en lugar del texto de uso en una máquina que no estaba activada, un valor no válido para -m o -a se aceptaba en silencio, y un modificador desconocido se ignoraba.
Nuevo en esta versión, -r (o -remote) convierte un documento YAML o Swagger 2.0 a través del conversor público de converter.swagger.io. Está desactivado por defecto, porque envía tu documento a un tercero, así que es algo que activas a sabiendas.
La propia conversión de Swagger 2.0 estaba rota de dos formas que merece la pena nombrar. Todos los números se convertían en strings, así que un valor por defecto numérico producía una unit que no compilaba y el documento convertido no era OpenAPI 3.0 válido. Y un discriminator de Swagger 2.0, que allí es un simple string, abortaba todo el análisis con un typecast no válido.
Cambios incompatibles
Cinco cambios requieren una decisión por tu parte, no solo una actualización.
Los clientes generados verifican ahora el certificado del servidor. Antes no lo hacían, lo que significa que aceptaban cualquier certificado, incluido el presentado por un atacante intermedio. Para llegar a un endpoint autofirmado o de pruebas, desactívalo de forma deliberada.
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';
El cuerpo de la petición es UTF-8. Como exige RFC 8259. Una clase serializa ahora una cadena vacía como "field": "" en lugar de omitirla. Los valores nulos se controlan por separado.
oClient.JSONIgnoreEmptyStrings := True; // previous output
oClient.JSONIgnoreNullValues := True; // default
Una respuesta ya no libera un ResponseStream que hayas proporcionado. Establece OwnsResponseStream a True para el comportamiento anterior. Liberar el cliente desde dentro de su propio manejador OnResponse, OnError o OnCancel lanza ahora un error claro en lugar de quedarse colgado.
El valor de un modificador de línea de comandos debe escribirse como -name value o -name:value. La forma pegada sin separador, como -x"GET /pets", ya no se acepta. Esa forma era también lo que hacía que -x coincidiera con otros modificadores que empiezan por x, como -xml.
Un parámetro declarado como array se genera como array. Antes se generaba como string, así que la firma del método generado cambia para esas operaciones.
Todo lo demás
Las correcciones restantes son de las que solo se notan cuando te muerden. Un miembro con un tipo JSON inesperado, por ejemplo "properties": [], abortaba el análisis con un typecast no válido en lugar de omitirse. A un esquema de tipo entero sin valor por defecto se le asignaba un valor por defecto de 0, y un enum de un solo valor se trataba como una constante, lo que eliminaba por completo el parámetro del método generado. Leer el mismo documento dos veces duplicaba cada path, tag, servidor y esquema. Una extensión de especificación como x-tagGroups colocada entre los paths se leía como si fuera un path. enum, required y tags se analizaban con un helper de texto separado por comas, así que un valor que contuviera una coma o un escape JSON se partía o se corrompía. Un requisito de seguridad que enumeraba varios esquemas conservaba solo el primero, con lo que se perdía la exigencia de que se cumplan todos. info.contact e info.license no se leían nunca, por una comprobación que no podía ser cierta jamás. Una URL de servidor con varias variables sustituía el valor equivocado y podía provocar un error de índice de lista. Empaquetar una especificación sobrescribía el archivo de entrada sin copia de seguridad y sin ningún mensaje, y eliminaba del documento todos los apóstrofos tipográficos. Y una especificación guardada en una ruta que contenía un espacio, como C:\My Specs\, no podía resolver sus referencias externas.
Cómo conseguirlo
sgcOpenAPI 2026.9.0 ya está disponible, con el código fuente completo y un año de actualizaciones. Es compatible desde Delphi 7 hasta Delphi 13 Florence y las versiones equivalentes de C++ Builder.
Página del producto · Descargar la versión de prueba · Registro de cambios
¿Preguntas o comentarios? Ponte en contacto, recibirás respuesta de las personas que escribieron el código.
