NSwag icon indicating copy to clipboard operation
NSwag copied to clipboard

Feature request: possibility to replace the JsonStringEnumConverter for System.Text.Json

Open AroglDarthu opened this issue 3 years ago • 11 comments

It would be nice to have a commandline option for nswag that enables replacement of the annotation [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))].

As it stands, the JsonStringEnumConverter has some shortcomings, which are alleviated by the open source package Macross.Json.Extensions. See: https://github.com/Macross-Software/core/blob/develop/ClassLibraries/Macross.Json.Extensions/README.md

In order to get the same effect, we now perform a replacement via powershell on the generated files: ((Get-Content -Path $filePath -Raw) -replace 'JsonStringEnumConverter', 'JsonStringEnumMemberConverter') | Set-Content -Path $filePath

AroglDarthu avatar Jan 24 '22 13:01 AroglDarthu

I would second a request to edit the templated logic for enums when using

"jsonLibrary": "SystemTextJson",

The attribute for the JsonConverter that is applied to enums takes precedence over any enum based converter that might be added via the UpdateJsonSerializerSettings extension point.

Current template formats an enum like this:

 [System.Text.Json.Serialization.JsonPropertyName("ecoaCode")]
 [System.ComponentModel.DataAnnotations.StringLength(1)]
 [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
 public ConsumerInfoVoEcoaCode? EcoaCode { get; set; } = default!;

As AroglDarthu points out, the current MS supplied JsonStringEnumConverter converter has some limitations. Most notably is the lack of support for NSwag enums generated like this. _05 is provided in the Json string, not the "05" as desired.

 public enum AccountInfoVoAccountStatusCode
    {
        [System.Runtime.Serialization.EnumMember(Value = @"05")]
        _05 = 0,

Suggested fix:

Just remove the attribute [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))] from the template.

Then allow/direct the users to the existing extension point to define an enum converter of their choice.

partial void UpdateJsonSerializerSettings(System.Text.Json.JsonSerializerOptions settings)
{
    // AroglDarthu can define his use case like this.
    settings.Converters.Add( new System.Text.Json.Serialization.JsonStringEnumMemberConverter(allowIntegerValues: false));

    // I might like this instead
    settings.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverterWithAttributeSupport());

    // MS User can do this:
    settings.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());

     // Allow any additional settings as well.
    settings.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;

}

Think it over. Ed

eobeda avatar Feb 15 '22 04:02 eobeda

@eobeda I like your proposal. It could even be implemented without a breaking change by providing a command-line option to suppress the attribute.

AroglDarthu avatar Apr 13 '22 07:04 AroglDarthu

Are there any updates on this request? It would be nice to at least have a config flag to suppress the generation of System.Text.Json.Serialization.JsonConverterAttribute on enum properties so that a custom converter defined w/ the config flag JsonConverters can be utilized for enum properties. Or alternatively have a config value to specify the converter type to be used in the generation of that attribute.

AndrewDRX avatar Oct 15 '22 15:10 AndrewDRX

I was able to change the attribute using template overrides (https://github.com/RicoSuter/NSwag/wiki/Templates). It would still be nice to have it as a configurable option, as to remove the need for a template override; however, this is now a non-issue to all my intents, constructions and purposes.

AndrewDRX avatar Oct 15 '22 16:10 AndrewDRX

We use powershell to kick off the client generation. As a workaround we just replaced the attribute with what we needed from the same powershell script. Ugly hack, but it works. A built-in option, like @eobeda 's proposal, would be extremely useful.

AroglDarthu avatar Oct 16 '22 10:10 AroglDarthu

Thanks for the suggestion!

I was replacing the line post generation via the csproj, but that caused the file to always be triggered by the build's up-to-date check (so building the containing multi-project solution would always build that project even when it contained no actual changes).

I could have just removed the generated file from the up-to-date check by using a csproj UpToDateCheckInput item, but that was not feasible since I still want the build to be noticed as out-of-date when the file is regenerated.

AndrewDRX avatar Oct 16 '22 14:10 AndrewDRX

I was able to change the attribute using template overrides (https://github.com/RicoSuter/NSwag/wiki/Templates). It would still be nice to have it as a configurable option, as to remove the need for a template override; however, this is now a non-issue to all my intents, constructions and purposes.

For those interested, here is the relevant lines to remove for what he's suggesting. You will need to sub in your own enum converter to the JsonSerializationSettings if you dont just want the enum numbers. https://github.com/RicoSuter/NJsonSchema/blob/94647c590b3c6cbc3d09569b027ee7cd77c20463/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid#LL83-L89C15

JustinGrote avatar Mar 09 '23 19:03 JustinGrote

Any update on this, we need the same feature

cli00004 avatar Mar 13 '23 21:03 cli00004

Has this been fixed yet?

BoasHoeven avatar Feb 08 '24 15:02 BoasHoeven

Also stuck here. I have no extension point to make it work.

Openapi spec:

image

Generated enum:

image

Generated class that uses the enum:

image

The serialized enum value is pascal-cased, e.g. AccountHolderKey instead of accountHolderKey, and the API call fails.

rafaelkallis avatar Jun 21 '24 12:06 rafaelkallis

@rafaelkallis

If you are using an NSwag configuration file (e.g., nswag.json) for your build then you can add a template overrides directory from there.

{
  ...
  "codeGenerators": {
    ...
    "templateDirectory": "./TemplateOverrides"
  }
}

And then just put the raw Class.liquid into that directory w/ any updates relevant per my post (https://github.com/RicoSuter/NSwag/issues/3846#issuecomment-1279779176) and @JustinGrote's post (https://github.com/RicoSuter/NSwag/issues/3846#issuecomment-1462659271).

Raw: https://raw.githubusercontent.com/RicoSuter/NJsonSchema/master/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid

If you are not using an NSwag configuration file, then it might be preferable for you to investigate how to incorporate that into your project. Or if that not feasible then there might be some other method for passing a configuration for the template overrides directory into whatever entry point you have for the NSwag library.

AndrewDRX avatar Jul 06 '24 19:07 AndrewDRX