Skip to content
Merged
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ EXAMPLES:
refitter ./openapi.json --use-polymorphic-serialization
refitter ./openapi.json --collection-format Csv
refitter ./openapi.json --simple-output
refitter ./openapi.json --no-inline-json-converters

ARGUMENTS:
[URL or input file] URL or file path to OpenAPI Specification file
Expand Down Expand Up @@ -132,6 +133,7 @@ OPTIONS:
payloads with (yet) unknown types are offered by newer versions of an API
See https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism for more information
--disposable Generate refit clients that implement IDisposable
--no-inline-json-converters Don't inline JsonConverter attributes for enum properties. When disabled, enum properties will not have [JsonConverter(typeof(JsonStringEnumConverter))] attributes
```

To generate code from an OpenAPI specifications file, run the following:
Expand Down Expand Up @@ -304,6 +306,7 @@ The following is an example `.refitter` file
"generateNativeRecords": false,
"generateDefaultValues": true,
"inlineNamedAny": false,
"inlineJsonConverters": true, // Optional. Default=true. Set to false to not generate JsonConverter attributes for enum properties
"dateFormat": "yyyy-MM-dd",
"dateTimeFormat": "yyyy-MM-dd",
"excludedTypeNames": [
Expand Down Expand Up @@ -390,6 +393,7 @@ The following is an example `.refitter` file
- `generateNativeRecords` - Default is false
- `generateDefaultValues` - Default is true
- `inlineNamedAny` - Default is false
- `inlineJsonConverters` - Default is true. When set to false, enum properties will not have `[JsonConverter(typeof(JsonStringEnumConverter))]` attributes
- `dateFormat` - Default is null
- `dateTimeFormat` - Default is null
- `excludedTypeNames` - Default is empty
Expand Down
5 changes: 4 additions & 1 deletion docs/docfx_project/articles/cli-tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ EXAMPLES:
refitter ./openapi.json --use-polymorphic-serialization
refitter ./openapi.json --collection-format Csv
refitter ./openapi.json --simple-output
refitter ./openapi.json --no-inline-json-converters

ARGUMENTS:
[URL or input file] URL or file path to OpenAPI Specification file
Expand Down Expand Up @@ -114,7 +115,9 @@ OPTIONS:
Replaces NSwag JsonInheritanceConverter attributes with System.Text.Json JsonPolymorphicAttributes.
To have the native support of inheritance (de)serialization and fallback to base types when
payloads with (yet) unknown types are offered by newer versions of an API
See https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism for more information --disposable Generate refit clients that implement IDisposable
See https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism for more information
--disposable Generate refit clients that implement IDisposable
--no-inline-json-converters Don't inline JsonConverter attributes for enum properties. When disabled, enum properties will not have [JsonConverter(typeof(JsonStringEnumConverter))] attributes
```

## CLI Tool Output Example
Expand Down
2 changes: 2 additions & 0 deletions docs/docfx_project/articles/refitter-file-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ The following is an example `.refitter` file
"generateNativeRecords": false,
"generateDefaultValues": true,
"inlineNamedAny": false,
"inlineJsonConverters": true, // Optional. Default=true. Set to false to not generate JsonConverter attributes for enum properties
"dateFormat": "string",
"dateTimeFormat": "string",
"excludedTypeNames": [
Expand Down Expand Up @@ -206,6 +207,7 @@ The following is an example `.refitter` file
- `generateNativeRecords` - Default is false
- `generateDefaultValues` - Default is true
- `inlineNamedAny` - Default is false
- `inlineJsonConverters` - Default is true. When set to false, enum properties will not have `[JsonConverter(typeof(JsonStringEnumConverter))]` attributes
- `excludedTypeNames` - Default is empty
- `dateFormat` - Default is null
- `dateTimeFormat` - Default is null
Expand Down
16 changes: 16 additions & 0 deletions src/Refitter.Core/RefitGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public string Generate()
var generator = factory.Create();
var docGenerator = new XmlDocumentationGenerator(settings);
var contracts = generator.GenerateFile();
contracts = SanitizeGeneratedContracts(contracts);

if (settings.GenerateClients)
{
contracts = RefitInterfaceImports
Expand Down Expand Up @@ -153,6 +155,7 @@ public GeneratorOutput GenerateMultipleFiles()
var generator = factory.Create();
var docGenerator = new XmlDocumentationGenerator(settings);
var contracts = generator.GenerateFile();
contracts = SanitizeGeneratedContracts(contracts);

IRefitInterfaceGenerator interfaceGenerator = settings.MultipleInterfaces switch
{
Expand Down Expand Up @@ -199,6 +202,19 @@ public GeneratorOutput GenerateMultipleFiles()
return new GeneratorOutput(generatedFiles);
}

private string SanitizeGeneratedContracts(string contracts)
{
if (settings.CodeGeneratorSettings is not { InlineJsonConverters: false })
{
return contracts;
}

const string pattern = @"^\s*\[(System\.Text\.Json\.Serialization\.)?JsonConverter\(typeof\((System\.Text\.Json\.Serialization\.)?JsonStringEnumConverter\)\)\]\s*$";
var lines = contracts.Split(["\r\n", "\r", "\n"], StringSplitOptions.None);
var filteredLines = lines.Where(line => !Regex.IsMatch(line, pattern)).ToArray();
return string.Join(Environment.NewLine, filteredLines);
}

/// <summary>
/// Generates the client code based on the specified interface generator.
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions src/Refitter.Core/Settings/CodeGeneratorSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,4 +257,13 @@ Gets or sets a value indicating whether named/referenced any schemas should be i
[Description("Gets or sets a custom IPropertyNameGenerator.")]
[JsonIgnore]
public IPropertyNameGenerator? PropertyNameGenerator { get; set; }

/// <summary>
/// Gets or sets a value indicating whether to inline JsonConverter attributes for enum properties (default: true).
/// When set to false, enum properties will not have [JsonConverter(typeof(JsonStringEnumConverter))] attributes.
/// </summary>
[Description(
"Gets or sets a value indicating whether to inline JsonConverter attributes for enum properties (default: true). When set to false, enum properties will not have [JsonConverter(typeof(JsonStringEnumConverter))] attributes."
)]
public bool InlineJsonConverters { get; set; } = true;
}
2 changes: 2 additions & 0 deletions src/Refitter.SourceGenerator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ The following is an example `.refitter` file
"generateNativeRecords": false,
"generateDefaultValues": true,
"inlineNamedAny": false,
"inlineJsonConverters": true, // Optional. Default=true. Set to false to not generate JsonConverter attributes for enum properties
"dateFormat": "yyyy-MM-dd",
"dateTimeFormat": "yyyy-MM-dd",
"excludedTypeNames": [
Expand Down Expand Up @@ -193,6 +194,7 @@ The following is an example `.refitter` file
- `generateNativeRecords` - Default is false
- `generateDefaultValues` - Default is true
- `inlineNamedAny` - Default is false
- `inlineJsonConverters` - Default is true. When set to false, enum properties will not have `[JsonConverter(typeof(JsonStringEnumConverter))]` attributes
- `dateFormat` - Default is null
- `dateTimeFormat` - Default is null
- `excludedTypeNames` - Default is empty
155 changes: 155 additions & 0 deletions src/Refitter.Tests/Examples/InlineJsonConvertersTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using FluentAssertions;
using FluentAssertions.Execution;
using Refitter.Core;
using Refitter.Tests.Build;
using Refitter.Tests.TestUtilities;
using Xunit;

namespace Refitter.Tests.Examples;

public class InlineJsonConvertersTests
{
private const string OpenApiSpec = @"
{
""swagger"": ""2.0"",
""info"": {
""title"": ""Enum Test API"",
""version"": ""1.0.0""
},
""host"": ""example.com"",
""basePath"": ""/"",
""schemes"": [
""https""
],
""paths"": {
""/api/pets/{petId}"": {
""get"": {
""summary"": ""Get pet by ID"",
""operationId"": ""getPetById"",
""parameters"": [
{
""name"": ""petId"",
""in"": ""path"",
""required"": true,
""type"": ""integer""
}
],
""responses"": {
""200"": {
""description"": ""Success"",
""schema"": {
""$ref"": ""#/definitions/Pet""
}
}
}
}
}
},
""definitions"": {
""Pet"": {
""type"": ""object"",
""properties"": {
""id"": {
""type"": ""integer"",
""format"": ""int64""
},
""name"": {
""type"": ""string""
},
""status"": {
""type"": ""string"",
""enum"": [
""available"",
""pending"",
""sold""
]
}
}
}
}
}
";

[Fact]
public async Task Can_Generate_Code()
{
string generatedCode = await GenerateCode();
generatedCode.Should().NotBeNullOrWhiteSpace();
}

[Fact]
public async Task Can_Build_Generated_Code()
{
string generatedCode = await GenerateCode();
BuildHelper
.BuildCSharp(generatedCode)
.Should()
.BeTrue();
}

[Fact]
public async Task Generated_Code_Contains_JsonConverter_By_Default()
{
string generatedCode = await GenerateCode(inlineJsonConverters: true);

using (new AssertionScope())
{
generatedCode.Should().Contain("[JsonConverter(typeof(JsonStringEnumConverter))]");
generatedCode.Should().Contain("Status { get; set; }");
}
}

[Fact]
public async Task Generated_Code_Does_Not_Contain_JsonConverter_When_Disabled()
{
string generatedCode = await GenerateCode(inlineJsonConverters: false);

using (new AssertionScope())
{
generatedCode.Should().NotContain("[JsonConverter(typeof(JsonStringEnumConverter))]");
generatedCode.Should().Contain("Status { get; set; }");
generatedCode.Should().Contain("public enum PetStatus");
}
}

[Fact]
public async Task Generated_Code_Without_JsonConverter_Can_Build()
{
string generatedCode = await GenerateCode(inlineJsonConverters: false);
BuildHelper
.BuildCSharp(generatedCode)
.Should()
.BeTrue();
}

private static async Task<string> GenerateCode(bool inlineJsonConverters = true)
{
string swaggerFile = await SwaggerFileHelper.CreateSwaggerFile(OpenApiSpec);
try
{
var settings = new RefitGeneratorSettings
{
OpenApiPath = swaggerFile,
CodeGeneratorSettings = new CodeGeneratorSettings
{
InlineJsonConverters = inlineJsonConverters
}
};
var generator = await RefitGenerator.CreateAsync(settings);
return generator.Generate();
}
finally
{
if (File.Exists(swaggerFile))
{
File.Delete(swaggerFile);
}

var directory = Path.GetDirectoryName(swaggerFile);
if (directory != null && Directory.Exists(directory))
{
Directory.Delete(directory, true);
}
}
}
}
6 changes: 5 additions & 1 deletion src/Refitter/GenerateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,11 @@ private static RefitGeneratorSettings CreateRefitGeneratorSettings(Settings sett
UsePolymorphicSerialization = settings.UsePolymorphicSerialization,
GenerateDisposableClients = settings.GenerateDisposableClients,
CollectionFormat = settings.CollectionFormat,
GenerateXmlDocCodeComments = !settings.NoXmlDocCodeComments
GenerateXmlDocCodeComments = !settings.NoXmlDocCodeComments,
CodeGeneratorSettings = new CodeGeneratorSettings
{
InlineJsonConverters = !settings.NoInlineJsonConverters
}
};
}
private static async Task WriteSingleFile(
Expand Down
5 changes: 4 additions & 1 deletion src/Refitter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ EXAMPLES:
refitter ./openapi.json --operation-name-template '{operationName}Async'
refitter ./openapi.json --optional-nullable-parameters
refitter ./openapi.json --use-polymorphic-serialization
refitter ./openapi.json --no-inline-json-converters

ARGUMENTS:
[URL or input file] URL or file path to OpenAPI Specification file
Expand Down Expand Up @@ -109,7 +110,9 @@ OPTIONS:
Replaces NSwag JsonInheritanceConverter attributes with System.Text.Json JsonPolymorphicAttributes.
To have the native support of inheritance (de)serialization and fallback to base types when
payloads with (yet) unknown types are offered by newer versions of an API
See https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism for more information --disposable Generate refit clients that implement IDisposable
See https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism for more information
--disposable Generate refit clients that implement IDisposable
--no-inline-json-converters Don't inline JsonConverter attributes for enum properties. When disabled, enum properties will not have [JsonConverter(typeof(JsonStringEnumConverter))] attributes
```

## CLI Tool Output Example
Expand Down
5 changes: 5 additions & 0 deletions src/Refitter/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,9 @@ payloads with (yet) unknown types are offered by newer versions of an API
[CommandOption("--simple-output")]
[DefaultValue(false)]
public bool SimpleOutput { get; set; }

[Description("Don't inline JsonConverter attributes for enum properties. When disabled, enum properties will not have [JsonConverter(typeof(JsonStringEnumConverter))] attributes")]
[CommandOption("--no-inline-json-converters")]
[DefaultValue(false)]
public bool NoInlineJsonConverters { get; set; }
}
Loading