RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.74k stars 1.29k forks source link

Error: Unexpected character '«' when generate client API using CSharpClientGenerator #2878

Open wk-j opened 4 years ago

wk-j commented 4 years ago
Screen Shot 2563-06-08 at 21 14 45

Project

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
    <PackageReference Include="NSwag.CodeGeneration.CSharp" Version="13.6.0" />
  </ItemGroup>

</Project>

Source

  public static class Program {
        private static async Task<OpenApiDocument> FromUrl() {
            const string url = "https://activiti.alfresco.com/activiti-app/api/v2/api-docs?group=enterprise";
            return await OpenApiDocument.FromUrlAsync(url).ConfigureAwait(false);
        }

        private static async Task<OpenApiDocument> FromFile() {
            const string input = "resource/api.json";
            var json = File.ReadAllText(input);
            return await OpenApiDocument.FromJsonAsync(json).ConfigureAwait(false);
        }

        public static async Task Main(string[] _) {
            const string output = "src/ProcessApi/ProcessClient.cs";

            // var single = new SingleClientFromPathSegmentsOperationNameGenerator();

            var settings = new CSharpClientGeneratorSettings {
                ClassName = "ProcessClient",
                ExposeJsonSerializerSettings = true,
                InjectHttpClient = true,
                // OperationNameGenerator = single,
                CSharpGeneratorSettings = {
                    Namespace = "ProcessApi",
                },
            };

            var document = await FromUrl();
            var generator = new CSharpClientGenerator(document, settings);
            var code = generator.GenerateFile();

            File.WriteAllText(output, code);
        }
    }
RicoSuter commented 4 years ago

Does the type/schema key name contain «? That's funny...

RicoSuter commented 4 years ago

Best option is to preprocess the spec and remove these...

wk-j commented 4 years ago

Try to remove unexpected characters I got another error

Source

namespace ProcessClient.Generator {
    public static class Program {
        private static async Task<OpenApiDocument> FromUrl() {
            const string url = "https://activiti.alfresco.com/activiti-app/api/v2/api-docs?group=enterprise";
            return await OpenApiDocument.FromUrlAsync(url).ConfigureAwait(false);
        }

        private static async Task<OpenApiDocument> FromFile() {
            const string input = "resource/api.json";
            var json = File.ReadAllText(input);
            return await OpenApiDocument.FromJsonAsync(json).ConfigureAwait(false);
        }

        public static async Task Main(string[] _) {
            const string output = "src/ProcessApi/ProcessClient.cs";

            // var single = new SingleClientFromPathSegmentsOperationNameGenerator();

            var settings = new CSharpClientGeneratorSettings {
                ClassName = "ProcessClient",
                ExposeJsonSerializerSettings = true,
                InjectHttpClient = true,
                // OperationNameGenerator = single,
                CSharpGeneratorSettings = {
                    Namespace = "ProcessApi",
                },
            };

            var document = await FromUrl();
            var generator = new CSharpClientGenerator(document, settings);
            var code = generator.GenerateFile();

            code = code.Replace("«", "<").Replace("»", ">");

            File.WriteAllText(output, code);
        }
    }
}

Error

ProcessClient.cs(17750,21): error CS1002: ; expected [/Users/wk/Source/process-service-client/src/ProcessApi/ProcessApi.csproj]
ProcessClient.cs(17758,50): error CS1519: Invalid token '1' in class, struct, or interface member declaration [/Users/wk/Source/process-service-client/src/ProcessApi/ProcessApi.csproj]
ProcessClient.cs(17950,103): error CS1519: Invalid token '2' in class, struct, or interface member declaration [/Users/wk/Source/process-service-client/src/ProcessApi/ProcessApi.csproj]
ProcessClient.cs(17952,21): error CS1002: ; expected [/Users/wk/Source/process-service-client/src/ProcessApi/ProcessApi.csproj]
ProcessClient.cs(17961,109): error CS1519: Invalid token '2' in class, struct, or interface member declaration [/Users/wk/Source/process-service-client/src/ProcessApi/ProcessApi.csproj]
ProcessClient.cs(18031,113): error CS1519: Invalid token '1' in class, struct, or interface member declaration [/Users/wk/Source/process-service-client/src/ProcessApi/ProcessApi.csproj]
ProcessClient.cs(18033,21): error CS1002: ; expected [/Users/wk/Source/process-service-client/src/ProcessApi/ProcessApi.csproj]
ProcessClient.cs(18041,119): error CS1519: Invalid token '1' in class, struct, or interface member declaration [/Users/wk/Source/process-service-client/src/ProcessApi/ProcessApi.csproj]
ProcessClient.cs(18237,73): error CS1519: Invalid token '1' in class, struct, or interface member declaration [/Users/wk/Source/process-service-client/src/ProcessApi/ProcessApi.csproj]
Screen Shot 2563-06-10 at 19 25 57 Screen Shot 2563-06-10 at 19 25 02
MissGze commented 4 years ago

In my case, the characters «» came directly from the swagger file.

    "Deque«WsWorkflowExecutionStackItem»": { "type": "object" },
    "ErrorHandler": {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "position": { "$ref": "#/definitions/Position" },
        "throwBindName": { "type": "string" }
      }
    },

I got the same second error CS1519: Invalid token '1' in class, struct, or interface member declaration but it seems it is the same issue as https://github.com/RicoSuter/NSwag/issues/2192 in my case. Operation identifier like "myOperation_1" ends up in a generated method named 1Async instead of myOperationAsync or something like that.

I ended up fixing the Swagger file before generating the client in my build with a MsBuild task:

  <UsingTask TaskName="FixSwaggerFileForNSwag" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
      <Filename ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Reference Include="System.Core" />
      <Using Namespace="System" />
      <Using Namespace="System.IO" />
      <Using Namespace="System.Text.RegularExpressions" />
      <Code Type="Fragment" Language="cs">
        <![CDATA[
        var fileContent = File.ReadAllText(Filename);
        fileContent = Regex.Replace(fileContent, "\"operationId\": \"(\\w+)_(\\d+)\"", "\"operationId\": \"$1n$2\"");
        fileContent = fileContent.Replace("«","_");
        fileContent = fileContent.Replace("»","");
        File.WriteAllText(Filename, fileContent);
      ]]>
      </Code>
    </Task>
  </UsingTask>

It led to generated code which successfully compiles, with method names making a few sense.

RicoSuter commented 4 years ago

Will be fixed by: https://github.com/RicoSuter/NSwag/pull/2989