Search Results for

    Show / Hide Table of Contents

    OpenAPI (Swagger) importer

    Note

    OpenAPI importer became a separated, stand alone tool and it's available as open-source project at https://github.com/landgraf-dev/openapi-delphi-generator

    Warning

    All the documentation below is deprecated, classes are not available anymore. We will keep it here as a reference for the open source project mentioned in the note above.

    XData allows you to import an existing server Swagger specification and generate service contract interfaces that you can use together with TXDataClient to perform requests to such server. The importer will also create DTO classes representing all the JSON used for requests and responses in the server.

    For now the importer can import Swagger 2.0 specification in JSON format.

    Generating the imported unit

    Use the TOpenApiImporter class declared in unit XData.OpenApi.Importer to read a Swagger JSON and generate meta information for the unit to be generated. Then you can use TDelphiCodeGenerator class (unit Bcl.Code.DelphiGenerator) to effectively generate the code. Here is an example:

    uses {...}, Bcl.Code.DelphiGenerator, Bcl.Code.MetaClasses,
      OpenApi.Document, OpenAPI.Json.Serializer, XData.OpenApi.Importer;
    
    function GenerateSource(CodeUnit: TCodeUnit): string;
    var
      Generator: TDelphiCodeGenerator;
    begin
      Generator := TDelphiCodeGenerator.Create;
      try
        Generator.StructureStatements := True;
        Result := Generator.GenerateCodeFromUnit(CodeUnit);
      finally
        Generator.Free;
      end;
    end;
    
    procedure ImportApi(const SwaggerJson, OutputFile: string);
    var
      Document: TOpenApiDocument;
      Importer: TOpenApiImporter;
    begin
      Importer := TOpenApiImporter.Create;
      try
        Document := TOpenApiDeserializer.JsonToDocument(SwaggerJson);
        try
          // SetImporterEvents(Importer); // Uncomment to set custom events
          Importer.Build(Document);
          Importer.CodeUnit.Name := TPath.GetFileNameWithoutExtension(OutputFile);
          TFile.WriteAllText(OutputFile, GenerateSource(Importer.CodeUnit), TEncoding.UTF8);
        finally
          Document.Free;
        end;
      finally
        Importer.Free;
      end;
    end;
    

    The above code will parse the Swagger JSON, build a TCodeUnit object with the unit information, then generate the file .pas file with the file name indicated by OutputFile.

    Customizing the imported API

    You can use events to customize the generated unit. Sometimes the importer doesn't generate 100% accurate source code, or sometimes you simply want to change the generated API. The following code is an example extracted from the KeapApiImporterV2 demo, available in the demos folder. The demo shows how to import and generate a client for the Keap API.

    function ToPascalCase(const S: string): string;
    var
      I: Integer;
      Convert: Boolean;
     begin
      I := 1;
      Result := '';
      Convert := True;
      while I <= Length(S) do
      begin
        if TBclUtils.IsLetter(S[I]) then
        begin
          if Convert then
          begin
            Result := Result + UpCase(S[I]);
            Convert := False;
          end
          else
            Result := Result + S[I];
        end
        else
        if S[I] = '_' then
          Convert := True
        else
          Result := Result + S[I];
        Inc(I);
      end;
    end;
    
    procedure SetImporterEvents(Importer: TOpenApiImporter);
    begin
      // OnGetMethodName allows you to change name of the generated service contract method 
      Importer.OnGetMethodName :=
        procedure(var MethodName: string; const Original: string)
        var
          P: Integer;
        begin
          // Convert specific names
          if Original = 'listCountriesUsingGET_3' then
            MethodName := 'ListCountryProvinces'
          else
          if Original = 'updateCompanyUsingPATCH_3' then
            MethodName := 'UpdateCompany2'
          else
          if Original = 'removeTagsFromContactUsingDELETE_3' then
            MethodName := 'RemoveTagFromContact';
        end;
    
      // OnGetTypeName also allows you to provide a custom name for the DTO classes
      Importer.OnGetTypeName :=
        procedure(var TypeName: string; const Original: string)
        begin
          TypeName := 'T' + TypeName;
        end;
    
      // OnGetPropName allows you to modify the name of a property. In the following example,
      // some generated property names don't compile because they are invalid identifiers.
      Importer.OnGetPropName :=
        procedure(var PropName: string; const Original: string)
        begin
          if Original = '24_hours' then
            PropName := '_24Hours'
          else
          if Original = '30_days' then
            PropName := '_30Days'
          else
          if Original = 'className' then
            PropName := 'ClassName_'
          else
          if Original = 'methodName' then
            PropName := 'MethodName_';
          else
            PropName := ToPascalCase(Original);
        end;
    
      // OnMethodCreated is called after the full method meta information is generated. You can then change everything
      // you need from it. In this case, optional_properties parameter is removed from the final method
      Importer.OnMethodCreated :=
        procedure(Method: TCodeMemberMethod; Parent: TCodeTypeDeclaration)
        begin
          Method.RemoveParameter('optional_properties');
        end;
    
      // OnGetServiceName event allows settings the name of the interface type
      Importer.OnGetServiceName :=
        procedure(var ServiceName: string; var Guid: TGUID; PathItem: TPathItem; Operation: TOperation)
        var
          Name: string;
        begin
          if Operation.Tags.Count = 1 then
          begin
            Name := Operation.Tags[0];
            Name := StringReplace(Name, ' ', '', [rfReplaceAll]);
            Name := StringReplace(Name, '-', '', [rfReplaceAll]);
            ServiceName := Format('IKeap%s', [ToPascalCase(Name)]);
          end;
        end;
    end;
    

    Using the API

    The importer generates a unit with service contract interfaces and JSON DTOs. Use it with a TXDataClient the same way you would use a service contract to invoke operations in a XData server. The difference, of course, is that the server is not XData but a 3rd party API.

    var
      Client: TXDataClient;
      Request: TCreatePatchContactRequest;
    begin
      Client := TXDataClient.Create;
      Client.Uri := 'https://api.infusionsoft.com/crm/rest/v2';
    
      Request := TCreatePatchContactRequest.Create;
      Request.FamilyName := 'Foo';
      Client.Service<IKeapContact>.CreateContact(Request);
    
      Request.Free;
      Client.Free;
    end;
    
    In This Article
    Back to top TMS XData v5.21
    © 2002 - 2025 tmssoftware.com