RicoSuter / NSwag

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

System.Runtime.Serialization.EnumMemberAttribute is not supported by System.Text.Json when spaces are present #4696

Open johannesmols opened 8 months ago

johannesmols commented 8 months ago

As stated in the System.Text.Json migration guide, the System.Runtime.Serialization attributes are not supported (see also https://github.com/dotnet/runtime/issues/31081).

I have noticed that it does work when my enum names do not contain spaces. Once they do, I get an exception:

System.Text.Json.JsonException
  HResult=0x80131500
  Message=The JSON value could not be converted to EmailTemplateEmail_type. Path: $.data[0].attributes.email_type | LineNumber: 0 | BytePositionInLine: 564.
  Source=System.Text.Json
  StackTrace:
   at System.Text.Json.ThrowHelper.ThrowJsonException(String message)
   at System.Text.Json.Serialization.Converters.EnumConverter`1.ReadEnumUsingNamingPolicy(String enumString)
   at System.Text.Json.Serialization.Converters.EnumConverter`1.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.<DeserializeAsync>d__1.MoveNext()

This is the code generated:

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.0.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")]
public enum EmailTemplateEmail_type
{
    [System.Runtime.Serialization.EnumMember(Value = @"Order  receipt")]
    Order_receipt = 0,

    [System.Runtime.Serialization.EnumMember(Value = @"Order completed")]
    Order_completed = 1,

    [System.Runtime.Serialization.EnumMember(Value = @"Order completed with refund")]
    Order_completed_with_refund = 2,
}

When the names in the definition do not contain spaces, this works fine:

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.0.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")]
public enum EmailTemplateEmail_type
{
    [System.Runtime.Serialization.EnumMember(Value = @"Order_receipt")]
    Order_receipt = 0,

    [System.Runtime.Serialization.EnumMember(Value = @"Order_completed")]
    Order_completed = 1,

    [System.Runtime.Serialization.EnumMember(Value = @"Order_completed_with_refund")]
    Order_completed_with_refund = 2,
}

For completeness, this is how the enum is used in a class of the generated code:

[System.Text.Json.Serialization.JsonPropertyName("email_type")]
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
public EmailTemplateEmail_type Email_type { get; set; }

And this is the specification defining the enum:

"email_type": {
  "type": "string",
  "enum": [
    "Order_receipt",
    "Order_completed",
    "Order_completed_with_refund"
  ]
}
Xeevis commented 7 months ago

Yup, JsonStringEnumConverter leaves much to be desired, you need 3rd party converter to support such scenarios. Most popular being JsonStringEnumMemberConverter. I'm just trying to figure out how to tell NSwag to not hardcode JsonStringEnumConverter.

Eruzo commented 2 weeks ago

Was looking for that as well. If I'm right the code is generated here: https://github.com/RicoSuter/NJsonSchema/blob/9bf8f695b373410e8b51e1363270c08dda7b8127/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid#L82. Looks like there is no way for now to change it.